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
🎯 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.
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:
- 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í.
- 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.
- 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 y un pasaje en representaciones vectoriales densas normalizadas sobre la hiperesfera unitaria ( en e5-large):
El entrenamiento contrastivo optimiza la pérdida InfoNCE sobre un lote de pares relevantes frente a negativos dentro del lote :
Donde actúa como parámetro de temperatura. Esta función maximiza analíticamente la cota inferior de la información mutua , 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.
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 el grafo de 16 significantes amo con matriz de adyacencia y grados . La matriz de transición estocástica es . Para un concepto inicial activado en la consulta con distribución semilla , la propagación contextualizada se define mediante la ecuación recursiva de Personalized PageRank con factor de reinicio :
Cuya solución analítica en estado estacionario es:
Los nodos con mayor masa de probabilidad estacionaria seleccionan automáticamente los conceptos y citas del seminario correspondiente que deben inyectarse en el prompt del generador.
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:
- El daemon generativo produce insights y diálogos de forma autónoma (24/7).
- Curaduría: cada pieza se filtra por novedad y por rating 👍/👎 humano.
- Esa producción curada, más un 20% de "replay" de generaciones anteriores (para no olvidar), entra al SFT.
- 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 se ajusta sobre una nueva distribución , el riesgo de olvido catastrófico de la distribución base se formaliza analíticamente a través de la expansión de Taylor de la divergencia KL:
Donde 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:
Este término actúa como un ancla regularizadora que acota el desplazamiento en los autoespacios de alta curvatura de , evitando que el fine-tuning sucesivo colapse las capacidades lingüísticas basales del modelo.
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
- Lewis et al. — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (arXiv:2005.11401) — el paper que le puso nombre a RAG; leelo para ver que la idea original entrenaba retriever y generador juntos, algo que casi nadie hace hoy.
- Yao et al. — ReAct: Synergizing Reasoning and Acting in Language Models (arXiv:2210.03629) — el bucle razonar → actuar → observar que implementa el
ReActAgentde LlamaIndex. - Wang et al. — Multilingual E5 Text Embeddings: A Technical Report (arXiv:2402.05672) — cómo se entrenó el encoder de tu memoria, y por qué pide los prefijos
query:/passage:. - Hu et al. — LoRA: Low-Rank Adaptation of Large Language Models (arXiv:2106.09685) — la idea del rango bajo, con los experimentos que muestran que r chico alcanza.
- Dettmers et al. — QLoRA: Efficient Finetuning of Quantized LLMs (arXiv:2305.14314) — el modelo base en 4 bits durante el entrenamiento; lo que hace posible fine-tunear 31B en una GPU.
- Rolnick et al. — Experience Replay for Continual Learning (arXiv:1811.11682) — la evidencia de que una muestra chica de datos viejos alcanza para frenar el olvido catastrófico.
- Unsloth — documentación — CPT y QLoRA con la librería que usa tu pipeline; buscá las guías de "continued pretraining".
- Chroma — documentación — colecciones, métricas de distancia y metadata; lo que hay debajo de tu memoria de 45.904 pasajes.
- Laplanche, J. & Pontalis, J.-B. — Diccionario de psicoanálisis, entrada "Posterioridad (après-coup)" — la definición canónica del concepto del §5, con su historia en Freud y su relectura lacaniana.