Un recorrido desde primeros principios sobre la memoria de agentes: desde listas de Python hasta archivos Markdown, búsqueda vectorial, híbridos grafo-vector y, finalmente, una solución limpia y de código abierto para todo esto.

Un LLM es apátrida por diseño. Cada llamada a la API comienza desde cero. La "memoria" que sientes al chatear con ChatGPT es una ilusión creada al reenviar todo el historial de la conversación con cada solicitud.
Ese truco funciona para charlas casuales. Se desmorona en cuanto intentas construir un agente real.
Aquí tienes 7 modos de fallo que aparecen en cuanto te saltas la memoria:
- Amnesia de contexto: el agente pregunta por información que ya le diste
- Cero personalización: cada interacción se siente genérica
- Fallo en tareas de varios pasos: el estado intermedio se pierde silenciosamente a mitad de la tarea
- Errores repetidos: sin recuerdo episódico, los mismos errores para siempre
- Sin acumulación de conocimiento: cada sesión empieza desde cero
- Alucinaciones por lagunas: cuando el contexto se desborda, el modelo inventa
- Colapso de identidad: sin continuidad, no hay confianza
La respuesta obvia es "arrojarle más contexto". Por eso las ventanas de 128K y 200K tokens parecen que deberían solucionarlo todo.
No lo hacen.
La precisión cae más del 30% cuando la información relevante está en medio de un contexto largo. Este es el conocido efecto de "perdido en el medio".
El contexto es un presupuesto compartido: los prompts del sistema, los documentos recuperados, el historial de la conversación y la salida compiten por los mismos tokens.
Incluso con 100K tokens, la ausencia de persistencia, priorización y relevancia hace que la longitud bruta del contexto sea insuficiente.

La memoria no consiste en meter más texto en el prompt. Se trata de estructurar lo que el agente recuerda para que pueda encontrar lo que importa.
El marco de la ciencia cognitiva que realmente ayuda
La formulación de Lilian Weng en 2023 se ha convertido en el marco por defecto:
Agente = LLM + Memoria + Planificación + Uso de herramientas.
Los cuatro pilares en igualdad de condiciones.
Su taxonomía toma prestado de la ciencia cognitiva, donde la memoria humana se divide en tres sistemas:
- Memoria sensorial: captura la entrada perceptual bruta y la retiene por una fracción de segundo. Solo las partes a las que prestas atención se transmiten.
- Memoria de trabajo: aquí ocurre el pensamiento activo. Retiene aproximadamente 7±2 elementos a la vez (hallazgo de Miller en 1956). Si pierdes el foco, los contenidos desaparecen.
- Memoria a largo plazo: almacenamiento duradero sin límite práctico de capacidad. La recuperación es el cuello de botella: puedes almacenar millones de cosas y aun así fallar al recordar la que necesitas.
Cada una se asigna directamente a un componente en las arquitecturas modernas de agentes:

La memoria a largo plazo se divide a su vez:
- Episódica: eventos pasados específicos ("el martes, el clúster de PostgreSQL se cayó")
- Semántica: hechos y conceptos ("PostgreSQL es una base de datos relacional")
- Procedimental: habilidades y flujos de trabajo ("cuando un usuario pide un reembolso, primero verifica la fecha de compra")
El puente entre lo episódico y lo semántico es la consolidación de la memoria: eventos repetidos específicos que se destilan en conocimiento general. Un agente que nota que "los usuarios prefieren consistentemente los resúmenes ejecutivos" a lo largo de docenas de interacciones debería convertir eso en una regla reutilizable. Sin consolidación, tu agente reproduce eventos individuales en lugar de aprender de ellos.

El agente mínimo, y qué se rompe primero
Quita los marcos teóricos y un agente es un bucle: percibir, pensar, actuar.
1class Agent:2 """Agente de IA mínimo: percibir, pensar, actuar"""3 def __init__(self):4 self.client = anthropic.Anthropic()5 self.model = "claude-sonnet-4-20250514"67 def run(self, user_input: str) -> str:8 response = self.client.messages.create(9 model=self.model,10 max_tokens=1024,11 messages=[{"role": "user", "content": user_input}],12 )13 return response.content[0].text
Dile "Tengo 4 manzanas", luego pregúntale "Me comí una, ¿cuántas quedan?" y no tendrá idea de qué manzanas estás hablando. Cada llamada existe de forma aislada.
Capa 1: La lista de Python
La primera solución a la que todos recurren:
1class Agent:2 def __init__(self):3 self.client = anthropic.Anthropic()4 self.messages = [] # Toda la "memoria" es una lista56 def chat(self, user_input: str) -> str:7 self.messages.append({"role": "user", "content": user_input})8 response = self.client.messages.create(9 model="claude-sonnet-4-20250514",10 max_tokens=1024,11 messages=self.messages, # Historial completo enviado cada vez12 )13 reply = response.content[0].text14 self.messages.append({"role": "assistant", "content": reply})15 return reply
Ahora funcionan los múltiples turnos. La pregunta de las manzanas se responde correctamente porque la conversación completa se reenvía con cada llamada.
Dos problemas aparecen rápido:
- La lista crece sin límite. Alrededor del turno 200, alcanzas el techo de contexto y los mensajes más antiguos se caen silenciosamente. El nombre del usuario del turno 1 desaparece mucho antes que el chiste desechable de ayer. Sin priorización, solo orden cronológico estricto.
- Todo vive en RAM. En cuanto el proceso de Python termina, tu agente no tiene idea de quién eres.
Capa 2: Archivos Markdown para persistencia
El siguiente paso es escribir la memoria en disco. Markdown encaja de forma natural: es legible por humanos, compatible con Git y el agente puede leerlo como texto plano. Claude Code usa exactamente este patrón con archivos CLAUDE.md y MEMORY.md.
1class MarkdownMemoryAgent:2 def __init__(self):3 self.client = anthropic.Anthropic()4 self.history_file = Path("memory/conversation_history.md")5 self.facts_file = Path("memory/known_facts.md")67 def save_to_disk(self, role: str, content: str) -> None:8 with open(self.history_file, "a") as f:9 f.write(f"### {role} at {datetime.now().isoformat()}\n{content}\n\n")1011 def load_history(self) -> str:12 if self.history_file.exists():13 return self.history_file.read_text()14 return ""1516 def chat(self, user_input: str) -> str:17 self.save_to_disk("user", user_input)18 history = self.load_history()19 response = self.client.messages.create(20 model="claude-sonnet-4-20250514",21 max_tokens=1024,22 system=f"Conversación anterior:\n{history}",23 messages=[{"role": "user", "content": user_input}],24 )25 reply = response.content[0].text26 self.save_to_disk("assistant", reply)27 return reply
La persistencia está resuelta. Reinicia el script y la conversación sigue en disco. También podrías mantener un archivo de hechos separado que el agente extraiga con el tiempo:
1- El nombre del usuario es Sarah2- Sarah es la líder del backend en Acme Corp3- Acme Corp es una empresa SaaS B2B4- Actualmente migrando la base de datos de producción a una nueva región de AWS
Puedes abrir el archivo en cualquier editor, ver exactamente lo que el agente sabe y corregirlo manualmente. Realmente útil para prototipado.
Con 4 hechos, esto funciona perfectamente. Carga todo el archivo en el contexto y el LLM maneja cualquier pregunta sobre Sarah, su empresa o su industria.
Ahora avanza tres meses. Tu agente tiene 2000 hechos extraídos y 200 registros de conversación. Eso son más de 500K tokens de markdown en disco, y tu ventana de contexto es de 128K.
Ya no puedes cargarlo todo. Necesitas recuperar selectivamente solo los hechos relevantes para la consulta actual. Con archivos planos, tu única opción es la búsqueda por palabras clave:
1# El usuario pregunta: "¿Cuál es el estado de nuestra migración a la nube?"2grep("cloud migration", facts_file)3# Devuelve: []4# El hecho en disco dice "migrando la base de datos de producción a una nueva región de AWS."5# Las palabras "cloud migration" no aparecen en ninguna parte.67# El usuario pregunta: "¿Qué equipo está manejando el trabajo de la base de datos?"8grep("database team", facts_file)9# Devuelve: []10# Un hecho dice que Sarah "es la líder del backend." Otro dice que el equipo11# está "migrando la base de datos de producción." Pero ninguna línea contiene12# "database" y "team" juntos.
A pequeña escala, los archivos markdown funcionan. A escala real, fuerzan la recuperación por palabras clave, y las palabras clave no pueden manejar sinónimos, paráfrasis ni conexiones entre hechos.
La información está en disco. Pero no puedes cargarla toda, y la búsqueda por palabras clave es demasiado frágil para encontrar las piezas correctas.
Si has usado OpenClaw, has visto esto en acción. Almacena la memoria como archivos de punto de control en markdown, y con semanas de uso diario, los hechos anteriores se desvanecen silenciosamente a medida que el contexto se acumula y se compacta. El almacenamiento está ahí. La recuperación no.
Almacenamiento sin recuperación inteligente es una biblioteca sin catálogo.
Capa 3: Búsqueda vectorial y el muro que encuentra
Añade embeddings. Fragmenta tu markdown, incrusta los fragmentos, busca por similitud de coseno. Ahora "base de datos" coincide con "PostgreSQL" porque sus vectores viven cerca en el espacio de embeddings. El problema de los sinónimos se disuelve.
Luego te topas con un nuevo muro. Considera estos tres hechos en tu base de datos vectorial:
1- "Alice es la líder técnica del Proyecto Atlas"2- "El Proyecto Atlas usa PostgreSQL como almacén de datos principal"3- "El clúster de PostgreSQL tuvo una interrupción el martes"
El usuario pregunta: "¿El proyecto de Alice se vio afectado por la interrupción del martes?"
La consulta menciona a Alice y la interrupción del martes, por lo que la búsqueda vectorial clasifica alto el primer y el tercer hecho. Pero el puente crítico, "El Proyecto Atlas usa PostgreSQL", no menciona ni a Alice ni al martes. Es la pieza conectora, y es la que no aparecerá.
Cada hecho es un punto aislado en el espacio de embeddings. El tejido conectivo que los une es invisible para los vectores.

Esto no es un caso extremo. Es la forma normal de las preguntas del mundo real. El conocimiento empresarial es inherentemente relacional: las personas pertenecen a equipos, los equipos son dueños de proyectos, los proyectos dependen de sistemas, los sistemas tienen incidentes. Cualquier pregunta que cruce dos o más saltos excede lo que la recuperación vectorial plana puede responder.
La matriz de capacidades
Cada capa soluciona el dolor anterior pero revela uno más profundo:

Necesitas persistencia, comprensión semántica y razonamiento relacional en una sola capa de memoria.
Construir esto tú mismo significa pegar una base de datos vectorial, una base de datos de grafos, un almacén relacional, un extractor de entidades, un pipeline de deduplicación y un sistema de ponderación de aristas. Eso son semanas de trabajo de infraestructura antes de escribir una sola línea de lógica de agente.
He estado usando una solución que llena este vacío de forma limpia. Es completamente de código abierto, maneja los tres paradigmas de almacenamiento bajo un mismo techo y puedes ponerla en funcionamiento en minutos. Hablemos de Cognee.
Cognee: tres almacenes, un motor, cuatro llamadas
Cognee es un motor de conocimiento de código abierto construido para la memoria de agentes. Combina la búsqueda vectorial con grafos de conocimiento y una capa relacional de procedencia en un solo sistema.
La superficie completa de la API son cuatro llamadas asíncronas:
1import cognee23await cognee.add("Tu documento aquí") # Ingiere cualquier cosa4await cognee.cognify() # Construye grafo de conocimiento + embeddings5await cognee.memify() # Auto-mejora la memoria6await cognee.search("Tu consulta") # Recupera con razonamiento
Detrás de esas cuatro llamadas hay una arquitectura de tres almacenes.

¿Por qué tres almacenes y no uno?
Cada almacén captura una dimensión del conocimiento que los otros no pueden:
- Almacén relacional → procedencia: de dónde vienen los datos, cuándo se ingirieron, quién tiene acceso
- Almacén vectorial → semántica: qué significa el contenido, a qué es similar
- Almacén de grafos → relaciones: cómo se conectan las entidades, qué causa qué, quién reporta a quién
Aplana cualquiera de estos y pierdes información que importa para la precisión de la recuperación.
La pila predeterminada es SQLite + LanceDB + Kuzu, completamente embebida y basada en archivos. pip install cognee más una clave de API de LLM y ya estás funcionando.
Sin Docker, sin servicios externos.
Para producción, cambia SQLite por Postgres, LanceDB por Qdrant/Pinecone/pgvector y Kuzu por Neo4j/FalkorDB/Neptune.
La misma API de cuatro llamadas de cualquier manera.
¿Qué hace realmente cognify?
cognee.cognify() ejecuta un pipeline de múltiples etapas que convierte texto bruto en conocimiento estructurado e interconectado:
- Clasificación de documentos por tipo y dominio
- Verificación de permisos para control de acceso multiinquilino
- Extracción de fragmentos que respeta la estructura de párrafos (no cortes de tamaño fijo)
- Extracción de entidades y relaciones mediante LLM, con deduplicación automática mediante hash de contenido
- Generación de resúmenes para una recuperación eficiente
- Indexación dual en el almacén vectorial (embeddings) y el almacén de grafos (aristas)
El paso de deduplicación importa más de lo que parece. Si la misma entidad aparece en 50 documentos, Cognee la fusiona en un solo nodo de grafo con 50 aristas entrantes. Tu agente ya no ve a "Alice" como 50 desconocidos diferentes. Y el pipeline es incremental por defecto: solo los archivos nuevos o actualizados se reprocesan.

Cada nodo del grafo tiene un embedding correspondiente. Esta representación dual es el truco central: entra a través de los vectores (encuentra contenido semánticamente similar) y sal a través del grafo (sigue relaciones hacia entidades conectadas), o viceversa. Eso es lo que hace que las consultas de múltiples saltos funcionen sin sacrificar la búsqueda semántica.
Memify: memoria que aprende
memify() es lo que separa a Cognee de cualquier herramienta de "ingerir y buscar". Ejecuta un paso de optimización inspirado en RL sobre el grafo:
- Fortaleciendo caminos útiles que llevaron a una buena recuperación
- Podando nodos obsoletos que no han sido tocados
- Autoajustando los pesos de las aristas basándose en el uso real
- Agregando hechos derivados al identificar relaciones implícitas
El grafo de un agente de atención al cliente fortalece naturalmente los caminos a través de la documentación del producto y las políticas de reembolso, mientras deja que las aristas de recursos humanos raramente consultadas se debiliten. El grafo desarrolla su propio sentido de relevancia con el tiempo.

Catorce modos de recuperación
Cognee incluye 14 modos de búsqueda. Los que realmente usarás:

Construyendo un agente real con memoria Cognee
Aquí está el patrón completo para conectar Cognee en el bucle percibir-pensar-actuar:
1import cognee2from cognee import SearchType34class CogneeMemoryAgent:5 """Agente con memoria híbrida grafo-vector persistente."""67 def __init__(self, session_id: str = "default"):8 self.llm_client = OpenAI()9 self.session_id = session_id1011 async def ingest(self, text: str, dataset: str = "main"):12 await cognee.add(text, dataset)13 await cognee.cognify([dataset])1415 async def recall(self, query: str) -> str:16 results = await cognee.search(17 query_text=query,18 query_type=SearchType.GRAPH_COMPLETION,19 session_id=self.session_id,20 )21 return results[0] if results else ""2223 async def chat(self, user_input: str) -> str:24 context = await self.recall(user_input)25 messages = [26 {"role": "system", "content": "Eres útil. Usa el contexto de la memoria."},27 {"role": "system", "content": f"Contexto de memoria:\n{context}"},28 {"role": "user", "content": user_input},29 ]30 response = self.llm_client.chat.completions.create(31 model="gpt-4o-mini", messages=messages32 )33 reply = response.choices[0].message.content34 await cognee.add(35 f"Usuario: {user_input}\nAsistente: {reply}",36 "conversations"37 )38 await cognee.cognify(["conversations"])39 return reply
El ciclo de memoria: ingerir, extraer, almacenar, recuperar, responder, almacenar de nuevo. Cada turno enriquece el grafo de conocimiento, y el procesamiento incremental significa que solo pagas para indexar contenido nuevo.
La memoria de sesión maneja la resolución de pronombres automáticamente:
1await cognee.search(query_text="¿Dónde vive Alice?", session_id="conv_1")2await cognee.search(query_text="¿A qué se dedica?", session_id="conv_1")3# "se" refiere a Alice desde el contexto de la sesión
La multiinquilinato está incorporada a nivel de grafo con permisos por conjunto de datos (lectura, escritura, eliminación, compartir). No es separación por espacios de nombres, sino aislamiento real a nivel de grafo.
El camino práctico a seguir
Si estás construyendo un agente hoy, la pregunta inicial real es: "¿qué necesita recordar mi agente, y qué tipo de preguntas va a responder?"
Si tus consultas solo necesitan búsqueda por similitud ("encuentra conversaciones como esta"), la memoria solo vectorial funciona. En cuanto las consultas cruzan límites de entidades ("¿El proyecto de Alice se vio afectado por la interrupción del martes?"), necesitas recorrido de grafos.
Puedes conectar almacenes vectoriales, de grafos y relacionales separados tú mismo. Los equipos que siguen esta ruta suelen quemar semanas en infraestructura para una capa de memoria que aún no aprende de su propio uso.
Cognee reduce eso a cuatro llamadas API. Los valores predeterminados embebidos te permiten funcionar en minutos. Los backends intercambiables (Postgres, Qdrant, Neo4j) te llevan a producción sin cambiar el código de tu agente.
La inteligencia requiere estructura, no solo almacenamiento. Los tres paradigmas de almacenamiento (relacional, vectorial, grafo) no son opciones en competencia. Son capas complementarias del mismo sistema de memoria. Tratarlos así es lo que convierte un envoltorio de LLM sin estado en algo que realmente aprende.
¿Qué es lo siguiente que te gustaría que tu agente recordara mañana que hoy olvidó? Empieza por ahí.
👉 Revisa Cognee en GitHub →, dale una estrella y prueba a integrarlo en tu próximo agente.
Cuatro llamadas asíncronas, un pip install, y ya estás funcionando.
¡Eso es todo!
Si disfrutaste leer esto:
Encuéntrame → @akshay_pachaar ✔️
Cada día comparto tutoriales e información sobre IA, Machine Learning y mejores prácticas de vibe coding.





