Curso / Aplicado a tus proyectos / Interpretante Lacaniano
🔧 basado en tu código real · 2 de 5

Aplicado a tus proyectos · interpretante-lacaniano

Un LLM que no consulta a Lacan — lo habita

Acá no hay un solo concepto del Capítulo 1 que no puedas señalar corriendo en producción, en tu propia laptop. Y hay un giro extra: la arquitectura del sistema termina siendo, literalmente, una implementación computacional de un concepto lacaniano.

Antes de leerHF cap. 3 §5, LoRA · HF cap. 5 §5, embeddings e índices · LLM 2026 · 4, RAG y agentes

Cuerpo: Gemma-4-31B-lacan (NVFP4-mse + draft MTP 0.5B) Memoria: ChromaDB · 45.904 pasajes Embeddings: intfloat/multilingual-e5-large Orquestación: LlamaIndex ReActAgent Fine-tuning: Unsloth (CPT + QLoRA)

🎯 Al terminar vas a poder

  • Señalar en un sistema real dónde trabaja un decoder-only, dónde un encoder-only, y por qué hacen falta los dos.
  • Explicar RAG en tres pasos (chunking → embedding → retrieval + generación) y decir qué cambia cuando cambiás el tamaño de chunk o el solapamiento.
  • Distinguir conocimiento paramétrico de conocimiento simbólico explícito, y qué aporta cada uno.
  • Explicar qué es LoRA/QLoRA, qué es el olvido catastrófico y por qué el "replay" lo mitiga.
  • Reconocer en un ciclo de reentrenamiento el concepto de après-coup, y decir por qué no es solo una metáfora.

0. El metabolismo

Tu propio README lo resume mejor que cualquier paráfrasis mía: el sistema tiene una memoria que aprende en tiempo real (cada insight y cada libro nuevo entra al RAG al instante) y unos pesos que aprenden por generaciones (la producción curada vuelve al modelo vía LoRA, con gate de aceptación y rollback). Son dos velocidades de aprendizaje completamente distintas conviviendo en el mismo sistema — y cada una corresponde a un concepto separado del Capítulo 1.

El metabolismo del sistema: memoria que aprende al instante, pesos que aprenden por generaciones El corpus de 81 obras produce la generación 0 por continued pretraining. El cuerpo es un decoder-only Gemma-4-31B-lacan en NVFP4 con su draft MTP; la memoria es un encoder e5-large que embebe 45.904 pasajes en ChromaDB y recibe cada libro nuevo al instante; el daemon genera insights y diálogos 24/7, orientado por un grafo simbólico de 16 nodos. La producción pasa por curaduría con rating y novedad, se mezcla con un 20 por ciento de replay, se entrena con SFT y LoRA, pasa un gate con rollback y da la generación siguiente, que vuelve a ser el cuerpo. corpus · 81 obras Lacan y Freud CPT · Generación 0 13,9M tokens · loss 1,93 → 1,35 un libro nuevo entra a la memoria al instante, sin entrenar CUERPO Gemma-4-31B-lacan decoder-only · NVFP4-mse + draft MTP 0,5B genera y sostiene la voz MEMORIA RAG · ChromaDB encoder e5-large 45.904 pasajes aprende al instante chunks al prompt GRAFO SIMBÓLICO 16 nodos Goce, objeto a, Lenguaje… explícito y editable orienta los prompts VIDA · daemon 24/7 insights + diálogos autónomos curaduría 👍/👎 humano + novedad SFT · LoRA + 20 % replay · Unsloth gate + rollback → Generación N+1 se compara contra N antes de promover los pesos aprenden por generaciones (après-coup: la curaduría resignifica al modelo que produjo)
Las dos velocidades del sistema en un solo mapa. Lo que cambia todos los días va a la memoria, sin entrenar; lo que tiene que volverse parte de la voz vuelve a los pesos por generaciones, con gate y rollback. El grafo simbólico no compite con los 31B de parámetros: orienta los prompts.

1. El cuerpo: un decoder-only que habita a Lacan

gemma-4-31B-lacan-NVFP4-Blackwell es exactamente el modelo que diseccionamos en la página anterior: decoder-only, cuantizado NVFP4-mse, con su draft MTP de 0.5B para decoding especulativo — su único trabajo es predecir tokens por adelantado, nada de entender ni razonar sobre Lacan. La única diferencia con el modelo base de Google es que este pasó por Continued Pretraining (CPT) sobre el corpus de Lacan y Freud antes de cuantizarse.

🔧 En tu proyecto

Generación 0 (2026-08-01): CPT de 1 epoch sobre 13,9M tokens del corpus, loss 1.93 → 1.35, gate de aceptación 6/6. Servido en la laptop con --max-model-len 26000 (26k, justo lo que entra de KV cache junto con el draft en 24 GB) y --gpu-memory-utilization 0.93 — con 0.90 el cache no entraba, faltaban ~0.9 GiB. Ese número no es arbitrario: es el resultado de medir, no de una regla general.

Este es el punto exacto donde el Capítulo 1 y tu proyecto se tocan más de cerca: el curso dice "decoder-only es para generación de texto, punto". Tu sistema es la prueba de que "generación de texto" puede significar "un modelo que sostiene una voz teórica coherente a través de miles de generaciones autónomas" — no solo autocompletar un prompt.

2. La memoria: dónde vuelve el encoder-only

El Capítulo 1 (actualizado) dice algo puntual: los encoder-only no desaparecieron, sobreviven fuerte en embeddings para RAG. Tu proyecto es ese caso exacto, en producción: cada uno de los 45.904 pasajes indexados en ChromaDB pasó por intfloat/multilingual-e5-large — un modelo encoder-only — para convertirse en un vector antes de guardarse.

La razón de fondo por la que necesitás dos modelos distintos (uno decoder para generar, uno encoder para embeber) y no uno solo: son tareas con formas opuestas. Generar necesita producir una secuencia nueva token a token; embeber necesita comprimir un texto ya completo en un solo vector de significado, comparándolo contra todo lo demás en el índice a la vez — el trabajo bidireccional para el que el encoder-only fue diseñado desde el origen.

RAG en tres pasos

RAG (Retrieval-Augmented Generation, Lewis et al., 2020) es el nombre general de lo que esa memoria hace. Vale la pena tenerlo como esquema, porque cada paso es una decisión de diseño separada:

  1. Chunking: cada documento se corta en pasajes (chunks) de un tamaño manejable. Un libro entero no cabe en un vector; un párrafo sí.
  2. Embedding: cada chunk pasa por el encoder y se convierte en un vector; ese vector se guarda en una base vectorial (acá ChromaDB) junto con el texto original y su metadata.
  3. Retrieval + generación: la pregunta del usuario se embebe con el mismo encoder, se buscan los chunks cuyos vectores están más cerca (similitud coseno, típicamente), y esos chunks se pegan al prompt del decoder para que responda leyéndolos, no recordándolos.

El detalle que más determina la calidad y que menos se ve es el paso 1. Un chunk demasiado grande mezcla varios temas en un solo vector — el embedding queda "promediado" y ya no se parece a ninguna pregunta concreta; además ocupa más contexto del decoder por cada resultado. Un chunk demasiado chico pierde el contexto que le daba sentido: una frase suelta de un seminario puede ser inentendible sin la anterior. El solapamiento (overlap) entre chunks consecutivos existe para que una idea que cae justo en el borde de un corte aparezca completa en al menos uno de los dos. No hay un número correcto universal: depende de la longitud de las unidades de sentido del corpus (un párrafo de Lacan no mide lo mismo que una fila de tabla), y se ajusta mirando qué devuelve el retrieval para preguntas reales.

Y una regla que se olvida seguido: el encoder de la pregunta y el de los pasajes tienen que ser el mismo modelo con la misma configuración. Si reindexás con otro encoder, o cambiás cómo preprocesás el texto de un lado y no del otro, las distancias dejan de significar lo mismo y el retrieval se degrada sin ningún error visible.

🔧 Un detalle operativo real de tu proyecto

e5-large corre en modo simétrico (sin los prefijos "query: " / "passage: " que ese modelo normalmente pide) — una decisión documentada en OPERACIONES.md §7, no el default de la librería. Y corre separado del LLM: la UI puede forzar EMBED_DEVICE=cpu para dejarle toda la VRAM de la RTX 5090 al modelo grande.

📐 Formulación matemática de la función de pérdida InfoNCE en e5-large

El encoder transforma una consulta q y un pasaje d en representaciones vectoriales densas normalizadas sobre la hiperesfera unitaria SD-1 (D=1024 en e5-large):

u=fϕ(q)∥fϕ(q)∥2,v=fϕ(d)∥fϕ(d)∥2

El entrenamiento contrastivo optimiza la pérdida InfoNCE sobre un lote de B pares relevantes (qi,di+) frente a negativos dentro del lote dj-:

LInfoNCE=-∑i=1Blogexpui⊤vi+τexpui⊤vi+τ+∑j≠iexpui⊤vj-τ

Donde τ actúa como parámetro de temperatura. Esta función maximiza analíticamente la cota inferior de la información mutua I(Q;D+)≥logB-LInfoNCE, garantizando que preguntas complejas sobre conceptos freudianos y lacanianos se alineen geométricamente en ChromaDB con los pasajes teóricos exactos.

El que decide cuándo consultar esa memoria es el agente: un agente ReAct (Yao et al., 2022) es un LLM que alterna, en el mismo texto, un paso de razonamiento ("necesito ver qué dice el Seminario 20 sobre esto") con una acción sobre una herramienta (buscar en el índice, llamar a una función del motor simbólico), lee el resultado, y vuelve a razonar — hasta que decide que tiene suficiente para responder. El ReActAgent de LlamaIndex es una implementación de ese bucle: el retrieval deja de ser un paso fijo antes de cada respuesta y pasa a ser una decisión del modelo.

El bucle ReAct sobre la memoria: razonar, buscar, observar, responder La pregunta entra al agente, un decoder que alterna razonamiento y acciones. Si decide buscar, la pregunta se embebe con el mismo encoder e5-large que indexó los pasajes, ChromaDB devuelve los k chunks más cercanos por coseno y esos chunks se pegan al prompt. Si decide consultar el grafo simbólico, una función como comparar conceptos devuelve texto. El agente observa, vuelve a razonar y, cuando tiene suficiente, responde. pregunta de la UI (Streamlit) agente ReAct · decoder-only Gemma-4-31B-lacan vía vLLM 1. razona: «necesito ver qué dice el Seminario 20 sobre esto» 2. actúa: elige una herramienta 3. observa el resultado y vuelve a 1 4. cuando alcanza, responde herramienta: buscar en la memoria pregunta → encoder e5-large → vector ChromaDB: k pasajes más cercanos por coseno (de 45.904) el mismo encoder y el mismo preprocesado que indexó los pasajes → los chunks vuelven como texto y se pegan al prompt el modelo responde leyendo, no recordando herramienta: motor simbólico comparar_conceptos · extender_red → texto respuesta, cuando el agente decide que tiene suficiente el retrieval deja de ser un paso fijo antes de cada respuesta: es una decisión del modelo, y puede repetirse varias veces
RAG dentro de un agente. El decoder no consulta la memoria por reflejo: razona, decide qué herramienta usar, lee lo que vuelve y sigue. La búsqueda semántica es la del capítulo 5 de HF con ChromaDB en vez de FAISS; el grafo de 16 nodos es la otra herramienta, y devuelve texto igual que la memoria.

3. El esqueleto simbólico sobre la red neuronal

Acá tu proyecto agrega algo que el Capítulo 1 no toca porque no es parte del transformer en sí: motor_razonamiento_lacan.json y grafo_lacan.py mantienen una estructura de conceptos explícita y editable a mano — nodos como Goce, Falo / Objeto a, Lenguaje, con sus variantes según el Seminario 4 o el Seminario 20 — que ninguna red neuronal "sabe" de por sí, por más grande que sea.

Es la diferencia entre conocimiento paramétrico (lo que el LLM aprendió difuso en sus pesos durante el CPT) y conocimiento simbólico explícito (esta estructura, que podés leer, editar y versionar como cualquier archivo). El motor no reemplaza al LLM — lo guía: funciones como comparar_conceptos o extender_red arman el prompt correcto para que el generador razone sobre relaciones que ya sabés que existen, en vez de esperar a que las infiera solo.

📐 Difusión topológica en el grafo simbólico: Random Walk with Restart

Sea G=(V,E) el grafo de 16 significantes amo con matriz de adyacencia A∈{0,1}16×16 y grados Dii=∑jAij. La matriz de transición estocástica es P=D-1A. Para un concepto inicial activado en la consulta con distribución semilla p(0)∈Δ15, la propagación contextualizada se define mediante la ecuación recursiva de Personalized PageRank con factor de reinicio α∈(0,1):

p(k+1)=(1-α)P⊤p(k)+αp(0)

Cuya solución analítica en estado estacionario es:

p*=αI-(1-α)P⊤-1p(0)

Los nodos con mayor masa de probabilidad estacionaria pi* seleccionan automáticamente los conceptos y citas del seminario correspondiente que deben inyectarse en el prompt del generador.

Un grafo de 16 nodos no compite con 31 mil millones de parámetros — los orienta.

4. Reentrenar por generaciones

El fine-tuning tiene sus propios capítulos en el curso — el Capítulo 3 (el bucle de entrenamiento con Trainer) y el Capítulo 11 (fine-tuning de LLMs con chat templates, SFT y LoRA) — pero tu sistema ya corre el ciclo completo en producción, así que acá va la versión aplicada: Unsloth entrena adaptadores LoRA (Low-Rank Adaptation — en vez de tocar los 31B de parámetros originales, entrenás matrices mucho más chicas que se suman a las existentes) sobre la producción curada, en una VM Spot gobernada por un watchdog, a un costo de USD 6-10 por ciclo.

LoRA, QLoRA y CPT, en lo esencial

LoRA (Hu et al., 2021) parte de una observación: el cambio que un fine-tuning le hace a una matriz de pesos W tiene rango bajo — se puede aproximar bien como el producto de dos matrices flacas, A (d × r) y B (r × d), con r de 8, 16 o 64 en vez de miles. Se congela W y se entrena solo A·B; el modelo calcula W·x + A·B·x. Para una matriz de 4096 × 4096 y r = 16, eso son 131 mil parámetros entrenables en vez de 16,8 millones — menos del 1 %. Los adaptadores se guardan aparte (unos MB), y se pueden mergear a los pesos base después, que es lo que hace tu pipeline antes de cuantizar.

QLoRA (Dettmers et al., 2023) agrega un truco de memoria: el modelo base congelado se guarda en 4 bits durante el entrenamiento, y solo los adaptadores (y los gradientes, que son solo de los adaptadores) viven en alta precisión. Es lo que permite fine-tunear un modelo de decenas de miles de millones de parámetros en una sola GPU. El resultado es el mismo tipo de adaptador que LoRA — la cuantización es un detalle de cómo se entrena, no del artefacto que sale.

CPT (Continued Pretraining) y SFT (Supervised Fine-Tuning) se diferencian por el dato, no por el mecanismo: CPT sigue entrenando con el objetivo original — predecir el siguiente token sobre texto crudo (el corpus de Lacan y Freud) — para absorber un dominio; SFT entrena sobre pares instrucción → respuesta con el formato de chat, para enseñar un comportamiento. Tu Generación 0 es CPT; los ciclos generacionales sobre producción curada son SFT. Los dos se pueden hacer con LoRA.

Cada ciclo generacional sigue el mismo camino:

  1. El daemon generativo produce insights y diálogos de forma autónoma (24/7).
  2. Curaduría: cada pieza se filtra por novedad y por rating 👍/👎 humano.
  3. Esa producción curada, más un 20% de "replay" de generaciones anteriores (para no olvidar), entra al SFT.
  4. Gate de aceptación: el nuevo modelo se compara contra el anterior antes de promoverse — con rollback si empeora.

🔧 En tu proyecto

El replay del 20% es la respuesta concreta al problema que el Capítulo 1 nombra pero no resuelve ("fine-tunear no borra el sesgo de base" — acá el riesgo análogo es el olvido catastrófico: que la Generación N+1 pierda matices de la N-1 por sobreajustarse solo a lo último). Mezclar producción vieja con la nueva en cada ciclo es la mitigación que elegiste, no algo que Unsloth te da gratis.

📐 Mitigación matemática del olvido catastrófico mediante Replay Buffer

Cuando un modelo con pesos preentrenados θ0 se ajusta sobre una nueva distribución Dnew, el riesgo de olvido catastrófico de la distribución base Dbase se formaliza analíticamente a través de la expansión de Taylor de la divergencia KL:

DKL(Pθ0∥Pθ)≈12(θ-θ0)⊤F(θ-θ0)

Donde F=Ex∼Dbase[∇θlogPθ(x)∇θlogPθ(x)⊤] es la matriz de Información de Fisher empírica. El buffer de repetición del 20% implementa un estimador insesgado de la mezcla convexa de densidades:

Ltrain(θ)=0.8Ex∼Dnew[L(x;θ)]+0.2Ex∼Danchor[L(x;θ)]

Este término actúa como un ancla regularizadora que acota el desplazamiento en los autoespacios de alta curvatura de F, evitando que el fine-tuning sucesivo colapse las capacidades lingüísticas basales del modelo.

Un ciclo generacional: qué entra al entrenamiento y quién decide si el modelo nuevo se promueve La generación N produce insights y diálogos; la curaduría filtra por rating humano y novedad. El dataset de SFT mezcla un 80 por ciento de producción nueva con un 20 por ciento de replay de generaciones anteriores. Unsloth entrena adaptadores LoRA en una VM spot. El candidato pasa por un gate que lo compara con N: si mejora se promueve como N+1, si no, rollback a N. Generación N produce 24/7 curaduría 👍/👎 humano + filtro de novedad dataset del SFT 80 % producción curada de N 20 % el 20 % es replay: producción de generaciones anteriores, para que el gradiente también tenga que satisfacer lo viejo SFT · LoRA con Unsloth VM Spot con watchdog · USD 6–10 por ciclo gate de aceptación candidato vs. Generación N mejora Generación N+1 empeora → rollback: N sigue sirviendo sin replay: olvido catastrófico, los matices de N−1 se erosionan ciclo a ciclo
El ciclo de §4 con las dos decisiones que lo vuelven un sistema y no un experimento: la mezcla del dataset, que es una decisión sobre datos y no sobre librerías, y el gate con rollback, que compara contra el modelo anterior antes de promover.

Olvido catastrófico y replay, en general

El olvido catastrófico es una propiedad de cualquier red neuronal entrenada por gradiente: cuando entrenás sobre datos nuevos, el gradiente mueve los pesos hacia donde minimiza la loss de esos datos, sin ninguna noción de qué otras cosas esos mismos pesos estaban codificando. Si los datos nuevos no contienen ejemplos de lo viejo, no hay nada que "defienda" lo viejo — y se erosiona, a veces en pocas épocas. Es peor cuanto más distinta es la distribución nueva y cuanto más chico es el conjunto (más épocas sobre lo mismo = más sobreajuste a lo último).

El replay (o experience replay, Rolnick et al., 2018) es la mitigación más simple y una de las más robustas: mezclar en cada ciclo una fracción de ejemplos de las etapas anteriores con los nuevos, para que el gradiente tenga que satisfacer a los dos a la vez. No hace falta guardar todo — una muestra representativa alcanza para anclar. Alternativas más elaboradas (penalizar cambios en los pesos que eran importantes para las tareas viejas, o reservar parámetros por tarea) existen, pero el replay tiene la ventaja de no requerir nada especial del modelo ni del optimizador: es una decisión sobre el dataset. Lo que sí exige es que los ejemplos viejos sigan siendo buenos — replay de producción mal curada propaga los errores hacia adelante.

5. Après-coup: la teoría explicando tu propia arquitectura

Esto es lo más lindo del proyecto, y tu propio README ya lo dice explícito: el efecto del reentrenamiento generacional es après-coup — la producción curada "resignifica retroactivamente al modelo que la produjo".

Après-coup (Nachträglichkeit en Freud, retomado por Lacan) es la idea de que un evento temprano no tiene un significado fijo en el momento en que ocurre — su sentido se termina de constituir después, cuando un evento posterior lo resignifica. No es que el pasado cambie: cambia cómo se lee.

Tu ciclo de entrenamiento hace exactamente eso, en código: la Generación N genera texto libremente. Ese texto se cura, y la curaduría vuelve como señal de entrenamiento que ajusta los pesos de la Generación N+1. La "Generación N" nunca se reescribe — pero lo que significó, en términos de qué pesos terminó produciendo el linaje del modelo, se decide después, por un acto de lectura posterior (la curaduría). Construiste, sin necesariamente buscarlo como metáfora, un sistema donde la mecánica de entrenamiento y el concepto que el modelo estudia son la misma estructura.

6. Lo que generaliza

Lo que este sistema enseña sirve para cualquier proyecto que combine un LLM con datos propios:

  • Dos velocidades de aprendizaje. Lo que cambia todos los días va a la memoria (RAG, sin entrenar); lo que tiene que volverse parte de la voz del modelo va a los pesos (fine-tuning, con gate). Confundir las dos — fine-tunear para "agregar un documento", o meter en el prompt lo que debería ser un rasgo estable — es el error de diseño más común.
  • Un modelo por tarea. Generar y embeber son tareas de forma opuesta; usar un decoder para embeber o un encoder para generar es posible pero peor que usar cada uno para lo suyo.
  • El retrieval se evalúa aparte del generador. Si la respuesta es mala, primero mirá qué chunks se recuperaron: la mitad de los problemas de RAG son de chunking o de embeddings, no del LLM.
  • Lo que sabés, escribilo. Una estructura simbólica chica y editable (un grafo de conceptos, una taxonomía, una lista de reglas) orienta a un modelo enorme mejor que esperar que lo haya memorizado — y se versiona como código.
  • Todo cambio de pesos pasa por un gate con rollback. Comparar contra el modelo anterior antes de promover, y poder volver atrás, es lo que convierte un experimento en un sistema.
  • Replay contra el olvido. Cualquier entrenamiento incremental necesita una fracción de datos viejos en cada ciclo; es una decisión de dataset, no de librería.

7. Mapa completo del sistema

Con todo el Capítulo 1 ya aplicado, así se lee tu propio stack de punta a punta:

  • Decoder-only (Gemma-4-31B-lacan, NVFP4-mse) → genera texto, sostiene la voz teórica.
  • Encoder-only (multilingual-e5-large) → embebe 45.904 pasajes para RAG, no genera nada.
  • Decoding especulativo (draft MTP, BF16) → mismo mecanismo que en tu pipeline NVFP4, aplicado acá para servir rápido en 24 GB.
  • Grafo simbólico → conocimiento explícito, editable, que orienta al LLM sin depender de que lo haya memorizado.
  • LoRA generacional + gate + replay → el mecanismo de aprendizaje lento, con su propio nombre teórico: après-coup.

Todo lo que faltaba del Capítulo 1 abstracto — arquitectura, inferencia, ahora también embeddings y fine-tuning en abstracto — ya lo tenés corriendo, con números reales, en dos proyectos que son tuyos.

8. Ponete a prueba

Respondé antes de abrir cada respuesta. Si fallás una, volvé a la sección indicada.

1. Llega un libro nuevo al corpus. ¿Va a la memoria o a los pesos? ¿Por qué?

A la memoria: se chunkea, se embebe y entra a ChromaDB al instante, sin entrenar. A los pesos solo llega la producción curada, por generaciones, con gate y rollback. Son las dos velocidades de aprendizaje del §0: lo nuevo y puntual va al RAG; lo que tiene que volverse parte de la voz va al fine-tuning. (§0, §4)

2. ¿Por qué hacen falta dos modelos (uno decoder, uno encoder) en vez de usar Gemma para todo?

Porque las tareas tienen forma opuesta: generar es producir una secuencia token a token; embeber es comprimir un texto completo en un vector comparable con miles de otros. El encoder-only bidireccional está diseñado para lo segundo, y es mucho más chico y rápido que un decoder de 31B para esa tarea. (§2)

3. Duplicás el tamaño de chunk sin cambiar nada más. ¿Qué esperás que pase con el retrieval?

Cada vector representa más texto, así que "promedia" más temas y se parece menos a una pregunta concreta: baja la precisión del retrieval, y cada resultado ocupa el doble de contexto del decoder. A cambio, un chunk grande conserva más contexto alrededor de la idea. Hay que mirar qué devuelve para preguntas reales, no hay número universal. (§2)

4. ¿Qué diferencia hay entre el conocimiento del grafo de 16 nodos y el que Gemma-4-lacan aprendió en el CPT?

El grafo es conocimiento simbólico explícito: legible, editable, versionable, y se usa para armar prompts que dirigen al modelo hacia relaciones que ya sabés que existen. El del CPT es paramétrico: difuso en los pesos, no se puede leer ni editar directamente. El grafo no compite con los 31B — los orienta. (§3)

5. ¿Qué pasaría si el reentrenamiento generacional se hiciera solo con la producción de la última generación, sin replay?

Olvido catastrófico: el gradiente solo defiende lo que está en los datos nuevos, así que los matices de generaciones anteriores se erosionan ciclo a ciclo. Mezclar un 20% de producción vieja obliga al modelo a seguir satisfaciendo lo anterior mientras aprende lo nuevo. (§4)

6. ¿En qué sentido exacto el ciclo de reentrenamiento es après-coup y no solo una metáfora linda?

Porque la estructura es la misma: la Generación N produce texto sin saber cuál va a importar; la curaduría posterior decide qué de eso vuelve como señal de entrenamiento y, con eso, qué pesos tendrá N+1. El pasado (lo generado) no cambia, pero su significado para el linaje del modelo se decide después, por un acto de lectura. (§5)

9. Para practicar

✏️ Ejercicio 1 — RAG mínimo, con cinco documentos de juguete

El mecanismo completo de la memoria del §2, aislado en veinte líneas: un encoder multilingüe de la misma familia que el de tu proyecto (la versión small, para que corra en CPU en segundos), una colección de ChromaDB en memoria, y una pregunta. Fijate qué documento sale primero y con qué distancia — y después cambiá la pregunta por una que no tenga respuesta en el corpus, para ver que el retrieval igual devuelve algo: RAG no sabe decir "no hay nada", solo devuelve lo más cercano.

# pip install sentence-transformers chromadb
from sentence_transformers import SentenceTransformer
import chromadb

docs = [
    "El après-coup es la resignificación retroactiva de un evento por otro posterior.",
    "El objeto a es el objeto causa del deseo, no el objeto del deseo.",
    "La receta lleva dos huevos, harina y una pizca de sal.",
    "El KV cache evita recalcular la atención sobre todo el contexto en cada token.",
    "El goce, en el Seminario 20, se distingue del placer.",
]
modelo = SentenceTransformer("intfloat/multilingual-e5-small")
# e5 espera prefijos: "passage: " para documentos, "query: " para preguntas
vectores = modelo.encode(["passage: " + d for d in docs], normalize_embeddings=True)

cliente = chromadb.Client()                                  # en memoria, se borra al salir
col = cliente.create_collection("juguete", metadata={"hnsw:space": "cosine"})
col.add(ids=[f"d{i}" for i in range(len(docs))], documents=docs, embeddings=vectores.tolist())

pregunta = "query: ¿qué significa que algo se resignifique después?"
q = modelo.encode([pregunta], normalize_embeddings=True)
res = col.query(query_embeddings=q.tolist(), n_results=3)
for texto, dist in zip(res["documents"][0], res["distances"][0]):
    print(f"{dist:.3f}  {texto}")
Pista / qué esperar

El documento del après-coup tiene que salir primero, con la distancia más chica; la receta, último o directamente afuera de los 3. Para ver el efecto de los prefijos, sacá "query: " y "passage: " y comparalo — es la decisión "modo simétrico" que tu proyecto documenta en OPERACIONES.md: con este modelo, las distancias cambian aunque el orden pueda mantenerse. Y para ver el problema del chunking, reemplazá los cinco documentos por un solo texto largo y cortalo con distintos tamaños antes de indexar.

✏️ Ejercicio 2 — Un chunker con solapamiento, y mirá qué se rompe en los bordes

Sin dependencias: cortá un texto en chunks de N palabras con un solapamiento de M, y imprimí los bordes. La pregunta a responder es concreta: ¿hay alguna frase que quede partida en todos los chunks que la contienen? Probá con M = 0 y con M = N/4.

def chunks(texto, n=40, solapamiento=10):
    palabras = texto.split()
    paso = n - solapamiento
    return [" ".join(palabras[i:i + n]) for i in range(0, max(1, len(palabras) - solapamiento), paso)]

texto = open("cualquier_texto_largo.txt", encoding="utf-8").read()
for m in (0, 10):
    cs = chunks(texto, 40, m)
    print(f"solapamiento={m}: {len(cs)} chunks")
    for c in cs[:3]:
        print("   …", c[-60:])            # el final de cada chunk: ahí se parten las frases
Pista / qué esperar

Con solapamiento 0, cada frase que cruza un borde queda partida en dos chunks y ninguno la tiene entera. Con solapamiento 10 aparecen más chunks (más costo de indexado) pero la mayoría de esas frases aparecen completas en al menos uno. Cortar por palabras es lo más crudo; el siguiente paso es cortar por párrafos u oraciones y usar el solapamiento solo como red de seguridad.

10. Fuentes y para seguir