El Protocolo de Memoria
El agente decide, Engram persiste
El protocolo de memoria de Engram parte de un principio claro: el agente es quien decide qué vale la pena recordar. No hay captura automática de todo el historial de conversación, no hay hooks que intercepten cada herramienta llamada, no hay logs masivos que indexar después.
Esta decisión es intencional. Un sistema que guarda todo produce una base de datos ruidosa donde la búsqueda se vuelve difícil. Engram confía en que el agente, al completar trabajo significativo, sabrá que ese resultado merece persistirse.
El flujo mental del protocolo es:
flowchart TD
A[Agente completa trabajo] --> B{¿Es significativo?}
B -->|Bugfix, decisión, patrón| C[mem_save con formato What/Why/Where/Learned]
B -->|Operación rutinaria| D[No guardar]
C --> E[Engram indexa en SQLite + FTS5]
E --> F[Disponible para búsqueda futura]
La herramienta central: mem_save
mem_save es la herramienta más importante. Su firma conceptual es:
mem_save(
title: string, # Título conciso y buscable
type: string, # Categoría de la memoria
content: string, # Contenido en formato What/Why/Where/Learned
scope?: string, # "project" (default) o "personal"
topic_key?: string # Clave para upsert en lugar de insert
)
El formato What/Why/Where/Learned
El contenido de una memoria bien estructurada sigue este patrón:
What: Qué se hizo o descubrió
Why: Por qué fue necesario o importante
Where: En qué archivos o módulos aplica
Learned: Qué se aprendió para el futuro
Ejemplo real — bugfix:
What: La API de pagos de Stripe tiene un retry delay mínimo de 3 segundos entre intentos fallidos.
Why: Nuestro cliente hacía reintentos con backoff de 500ms, causando errores 429 en producción
bajo carga alta.
Where: src/payments/stripe-client.ts, línea 145. Configuración en config/stripe.yaml.
Learned: Siempre revisar los límites de rate del proveedor antes de implementar retry logic.
Stripe documenta esto en su guía de error handling, sección "Rate limiting".
Ejemplo real — decisión de arquitectura:
What: Elegimos Event Sourcing para el módulo de inventario en lugar de CRUD tradicional.
Why: Los auditores necesitan un historial completo e inmutable de cada movimiento de stock.
Event Sourcing nos da esto de forma natural sin tablas de auditoría adicionales.
Where: src/inventory/, módulo completo. Integra con el event bus en src/events/.
Learned: Event Sourcing agrega complejidad de consultas. Para queries de lectura,
usamos proyecciones materializadas (patrón CQRS) en src/projections/.
Tipos de observaciones (type)
Engram usa los tipos como categorías de búsqueda. Los tipos disponibles incluyen:
| Tipo | Cuándo usarlo |
|---|---|
bugfix | Solución a un bug, incluyendo causa raíz |
architecture | Decisiones de diseño o arquitectura del sistema |
decision | Cualquier decisión técnica con tradeoffs |
pattern | Patrones de código recurrentes en el proyecto |
discovery | Comportamiento inesperado descubierto |
config | Configuraciones críticas o no obvias |
learning | Conocimiento nuevo adquirido |
session_summary | Resumen de fin de sesión (generado por mem_session_summary) |
El tipo es indexado junto al texto completo, lo que permite búsquedas como mem_search "auth" type=bugfix para encontrar solo bugs relacionados con autenticación.
Topic keys: memorias que evolucionan
Los topic keys son la característica más poderosa de Engram para gestionar conocimiento que cambia con el tiempo.
Sin topic key, cada llamada a mem_save crea una nueva observación independiente. Esto funciona bien para bugfixes (cada bug es único), pero es problemático para documentación de arquitectura: si guardas la decisión de arquitectura en enero y luego la actualizas en marzo, tendrías dos registros contradictorios.
Con topic key, el segundo mem_save con la misma clave actualiza la observación existente en lugar de crear una nueva. Esto convierte a Engram en un sistema de documentación viva.
Flujo con topic keys
1. Llama a mem_suggest_topic_key para obtener una clave normalizada
2. Guarda la observación con esa clave
3. Cuando el tema evoluciona, usa la misma clave → upsert automático
Paso 1 — sugerir una clave:
mem_suggest_topic_key(type="architecture", title="Auth middleware design")
# Respuesta: "architecture/auth-middleware-design"
mem_suggest_topic_key aplica una heurística de familia:
architecture/*— para decisiones de diseño y ADRsbug/*— para fixes, regresiones y erroresdecision/*,pattern/*,config/*,discovery/*,learning/*— según contenido
Paso 2 — guardar con la clave:
mem_save(
title="Auth middleware: JWT validation strategy",
type="architecture",
content="What: Usamos JWT con RS256 y validación en middleware...",
topic_key="architecture/auth-middleware-design"
)
Paso 3 — actualizar cuando evoluciona:
Tres meses después, cambias la estrategia de JWT a session tokens:
mem_save(
title="Auth middleware: migración a session tokens",
type="architecture",
content="What: Migramos de JWT RS256 a session tokens HTTP-only...",
topic_key="architecture/auth-middleware-design" ← misma clave
)
Resultado: la observación original se actualiza con revision_count++. No hay dos registros contradictorios. La historia de cambios queda implícita en revision_count y updated_at.
Deduplicación automática
Engram tiene un sistema de deduplicación basado en hash que previene observaciones duplicadas en una ventana temporal. Si el agente llama mem_save dos veces con el mismo contenido, título, tipo y proyecto, la segunda llamada:
- No crea un nuevo registro
- Incrementa
duplicate_counten el registro existente - Actualiza
last_seen_at
Esto es especialmente útil cuando el agente tiene patrones de “guardar al iniciar sesión y encontrar que ya existe”. El sistema no falla ni produce duplicados, simplemente registra que la memoria sigue siendo relevante.
El ciclo de sesión
Las sesiones son otro concepto clave. Una sesión representa un período de trabajo continuo con el agente. El protocolo de sesión tiene tres fases:
sequenceDiagram
participant A as Agente
participant E as Engram
A->>E: mem_session_start (inicio de conversación)
Note over A,E: Trabajo en progreso...
A->>E: mem_save (memorias de trabajo)
Note over A,E: Fin del trabajo...
A->>E: mem_session_summary (resumen final)
A->>E: mem_session_end (cierra la sesión)
mem_session_start
Se llama al inicio de cada conversación. Registra que una nueva sesión comenzó para el proyecto actual. El ID de sesión generado se usa internamente para asociar las memorias guardadas durante esa sesión.
mem_session_summary
Se llama al finalizar el trabajo. Acepta un resumen estructurado con cuatro secciones:
Goal: Qué se intentaba lograr
Discoveries: Qué se encontró en el camino (bugs, comportamientos inesperados, etc.)
Accomplished: Qué se completó
Files: Archivos principales modificados
Este resumen es lo que mem_context recupera al inicio de la siguiente sesión, dando al agente una visión del estado del proyecto sin necesidad de releer todo el historial de memorias.
mem_session_end
Marca la sesión como completada. Es una operación administrativa que mantiene limpio el registro de sesiones activas.
Alcance de las memorias: project vs personal
Cada memoria tiene un scope (alcance):
project(default) — La memoria aplica al proyecto actual. Visible cuando el servidor MCP detecta ese proyecto.personal— Memorias personales que aplican en cualquier proyecto. Útil para preferencias de coding, convenciones personales, etc.
mem_save(
title="Prefiero async/await sobre Promises encadenadas",
type="learning",
content="...",
scope="personal"
)
Las memorias personales se recuperan en mem_context independientemente del proyecto actual.
Actualizar y eliminar memorias
Actualizar una memoria existente con su ID:
mem_update(
id=42,
content="Contenido actualizado...",
title="Nuevo título"
)
Eliminar una memoria:
mem_delete(id=42) # soft delete (marcada como eliminada, no borrada)
mem_delete(id=42, hard=true) # hard delete (eliminada permanentemente)
El soft delete es el comportamiento por defecto. Las memorias soft-deleted no aparecen en búsquedas ni en mem_context, pero pueden recuperarse con una llamada directa a la API HTTP si es necesario.
Captura pasiva: extraer memorias de texto
mem_capture_passive es una herramienta especial que extrae observaciones estructuradas a partir de texto libre (por ejemplo, la salida de un test o los logs de un error):
mem_capture_passive(
content="ERROR: Connection pool exhausted after 100 concurrent requests.
Pool size: 10. Queue timeout: 500ms.",
session_id="session-abc123"
)
Engram analiza el texto y extrae automáticamente learnings relevantes en el formato estándar. Es útil cuando el agente acaba de leer output verboso y quiere capturar lo importante sin reformatearlo manualmente.
La herramienta mem_judge: resolver conflictos
mem_judge es la herramienta para cuando tienes memorias que podrían estar en conflicto. Dada una lista de IDs de memorias, evalúa cuáles son contradictorias, cuáles son complementarias, y cuáles están desactualizadas.
Es especialmente útil cuando un topic evoluciona y quieres asegurarte de que no hay memorias huérfanas con información antigua que contradigan la actual.
Resumen del protocolo de memoria completo
flowchart LR
S[Inicio de sesión] --> SC[mem_session_start]
SC --> CP[mem_current_project]
CP --> CTX[mem_context]
CTX --> Work[Trabajo en progreso]
Work --> Save[mem_save por cada cosa significativa]
Save --> Work
Work --> End[Fin de sesión]
End --> SS[mem_session_summary]
SS --> SE[mem_session_end]
Este protocolo, cuando lo ejecuta el agente consistentemente, construye una base de conocimiento del proyecto que se enriquece con cada sesión. En el próximo capítulo veremos cómo recuperar ese conocimiento eficientemente con las herramientas de búsqueda.