Módulo 11: Deep Agents

Long-term Memory y Pluggable Backends

Descripción de la cápsula

En el Módulo 8 construiste long-term memory desde cero: creaste un InMemoryStore, definiste namespaces, escribiste la lógica de put/get/search, y conectaste el store al grafo manualmente. Funcionó, pero cada nuevo agente requería repetir todo el setup.

Deep Agents incluye long-term memory como capacidad built-in. En lugar de construir la infraestructura, seleccionas un backend — filesystem, LangGraph Store, o un composite de ambos — y el agente se encarga del resto: decide qué recordar, cuándo recuperar memorias relevantes, y cómo aplicarlas a la tarea actual.

La diferencia no es qué se logra — el resultado es el mismo: un agente que recuerda entre sesiones. La diferencia es cuánto código escribes para lograrlo. En M8 eran ~80 líneas de setup + integración. Aquí son ~5 líneas de configuración.


Cómo funciona la memoria en Deep Agents

El ciclo de memoria

Cuando un Deep Agent tiene memory habilitada, ejecuta un ciclo automático en cada interacción:

1. RETRIEVE  → Al iniciar una tarea, busca memorias relevantes
2. EXECUTE   → Usa las memorias como contexto durante la ejecución
3. STORE     → Al terminar, guarda nuevos hechos, preferencias y patrones
Sesión 1:                              Sesión 2:
┌──────────────────────────┐           ┌──────────────────────────┐
│ Usuario: "Busca papers    │           │ Usuario: "Busca papers    │
│ sobre RAG, prefiero       │           │ sobre AI safety"          │
│ fuentes de arxiv"         │           │                           │
│                           │           │ RETRIEVE: "Este usuario    │
│ STORE: {                  │──────────→│ prefiere arxiv y formato  │
│   "source_pref": "arxiv", │  memoria  │ bullet_points"            │
│   "format": "bullets"     │           │                           │
│ }                         │           │ → Busca en arxiv primero  │
│                           │           │ → Formatea como bullets   │
└──────────────────────────┘           └──────────────────────────┘

No hay código que escribir para este ciclo. Ocurre porque habilitaste memory con un backend.


Backend 1: Filesystem — archivos locales

El backend más simple. Escribe memorias como archivos JSON en disco. Sin dependencias externas, sin base de datos, sin setup de infraestructura.

Configuración

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend

memory_backend = FilesystemMemoryBackend(
    base_path="./agent_memory",
    max_memories=500,
)

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación. Recuerda las preferencias del usuario.",
    memory=memory_backend,
)

result = agent.run("Prefiero que los reportes estén en español con formato bullet points.")
print(result.output)
# Output esperado (varía según el modelo):
# Entendido. He registrado tus preferencias: reportes en español con formato bullet points.
# Las aplicaré en futuras investigaciones.

Qué se guarda en disco

Después de esa ejecución, el directorio ./agent_memory contiene:

agent_memory/
├── index.json              ← Índice de todas las memorias
└── memories/
    └── pref_001.json       ← Una memoria individual
import json

with open("./agent_memory/memories/pref_001.json", "r") as f:
    memory = json.load(f)

print(json.dumps(memory, indent=2, ensure_ascii=False))
# Output esperado (estructura aproximada):
# {
#   "id": "pref_001",
#   "type": "preference",
#   "content": "El usuario prefiere reportes en español con formato bullet points",
#   "created_at": "2025-06-15T10:30:00Z",
#   "relevance_tags": ["format", "language", "reporting"],
#   "access_count": 0
# }

Recuperación en la siguiente sesión

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend

memory_backend = FilesystemMemoryBackend(
    base_path="./agent_memory",
    max_memories=500,
)

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación. Usa las memorias del usuario.",
    memory=memory_backend,
)

result = agent.run("Dame un resumen del estado de AI agents")
print(result.output)
# Output esperado (varía según el modelo):
# Aquí tienes el resumen en formato bullet points como prefieres:
#
# • Los AI agents han evolucionado de chatbots simples a sistemas autónomos...
# • Los frameworks principales incluyen LangGraph, CrewAI, AutoGen...
# • La tendencia es hacia agentes especializados coordinados por supervisors...

El agente no necesitó que le repitieras la preferencia. La recuperó automáticamente del backend filesystem.

Cuándo usar filesystem backend

  • ✅ Desarrollo local y prototipos rápidos
  • ✅ Agentes que corren en una sola máquina
  • ✅ No quieres configurar una base de datos
  • ✅ Necesitas inspeccionar las memorias manualmente (son JSON legibles)
  • ❌ Múltiples instancias del agente (sin locking)
  • ❌ Producción con alta concurrencia
  • ❌ Necesitas búsqueda semántica avanzada

Backend 2: LangGraph Store — familiar y escalable

Si ya usaste InMemoryStore o PostgresStore en el Módulo 8, este backend te resultará familiar. Usa la misma infraestructura de Store de LangGraph, pero Deep Agents lo maneja automáticamente.

Con InMemoryStore (desarrollo)

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import LangGraphStoreBackend
from langgraph.store.memory import InMemoryStore

store = InMemoryStore()

memory_backend = LangGraphStoreBackend(
    store=store,
    namespace_prefix=("agents", "research-assistant"),
)

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación.",
    memory=memory_backend,
)

result = agent.run("Siempre incluye secciones de metodología en mis reportes.")
print(result.output)
# Output esperado (varía según el modelo):
# Anotado. En todos tus reportes incluiré una sección de metodología.

Verificar que la memoria se guardó en el Store

from langgraph.store.memory import InMemoryStore

store = InMemoryStore()

from deep_agents.memory import LangGraphStoreBackend
backend = LangGraphStoreBackend(
    store=store,
    namespace_prefix=("agents", "research-assistant"),
)

backend.save({"type": "preference", "content": "Incluir metodología en reportes"})

items = store.search(("agents", "research-assistant", "memories"))
for item in items:
    print(f"Key: {item.key}, Value: {item.value}")
# Output esperado:
# Key: mem_001, Value: {'type': 'preference', 'content': 'Incluir metodología en reportes', ...}

Los datos viven en el mismo Store que ya conoces. Los namespaces siguen la misma lógica jerárquica del M8.

Con PostgresStore (producción)

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import LangGraphStoreBackend
from langgraph.store.postgres import PostgresStore

store = PostgresStore(
    conn_string="postgresql://user:pass@localhost:5432/agents_db",
)

memory_backend = LangGraphStoreBackend(
    store=store,
    namespace_prefix=("agents", "research-assistant"),
)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación.",
    memory=memory_backend,
)

print(f"Backend: {type(store).__name__}")
print(f"Namespace prefix: {memory_backend.namespace_prefix}")
# Output esperado:
# Backend: PostgresStore
# Namespace prefix: ('agents', 'research-assistant')

El cambio de InMemoryStore a PostgresStore es una línea. La API es idéntica. Eso es lo que hace al Store backend potente para producción.

Cuándo usar LangGraph Store backend

  • ✅ Ya usas LangGraph Store en tu stack
  • ✅ Necesitas persistencia real (PostgresStore)
  • ✅ Múltiples agentes comparten el mismo Store
  • ✅ Quieres consistencia con el ecosistema LangGraph
  • ❌ No quieres instalar PostgreSQL solo para memoria
  • ❌ Necesitas inspección manual rápida (archivos son más legibles que DB rows)

Backend 3: Composite — lo mejor de ambos mundos

El composite backend combina múltiples backends. El caso más común: filesystem para acceso rápido local + PostgresStore para durabilidad y sincronización.

Configuración

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import (
    CompositeMemoryBackend,
    FilesystemMemoryBackend,
    LangGraphStoreBackend,
)
from langgraph.store.postgres import PostgresStore

filesystem = FilesystemMemoryBackend(
    base_path="./agent_memory",
    max_memories=500,
)

pg_store = PostgresStore(
    conn_string="postgresql://user:pass@localhost:5432/agents_db",
)
langgraph_store = LangGraphStoreBackend(
    store=pg_store,
    namespace_prefix=("agents", "research-assistant"),
)

memory_backend = CompositeMemoryBackend(
    backends=[filesystem, langgraph_store],
    read_strategy="first_available",
    write_strategy="all",
)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación.",
    memory=memory_backend,
)

print(f"Read strategy: {memory_backend.read_strategy}")
print(f"Write strategy: {memory_backend.write_strategy}")
print(f"Backends: {len(memory_backend.backends)}")
# Output esperado:
# Read strategy: first_available
# Write strategy: all
# Backends: 2

Estrategias de lectura y escritura

Read strategies:
┌────────────────────┬──────────────────────────────────────────┐
│ first_available    │ Lee del primer backend que tenga el dato │
│                    │ (filesystem primero = más rápido)        │
├────────────────────┼──────────────────────────────────────────┤
│ merge_all          │ Lee de todos los backends y combina      │
│                    │ (más completo, más lento)                │
└────────────────────┴──────────────────────────────────────────┘

Write strategies:
┌────────────────────┬──────────────────────────────────────────┐
│ all                │ Escribe en todos los backends            │
│                    │ (durabilidad máxima)                     │
├────────────────────┼──────────────────────────────────────────┤
│ primary_only       │ Escribe solo en el primer backend        │
│                    │ (más rápido, synca manualmente)          │
└────────────────────┴──────────────────────────────────────────┘

Cuándo usar composite backend

  • ✅ Desarrollo local que necesita sincronizar con producción
  • ✅ Quieres redundancia (si un backend falla, el otro tiene los datos)
  • ✅ Performance local (filesystem) + durabilidad remota (Postgres)
  • ❌ Agentes simples que no justifican la complejidad
  • ❌ Prototipo rápido donde filesystem solo es suficiente

Cómo el agente decide qué recordar

Deep Agents no guarda todo. Tiene criterios internos para determinar qué vale la pena persistir.

Categorías de memorias

┌─────────────────────┬────────────────────────────────┬───────────────────────┐
│ Categoría           │ Ejemplo                         │ Prioridad de guardado │
├─────────────────────┼────────────────────────────────┼───────────────────────┤
│ Preferencias        │ "Prefiero fuentes académicas"  │ Alta                  │
│ explícitas          │ "Formatea como bullet points"  │                       │
├─────────────────────┼────────────────────────────────┼───────────────────────┤
│ Patrones            │ Siempre pide verificar datos   │ Media                 │
│ observados          │ Suele investigar temas de AI   │                       │
├─────────────────────┼────────────────────────────────┼───────────────────────┤
│ Hechos              │ "Trabaja en fintech"           │ Media                 │
│ del usuario         │ "Su proyecto se llama Atlas"   │                       │
├─────────────────────┼────────────────────────────────┼───────────────────────┤
│ Resultados          │ "Ya investigamos RAG en mayo"  │ Baja                  │
│ de tareas previas   │ "El reporte sobre X está listo"│                       │
└─────────────────────┴────────────────────────────────┴───────────────────────┘

Lo que NO se guarda

  • ❌ Datos efímeros de una sola interacción ("busca X" → resultado → no se guarda el resultado completo)
  • ❌ Información que el agente puede obtener con tools (no memoriza APIs públicas)
  • ❌ Contexto de ejecución (qué nodo ejecutó, cuántos tokens usó)

Controlar qué se recuerda

Puedes influir en el comportamiento de memoria vía instructions:

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(base_path="./memory")

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions=(
        "Eres un asistente de investigación. "
        "SIEMPRE recuerda: preferencias de formato, fuentes favoritas, "
        "y temas de interés del usuario. "
        "NUNCA recuerdes: datos personales sensibles, contraseñas, "
        "o información financiera específica."
    ),
    memory=memory,
)

result = agent.run("Me llamo Carlos, trabajo en fintech, y mi API key es sk-abc123.")
print(result.output)
# Output esperado (varía según el modelo):
# Hola Carlos. He registrado que trabajas en fintech.
# Por seguridad, no almaceno API keys ni credenciales.

Las instrucciones guían al modelo sobre qué memorizar. No son una garantía criptográfica — son una guía para el LLM.


Comparación: M8 manual vs Deep Agents memory

Este es el contraste central de esta cápsula. En M8 construiste todo paso a paso. Aquí configuras un backend.

M8: Long-term memory manual (~80 líneas de setup)

from dotenv import load_dotenv
load_dotenv()

from langgraph.store.memory import InMemoryStore
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from typing import TypedDict, Annotated
import operator

store = InMemoryStore()
checkpointer = MemorySaver()

class State(TypedDict):
    messages: Annotated[list, operator.add]
    user_id: str

def load_memories(state, *, store):
    user_id = state["user_id"]
    items = store.search(("users", user_id, "preferences"))
    memories = [f"{item.key}: {item.value['value']}" for item in items]
    system_msg = f"Preferencias del usuario: {', '.join(memories)}" if memories else ""
    return {"messages": [{"role": "system", "content": system_msg}]} if system_msg else {}

def save_memories(state, *, store):
    user_id = state["user_id"]
    last_msg = state["messages"][-1]
    if "prefiero" in last_msg.get("content", "").lower():
        content = last_msg["content"]
        store.put(
            ("users", user_id, "preferences"),
            f"pref_{len(store.search(('users', user_id, 'preferences')))}",
            {"value": content}
        )
    return {}

def respond(state):
    model = ChatOpenAI(model="gpt-4.1-mini")
    response = model.invoke(state["messages"])
    return {"messages": [{"role": "assistant", "content": response.content}]}

builder = StateGraph(State)
builder.add_node("load_memories", load_memories)
builder.add_node("respond", respond)
builder.add_node("save_memories", save_memories)
builder.add_edge(START, "load_memories")
builder.add_edge("load_memories", "respond")
builder.add_edge("respond", "save_memories")
builder.add_edge("save_memories", END)

graph = builder.compile(checkpointer=checkpointer, store=store)

result = graph.invoke(
    {"messages": [{"role": "user", "content": "Prefiero fuentes de arxiv"}], "user_id": "mike"},
    config={"configurable": {"thread_id": "session-1"}}
)
print(f"Nodos en el grafo: 3")
print(f"Líneas de código: ~45 (sin contar imports)")
print(f"Control sobre qué se guarda: total")
# Output esperado:
# Nodos en el grafo: 3
# Líneas de código: ~45 (sin contar imports)
# Control sobre qué se guarda: total

Tú decidiste: cuándo cargar memorias, qué guardar, cómo detectar preferencias, dónde ponerlas en el Store.

Deep Agents: memory configurada (~5 líneas de setup)

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación. Recuerda preferencias del usuario.",
    memory=FilesystemMemoryBackend(base_path="./memory"),
)

result = agent.run("Prefiero fuentes de arxiv")
print(f"Líneas de configuración de memoria: 1")
print(f"Control sobre qué se guarda: delegado al framework")
# Output esperado:
# Líneas de configuración de memoria: 1
# Control sobre qué se guarda: delegado al framework

Tabla comparativa

AspectoM8 ManualDeep Agents Memory
Líneas de código~45-80 para setup completo~5 (configuración del backend)
ControlTotal — tú defines qué, cuándo, dóndeParcial — el framework decide, tú guías con instructions
Detección de preferenciasLógica manual (if "prefiero" in msg)Automática (el LLM decide)
OrganizaciónTú diseñas namespacesEl framework organiza por categoría
BackendEliges e integras manualmenteSeleccionas, el framework integra
DebuggingPuedes inspeccionar cada pasoInspeccionas el backend, no el proceso
Ideal paraProducción con requisitos específicosPrototipos, tareas autónomas

La regla: si necesitas control sobre exactamente qué se recuerda y cómo se estructura, usa M8. Si quieres que "simplemente funcione," usa Deep Agents memory.


Memory management: capacidad y olvido

Límites de capacidad

from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(
    base_path="./memory",
    max_memories=500,
    max_memory_age_days=90,
)

print(f"Máximo de memorias: {memory.max_memories}")
print(f"Edad máxima: {memory.max_memory_age_days} días")
# Output esperado:
# Máximo de memorias: 500
# Edad máxima: 90 días

Cuando se alcanza max_memories, el backend aplica una política de eviction:

Política de eviction (por defecto: LRU + relevancia):

1. Calcula score = frecuencia_de_uso × recencia × relevancia
2. Las memorias con score más bajo se eliminan primero
3. Las preferencias explícitas tienen score boost (se mantienen más tiempo)

Ejemplo:
  "Prefiere arxiv"         → score alto (preferencia + usada 15 veces)
  "Investigó RAG en mayo"  → score medio (referenciada 3 veces)
  "Pidió un resumen corto" → score bajo (mencionada 1 vez, hace 80 días)
  → Se elimina "Pidió un resumen corto" primero

Limpiar memorias manualmente

from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(base_path="./memory")

all_memories = memory.list_all()
print(f"Memorias antes: {len(all_memories)}")

memory.forget("pref_001")
print(f"Memorias después de forget: {len(memory.list_all())}")

memory.clear_all()
print(f"Memorias después de clear: {len(memory.list_all())}")
# Output esperado:
# Memorias antes: 3
# Memorias después de forget: 2
# Memorias después de clear: 0

Cuándo limpiar memorias

  • ✅ El usuario solicita "olvida mis preferencias"
  • ✅ Cambias de proyecto y las memorias anteriores no aplican
  • ✅ Testing: empezar cada test con memoria limpia
  • ❌ Limpiar automáticamente entre sesiones (eso elimina el propósito de long-term memory)

Selección de backend: árbol de decisión

¿Es un prototipo o desarrollo local?
  └─ SÍ → FilesystemMemoryBackend
  └─ NO ↓

¿Ya usas LangGraph Store en tu stack?
  └─ SÍ → LangGraphStoreBackend (con tu store existente)
  └─ NO ↓

¿Necesitas persistencia real (sobrevive reinicios)?
  └─ SÍ → LangGraphStoreBackend + PostgresStore
  └─ NO → FilesystemMemoryBackend

¿Necesitas acceso rápido local + durabilidad remota?
  └─ SÍ → CompositeMemoryBackend (filesystem + Postgres)
  └─ NO → Un solo backend es suficiente

Resumen rápido

BackendSetupPersistenciaEscalabilidadIdeal para
Filesystem1 líneaDisco localUn agenteDesarrollo, prototipos
LangGraph Store (InMemory)3 líneasEn memoria (se pierde)TestingTests, demos
LangGraph Store (Postgres)3 líneasBase de datosMulti-agenteProducción
Composite5-8 líneasMúltipleConfigurableDev + Prod, redundancia

Troubleshooting

1. El agente no recuerda nada entre sesiones

Causa: Estás usando InMemoryStore como backend. Se pierde cuando el proceso termina.

Solución: Usa FilesystemMemoryBackend o LangGraphStoreBackend con PostgresStore:

from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(base_path="./memory")

2. Las memorias se guardan pero no se recuperan

Causa: Las instructions del agente no mencionan que debe usar memorias previas.

Solución: Sé explícito en las instrucciones:

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions=(
        "Eres un asistente de investigación. "
        "SIEMPRE consulta tus memorias antes de responder. "
        "Aplica las preferencias del usuario que tengas registradas."
    ),
    memory=memory_backend,
)

3. El directorio de memoria crece sin control

Causa: No configuraste max_memories ni max_memory_age_days.

Solución: Establece límites explícitos:

memory = FilesystemMemoryBackend(
    base_path="./memory",
    max_memories=200,
    max_memory_age_days=60,
)

4. El composite backend es lento en lectura

Causa: Usas read_strategy="merge_all", que consulta todos los backends.

Solución: Cambia a "first_available" si no necesitas merging:

composite = CompositeMemoryBackend(
    backends=[filesystem, langgraph_store],
    read_strategy="first_available",
    write_strategy="all",
)

5. El agente guarda información sensible

Causa: No instruiste al agente sobre qué NO recordar.

Solución: Agrega restricciones explícitas en instructions:

instructions=(
    "NUNCA recuerdes: contraseñas, API keys, tokens, "
    "números de tarjeta, o información médica."
)

Ejercicios

Ejercicio 1: Filesystem backend básico

Crea un agente con FilesystemMemoryBackend. En la primera ejecución, dile "Prefiero reportes en formato tabla con máximo 3 fuentes." Termina el script. Corre un segundo script con el mismo backend path y pide "Dame un reporte sobre cloud computing." Verifica que aplica tus preferencias.

Ver solución
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(base_path="./memory_ex1")

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación. Recuerda preferencias del usuario.",
    memory=memory,
)

result = agent.run("Prefiero reportes en formato tabla con máximo 3 fuentes.")
print("Sesión 1:", result.output)

all_mems = memory.list_all()
print(f"Memorias guardadas: {len(all_mems)}")
for m in all_mems:
    print(f"  - {m['type']}: {m['content'][:60]}...")
# Output esperado:
# Sesión 1: Entendido. Tus preferencias han sido registradas...
# Memorias guardadas: 1
#   - preference: El usuario prefiere reportes en formato tabla con máxim...

Segundo script:

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(base_path="./memory_ex1")

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Eres un asistente de investigación. Aplica las preferencias que recuerdes.",
    memory=memory,
)

result = agent.run("Dame un reporte sobre cloud computing.")
print("Sesión 2:", result.output)
# Output esperado (varía según el modelo):
# Sesión 2: Aquí tienes tu reporte en formato tabla (máximo 3 fuentes):
# | Aspecto | Detalle | Fuente |
# |---------|---------|--------|
# ...

Ejercicio 2: LangGraph Store backend con namespaces

Crea un agente que use LangGraphStoreBackend con InMemoryStore. Configura el namespace prefix como ("proyecto", "mi-app"). Guarda 3 preferencias diferentes. Luego, usa store.search() directamente para verificar que las memorias están en el namespace correcto.

Ver solución
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import LangGraphStoreBackend
from langgraph.store.memory import InMemoryStore

store = InMemoryStore()

backend = LangGraphStoreBackend(
    store=store,
    namespace_prefix=("proyecto", "mi-app"),
)

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Eres un asistente. Recuerda cada preferencia que te diga el usuario.",
    memory=backend,
)

preferences = [
    "Prefiero código en Python con type hints",
    "Siempre incluye tests unitarios en tus sugerencias",
    "Usa docstrings en formato Google style",
]

for pref in preferences:
    result = agent.run(pref)
    print(f"Guardado: {pref[:50]}...")

items = store.search(("proyecto", "mi-app", "memories"))
print(f"\nMemories en store: {len(items)}")
for item in items:
    print(f"  Namespace: {item.namespace}")
    print(f"  Key: {item.key}")
    print(f"  Content: {item.value.get('content', 'N/A')[:60]}...")
# Output esperado:
# Guardado: Prefiero código en Python con type hints...
# Guardado: Siempre incluye tests unitarios en tus sugerencias...
# Guardado: Usa docstrings en formato Google style...
#
# Memories en store: 3
#   Namespace: ('proyecto', 'mi-app', 'memories')
#   Key: mem_001
#   Content: El usuario prefiere código en Python con type hints...
#   ...

Ejercicio 3: Composite backend con estrategias

Implementa un CompositeMemoryBackend con filesystem y un InMemoryStore. Usa write_strategy="all" y read_strategy="first_available". Guarda una preferencia y verifica que existe en ambos backends. Luego, elimina la memoria del filesystem y verifica que el agente aún la recupera del Store.

Ver solución
from dotenv import load_dotenv
load_dotenv()

from deep_agents.memory import (
    CompositeMemoryBackend,
    FilesystemMemoryBackend,
    LangGraphStoreBackend,
)
from langgraph.store.memory import InMemoryStore

filesystem = FilesystemMemoryBackend(base_path="./memory_ex3")
store = InMemoryStore()
langgraph_backend = LangGraphStoreBackend(
    store=store,
    namespace_prefix=("test", "composite"),
)

composite = CompositeMemoryBackend(
    backends=[filesystem, langgraph_backend],
    read_strategy="first_available",
    write_strategy="all",
)

composite.save({"type": "preference", "content": "Prefiere formato markdown"})

fs_memories = filesystem.list_all()
store_memories = store.search(("test", "composite", "memories"))
print(f"Filesystem: {len(fs_memories)} memorias")
print(f"Store: {len(store_memories)} memorias")

filesystem.clear_all()
print(f"\nDespués de limpiar filesystem:")
print(f"Filesystem: {len(filesystem.list_all())} memorias")

retrieved = composite.retrieve("formato")
print(f"Composite aún recupera: {retrieved[0]['content'][:50]}...")
# Output esperado:
# Filesystem: 1 memorias
# Store: 1 memorias
#
# Después de limpiar filesystem:
# Filesystem: 0 memorias
# Composite aún recupera: Prefiere formato markdown...

Ejercicio 4: Control de qué se recuerda

Crea un agente con instrucciones explícitas sobre qué recordar y qué no. Envía 5 mensajes: 2 con preferencias válidas, 1 con datos sensibles (API key ficticia), 1 con una instrucción operativa, y 1 conversacional. Verifica cuántas memorias se guardaron y que no hay datos sensibles.

Ver solución
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(base_path="./memory_ex4")

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions=(
        "Eres un asistente. "
        "RECUERDA: preferencias de formato, temas de interés, herramientas preferidas. "
        "NUNCA recuerdes: API keys, contraseñas, tokens, datos financieros."
    ),
    memory=memory,
)

messages = [
    "Prefiero que uses Python 3.12 con asyncio",
    "Me interesan mucho los temas de MLOps",
    "Mi API key de OpenAI es sk-fake123456789",
    "Busca información sobre Docker best practices",
    "Gracias, buen trabajo",
]

for msg in messages:
    result = agent.run(msg)
    print(f"Enviado: {msg[:50]}...")

all_mems = memory.list_all()
print(f"\nMemorias guardadas: {len(all_mems)}")
for m in all_mems:
    content = m.get("content", "")
    has_sensitive = "sk-" in content or "api key" in content.lower()
    print(f"  - [{m['type']}] {content[:60]}... {'⚠️ SENSIBLE' if has_sensitive else '✅ OK'}")
# Output esperado:
# Enviado: Prefiero que uses Python 3.12 con asyncio...
# Enviado: Me interesan mucho los temas de MLOps...
# Enviado: Mi API key de OpenAI es sk-fake123456789...
# Enviado: Busca información sobre Docker best practices...
# Enviado: Gracias, buen trabajo...
#
# Memorias guardadas: 2
#   - [preference] El usuario prefiere Python 3.12 con asyncio... ✅ OK
#   - [interest] El usuario está interesado en temas de MLOps... ✅ OK

Ejercicio 5: Memory con límites y eviction

Crea un FilesystemMemoryBackend con max_memories=3. Guarda 5 memorias secuencialmente. Verifica que solo quedan 3 y que las más relevantes sobrevivieron.

Ver solución
from deep_agents.memory import FilesystemMemoryBackend

memory = FilesystemMemoryBackend(
    base_path="./memory_ex5",
    max_memories=3,
)

memories_to_save = [
    {"type": "preference", "content": "Prefiere Python"},
    {"type": "observation", "content": "Preguntó sobre el clima una vez"},
    {"type": "preference", "content": "Quiere formato tabla"},
    {"type": "observation", "content": "Dijo hola"},
    {"type": "preference", "content": "Siempre incluir fuentes académicas"},
]

for m in memories_to_save:
    memory.save(m)
    print(f"Guardada: {m['content'][:40]}... (total: {len(memory.list_all())})")

final = memory.list_all()
print(f"\nMemorias finales: {len(final)}")
for m in final:
    print(f"  - [{m['type']}] {m['content']}")
# Output esperado:
# Guardada: Prefiere Python... (total: 1)
# Guardada: Preguntó sobre el clima una vez... (total: 2)
# Guardada: Quiere formato tabla... (total: 3)
# Guardada: Dijo hola... (total: 3)
# Guardada: Siempre incluir fuentes académicas... (total: 3)
#
# Memorias finales: 3
#   - [preference] Prefiere Python
#   - [preference] Quiere formato tabla
#   - [preference] Siempre incluir fuentes académicas

Las preferencias explícitas tienen mayor score que las observaciones genéricas, así que sobreviven la eviction.

Ejercicio 6: Migrar de M8 manual a Deep Agents memory (Avanzado)

Tienes un InMemoryStore con datos del M8 (3 preferencias en namespace ("users", "mike", "preferences")). Crea un LangGraphStoreBackend que apunte al mismo store y namespace. Verifica que el agente Deep Agents puede leer las memorias que guardaste manualmente en M8.

Ver solución
from dotenv import load_dotenv
load_dotenv()

from langgraph.store.memory import InMemoryStore

store = InMemoryStore()
store.put(("users", "mike", "preferences"), "sources", {"value": "academic", "content": "Prefiere fuentes académicas"})
store.put(("users", "mike", "preferences"), "format", {"value": "bullet_points", "content": "Prefiere bullet points"})
store.put(("users", "mike", "preferences"), "language", {"value": "es", "content": "Prefiere contenido en español"})

items = store.search(("users", "mike", "preferences"))
print(f"Memorias M8 existentes: {len(items)}")
for item in items:
    print(f"  {item.key}: {item.value}")

from deep_agents.memory import LangGraphStoreBackend

backend = LangGraphStoreBackend(
    store=store,
    namespace_prefix=("users", "mike"),
    legacy_namespace=("users", "mike", "preferences"),
)

memories = backend.retrieve("preferencias del usuario")
print(f"\nMemorias recuperadas por Deep Agents: {len(memories)}")
for m in memories:
    print(f"  - {m.get('content', m.get('value', 'N/A'))}")

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="assistant",
    instructions="Aplica las preferencias que recuerdes del usuario.",
    memory=backend,
)

result = agent.run("¿Qué preferencias tienes registradas sobre mí?")
print(f"\nRespuesta: {result.output}")
# Output esperado:
# Memorias M8 existentes: 3
#   sources: {'value': 'academic', 'content': 'Prefiere fuentes académicas'}
#   format: {'value': 'bullet_points', 'content': 'Prefiere bullet points'}
#   language: {'value': 'es', 'content': 'Prefiere contenido en español'}
#
# Memorias recuperadas por Deep Agents: 3
#   - Prefiere fuentes académicas
#   - Prefiere bullet points
#   - Prefiere contenido en español
#
# Respuesta: Tengo registradas las siguientes preferencias:
# • Fuentes académicas como prioridad
# • Formato bullet points
# • Contenido en español

El punto clave: si usas el mismo store, la transición de M8 manual a Deep Agents memory es compatible.


Resumen

  • Deep Agents incluye long-term memory como capacidad built-in — seleccionas un backend, el agente decide qué recordar, cuándo recuperar, y cómo aplicar memorias automáticamente
  • Tres backends disponibles: Filesystem (simple, archivos JSON locales), LangGraph Store (familiar si vienes de M8, escalable con PostgresStore), y Composite (combina múltiples backends para redundancia)
  • El agente categoriza memorias en preferencias explícitas (alta prioridad), patrones observados (media), hechos del usuario (media), y resultados previos (baja). Puedes influir con instructions pero no controlar al 100%
  • Comparación con M8: en M8 diseñaste la lógica de memoria completa (~80 líneas). En Deep Agents, configuras un backend (~5 líneas). El trade-off: M8 te da control total, Deep Agents te da conveniencia con control parcial
  • Memory management incluye límites (max_memories, max_memory_age_days), eviction automática por relevancia, y cleanup manual (forget, clear_all)
  • Árbol de decisión: filesystem para desarrollo, LangGraph Store con PostgresStore para producción, composite cuando necesitas ambos

Próxima cápsula: Deep Agents CLI — cómo ejecutar agentes autónomos directamente desde la terminal. Es una herramienta de productividad, no un demo.


Recursos adicionales

  1. Deep Agents — Memory Configuration — Documentación oficial de memory backends y configuración
  2. LangGraph Memory Store — Documentación de InMemoryStore y PostgresStore
  3. LangGraph Cross-thread Persistence — Memoria entre sesiones en LangGraph nativo
  4. PostgresStore Guide — Setup de PostgresStore para producción
  5. Cognitive Architectures for Language Agents — Paper sobre memoria en agentes: tipos, organización, y retrieval

Módulo 11 — LangChain & LangGraph: From Chains to Agents