El Protocolo de Memoria

Por: Artiko
engrammcpmemoriaprotocolotopic-keyssesiones

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:

TipoCuándo usarlo
bugfixSolución a un bug, incluyendo causa raíz
architectureDecisiones de diseño o arquitectura del sistema
decisionCualquier decisión técnica con tradeoffs
patternPatrones de código recurrentes en el proyecto
discoveryComportamiento inesperado descubierto
configConfiguraciones críticas o no obvias
learningConocimiento nuevo adquirido
session_summaryResumen 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:

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:

  1. No crea un nuevo registro
  2. Incrementa duplicate_count en el registro existente
  3. 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):

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.