# Gestión de Memoria en LangChain (v1+) y Despliegue con FastAPI

**Fecha de consulta:** 14 de agosto de 2025
**Fuente principal:** Documentación oficial de LangChain (docs.langchain.com)

---

## TL;DR

LangChain v1+ (vía LangGraph) maneja la memoria en dos niveles: **memoria a corto plazo** (thread-level, persistida con checkpointers usando `thread_id` como clave primaria) y **memoria a largo plazo** (cross-session, almacenada como documentos JSON en un store organizado por namespace/key). La persistencia no se logra con texto plano ni session_id genérico de LangChain por sí solo — requiere un **checkpointer** (MemorySaver, SqliteSaver, AsyncPostgresSaver) o un **store** (InMemoryStore, PostgresStore). Para Deep Agents el sistema es análogo pero con API propia. La mejor estrategia en producción es usar PostgreSQL (via checkpointer) para corto plazo y PostgreSQL (via BaseStore) para largo plazo.

---

## 1. Introducción y Contexto

Los agentes conversacionales construidos con LangChain enfrentan un desafío fundamental: los modelos de lenguaje grandes (LLM) no poseen memoria inherente. Cada invocación es independiente a menos que el historial de conversación se almacene y reinyecte explícitamente en el contexto del modelo. LangChain, como framework de orquestación, proporciona mecanismos para gestionar esta memoria en múltiples niveles, pero la implementación concreta depende del framework base que se utilice: **LangChain (Agents clásicos)**, **LangGraph (grafos de estado)** o **Deep Agents (agentes profundos de alto nivel)**.

La documentación oficial de LangChain distingue dos tipos fundamentales de memoria (LangChain, 2025a):

- **Memoria a corto plazo (short-term):** Almacenada como parte del estado del agente (típicamente en el campo `messages`). Persiste a nivel de hilo (thread) mediante un **checkpointer**. Cuando el agente finaliza, se guarda un checkpoint del grafo que permite reanudar la conversación.
- **Memoria a largo plazo (long-term):** Almacenada en un **store** externo como documentos JSON organizados por namespace y clave. Cruza sesiones y puede ser recuperada en cualquier momento, incluso en hilos de conversación diferentes.

Adicionalmente, LangChain reconoce otros subtipos de memoria como la **memoria semántica** (perfil del usuario), **memoria episódica** (eventos pasados), y **memoria procedimental** (cómo hacer tareas) (LangChain, 2025b).

---

## 2. Metodología de Investigación

Esta investigación se realizó mediante:

1. **Análisis de documentación oficial:** Revisión exhaustiva de los sitios oficiales de LangChain (docs.langchain.com) para las secciones de memoria en LangChain, LangGraph y Deep Agents.
2. **Búsqueda de tutoriales y ejemplos:** Consulta de artículos técnicos, blogs y foros especializados.
3. **Validación cruzada:** Contraste entre fuentes oficiales y comunitarias para confirmar patrones de implementación.

**Limitaciones detectadas:**

- Algunas implementaciones específicas de Deep Agents para memoria persistente se encuentran en etapa de desarrollo activo (documentación marcada como PREVIEW).
- No se encontraron fuentes independientes que comparen exhaustivamente texto plano vs. Redis vs. base de datos SQL para el caso concreto de LangChain v1+.
- El buscador web presentó bloqueos parciales (HTTP 502) en algunas consultas, por lo que ciertos datos provienen exclusivamente de documentación oficial.

---

## 3. Resultados: Arquitectura de Memoria en LangChain v1+

### 3.1 Memoria a Corto Plazo (Short-term Memory)

La memoria a corto plazo en LangChain v1+ se gestiona a través del **estado del agente (AgentState)**, específicamente mediante el campo `messages` que almacena el historial completo de la conversación. Este estado se persiste utilizando un **checkpointer** cuando se compila el grafo (LangChain, 2025c).

#### Mecanismo de funcionamiento

1. El agente mantiene un estado (`AgentState`) que incluye el historial de mensajes.
2. Al compilar el grafo con un checkpointer, cada "super-paso" (super-step) genera un checkpoint automático.
3. El `thread_id` actúa como clave primaria para almacenar y recuperar checkpoints.
4. Al invocar el grafo con un `thread_id` existente, el checkpoint recupera automáticamente el estado previo.

#### Código de ejemplo — Implementación básica

```python
from langgraph.graph import StateGraph
from langgraph.checkpoint import MemorySaver
from typing import TypedDict, List
from langchain_core.messages import AnyMessage

class AgentState(TypedDict):
    messages: List[AnyMessage]
    next: str

# Crear checkpointer en memoria
checkpointer = MemorySaver()

# Compilar grafo con checkpointer
graph = StateGraph(AgentState)
# ... definir nodos y aristas ...
app = graph.compile(checkpointer=checkpointer)

# Invocar con thread_id (session_id)
result = app.invoke(
    {"messages": [HumanMessage(content="Hola")]},
    config={"configurable": {"thread_id": "sesion-usuario-123"}}
)

# La siguiente invocación con el mismo thread_id
# recupera automáticamente el historial
result2 = app.invoke(
    {"messages": [HumanMessage(content="¿Recuerdas lo que dije?")]},
    config={"configurable": {"thread_id": "sesion-usuario-123"}}
)
```

**Tipos de checkpointers disponibles:**

| Checkpointer | Persistencia | Uso recomendado |
|---|---|---|
| `MemorySaver` | En memoria (RAM) | Desarrollo local, prototipado |
| `SqliteSaver` | Archivo SQLite local | Single-server, pruebas |
| `AsyncPostgresSaver` | PostgreSQL | Producción multi-servidor |
| `RedisSaver` (terceros) | Redis | Alta velocidad, sesiones temporales |

#### Gestión de sesiones con thread_id

LangChain v1+ **no** proporciona un mecanismo nativo de "session_id" como string plano en el agente clásico. En LangGraph, el equivalente es `thread_id`, que se pasa como parte de la configuración del grafo. La documentación oficial establece (LangChain, 2025d):

> "The checkpointer uses `thread_id` as the primary key for storing and retrieving checkpoints. Without it, the checkpointer cannot save state or resume execution."

Esto implica que **no es posible conservar la memoria como texto plano con un session_id a largo plazo usando solo LangChain sin almacenamiento externo**. Para persistencia real se requiere:

1. **Un checkpointer** (SQLite o PostgreSQL) para memoria a corto plazo (conversación dentro de un hilo).
2. **Un store** para memoria a largo plazo (información que cruza hilos de conversación).

### 3.2 Memoria a Largo Plazo (Long-term Memory)

La memoria a largo plazo en LangGraph se construye sobre **stores** que guardan datos como documentos JSON organizados por namespace y clave (LangChain, 2025e).

#### Estructura del Store

```
Store
├── namespace_1 (ej: "user_preferences")
│   ├── key_1 (ej: "user_123")
│   │   └── {JSON document con preferencias}
│   └── key_2 (ej: "user_456")
│       └── {JSON document}
└── namespace_2 (ej: "conversation_summaries")
    └── key_1 (ej: "session_789")
        └── {JSON document con resumen}
```

#### Implementación con BaseStore

```python
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore

# Store en memoria (desarrollo)
store = InMemoryStore()

# Guardar memoria a largo plazo
store.put(
    ("user_preferences", "user_123"),
    "theme",
    {"value": "dark", "language": "es"}
)

# Recuperar memoria
memory = store.get(("user_preferences", "user_123"), "theme")
# -> {"value": "dark", "language": "es"}
```

**Para producción**, LangChain recomienda usar `PostgresStore` en lugar de `InMemoryStore`. Las implementaciones de BaseStore disponibles incluyen:

| Store | Persistencia | Ideal para |
|---|---|---|
| `InMemoryStore` | RAM | Desarrollo y pruebas |
| `PostgresStore` | PostgreSQL | Producción, datos persistentes |
| Store personalizado | Cualquier backend | Casos de uso específicos |

#### Acceso al Store desde los nodos del agente

```python
def my_node(state: AgentState, config: RunnableConfig, *, store: BaseStore):
    # Leer memoria del usuario
    user_id = config["configurable"].get("user_id")
    memory = store.get(("user_memories", user_id), "preferences")
    
    # Escribir nueva memoria
    store.put(
        ("user_memories", user_id),
        "last_topic",
        {"topic": state["messages"][-1].content}
    )
    
    return {"messages": state["messages"]}
```

### 3.3 Deep Agents y Memoria

Deep Agents es la capa de agentes de alto nivel de LangChain, diseñada para abstraer la complejidad de LangGraph. La documentación oficial (LangChain, 2025f) indica que Deep Agents soporta memoria persistente mediante un sistema similar al de LangGraph.

```python
from deep_agents import Agent

agent = Agent(
    name="asistente",
    instructions="Eres un asistente útil.",
    model="gpt-4o",
    memory=True  # Habilita memoria persistente
)
```

Según la documentación (LangChain, 2025g), Deep Agents:

- Almacenan memoria de forma persistente entre conversaciones.
- Aprenden y mejoran a través de interacciones.
- Soportan tanto memoria a corto plazo (dentro de una conversación) como a largo plazo (entre conversaciones).
- Se integran con LangGraph stores para persistencia real.

Sin embargo, la documentación de Deep Agents está marcada como "PREVIEW" para el despliegue administrado (Managed Deep Agents), lo que indica que algunas funcionalidades aún están en desarrollo activo.

### 3.4 Deep Agents vs. LangChain Agents

| Aspecto | LangChain Agent (clásico) | LangGraph Agent | Deep Agent |
|---|---|---|---|
| Memoria corto plazo | ConversationBufferMemory / ventana / resumen | Estado del grafo (messages) + checkpointer | Estado interno + checkpointer |
| Memoria largo plazo | No nativa | Store (BaseStore) | Store integrado |
| Session ID | No nativo (requiere chat_memory personalizado) | thread_id en config | Administrado internamente |
| Persistencia externa | Custom (RedisChatMessageHistory, etc.) | Checkpointer + Store nativos | Integración con Store |
| Complejidad | Media | Alta (más control) | Baja (más abstracción) |

---

## 4. Estrategias de Persistencia Recomendadas

### 4.1 Comparativa de Opciones de Almacenamiento

| Criterio | Texto Plano | Redis | Base de Datos SQL |
|---|---|---|---|
| Velocidad de lectura | Alta (archivo local) | Muy alta (RAM) | Media-alta (con índices) |
| Velocidad de escritura | Media | Muy alta | Media |
| Persistencia | Depende del sistema de archivos | Configurable (RDB/AOF) | Garantizada (ACID) |
| Escalabilidad | No | Horizontal (cluster) | Horizontal (replicación) |
| Consultas complejas | No | Limitado | Sí (SQL) |
| Integración con LangChain | No nativa | Parcial (RedisChatMessageHistory legacy) | Nativa (checkpointers + stores) |
| Madurez en producción | Baja | Media-alta | Alta |
| Costo de infraestructura | Ninguno | Medio | Medio-alto |

### 4.2 Recomendación: PostgreSQL como opción principal

Para aplicaciones en producción que requieren conservar memoria a largo plazo con session ID tanto en Deep Agents como en LangGraph Agents, **PostgreSQL es la opción más sólida** porque:

1. **Cubre ambos niveles de memoria:** El `AsyncPostgresSaver` maneja la memoria a corto plazo (checkpoints por thread_id) y el `PostgresStore` maneja la memoria a largo plazo.
2. **Una sola infraestructura:** Se evita mantener múltiples sistemas de almacenamiento.
3. **Transacciones ACID:** Garantiza consistencia en escrituras concurrentes.
4. **Madurez probada:** Es la opción recomendada por la documentación oficial de LangChain para producción.

### 4.3 Redis como alternativa de alta velocidad

Redis es adecuado cuando:

- Se requiere latencia mínima en recuperación de contexto.
- Las sesiones tienen TTL (time-to-live) y no necesitan persistencia indefinida.
- Ya existe infraestructura Redis en el ecosistema.

Para integración con LangGraph, se puede implementar un checkpointer personalizado o usar `AioRedis` como backend del store.

---

## 5. Arquitectura de Flujo Recomendada

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#E8EAF6','primaryTextColor': '#1A237E','primaryBorderColor': '#3949AB','lineColor': '#546E7A','secondaryColor': '#E3F2FD','background': '#FAFAFA'}}}%%
flowchart TD
    classDef api fill:#E3F2FD,stroke:#1565C0,stroke-width:2px,color:#0D47A1
    classDef agent fill:#E8EAF6,stroke:#3949AB,stroke-width:1.5px,color:#1A237E
    classDef storage fill:#F3E5F5,stroke:#6A1B9A,stroke-width:1px,color:#4A148C
    classDef external fill:#FFF3E0,stroke:#E65100,stroke-width:1px,color:#BF360C,stroke-dasharray:4 2

    CLIENT([Cliente / Frontend]):::external
    API[FastAPI Server\n/api/chat]:::api
    AUTH{Extraer session_id\ny user_id}:::api
    
    subgraph LangGraph["Capa LangGraph"]
        AGENT[Agente con Estado\nStateGraph]:::agent
        MEM_SHORT[Memoria Corto Plazo\nmessages + checkpointer]:::agent
        MEM_LONG[Memoria Largo Plazo\nStore namespace/user]:::agent
    end
    
    subgraph Storage["Persistencia"]
        PG_CHECK[(PostgreSQL\nAsyncPostgresSaver\ncheckpoints por thread_id)]:::storage
        PG_STORE[(PostgreSQL\nPostgresStore\nmemorias por usuario)]:::storage
    end
    
    LLM[LLM\nOpenAI / Anthropic]:::external

    CLIENT -->|POST /api/chat\n{session_id, message}| API
    API --> AUTH
    AUTH -->|thread_id = session_id| AGENT
    AGENT -->|checkpoint cada super-step| MEM_SHORT
    AGENT -->|leer/escribir memorias| MEM_LONG
    MEM_SHORT --> PG_CHECK
    MEM_LONG --> PG_STORE
    AGENT -->|invocación LLM| LLM
    AGENT -->|respuesta| API
    API -->|{response, session_id}| CLIENT
```

### Flujo paso a paso

1. **El cliente envía** un mensaje con `session_id` (generado en el primer mensaje).
2. **FastAPI** recibe la solicitud y extrae `session_id` como `thread_id`.
3. **LangGraph** invoca el agente con `config={"configurable": {"thread_id": session_id}}`.
4. **El checkpointer** recupera el estado previo (historial de conversación) desde PostgreSQL.
5. **El agente ejecuta** los nodos del grafo, incluyendo la lectura de memoria a largo plazo desde el store.
6. **Cada super-step** genera un checkpoint automático en PostgreSQL.
7. **La respuesta** se devuelve al cliente junto con el mismo `session_id`.

---

## 6. Despliegue con FastAPI

### 6.1 Arquitectura del Servidor

Para desplegar un agente con memoria persistente desde un servidor en la nube usando FastAPI:

```
Servidor Cloud (VPS / VM)
├── FastAPI (Python)
│   ├── POST /api/chat      → invocar agente
│   ├── POST /api/session   → crear nueva sesión
│   ├── GET /api/session/{id} → historial de sesión
│   └── DELETE /api/session/{id} → limpiar sesión
├── LangGraph Runtime
│   ├── StateGraph compilado con checkpointer
│   └── Store para memoria a largo plazo
├── PostgreSQL
│   ├── Tabla de checkpoints (AsyncPostgresSaver)
│   └── Tabla de store (PostgresStore)
└── Proxy (Nginx/Caddy) → SSL + dominio
```

### 6.2 Implementación Base con FastAPI

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from uuid import uuid4
from langgraph.checkpoint.postgres import AsyncPostgresSaver
from langgraph.store.postgres import PostgresStore
from langgraph.graph import StateGraph
import asyncpg

app = FastAPI(title="Agente con Memoria Persistente")

# Modelos de datos
class ChatRequest(BaseModel):
    message: str
    session_id: str | None = None

class ChatResponse(BaseModel):
    response: str
    session_id: str

# Configuración de persistencia
DB_URI = "postgresql://user:pass@localhost:5432/langchain"

@app.on_event("startup")
async def startup():
    """Inicializar conexiones a PostgreSQL"""
    conn = await asyncpg.connect(DB_URI)
    checkpointer = AsyncPostgresSaver(conn)
    await checkpointer.setup()
    
    store = PostgresStore(conn)
    await store.setup()
    
    app.state.graph = create_agent_graph().compile(
        checkpointer=checkpointer,
        store=store
    )

@app.post("/api/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
    session_id = request.session_id or str(uuid4())
    
    config = {
        "configurable": {
            "thread_id": session_id,
            "user_id": session_id
        }
    }
    
    result = await app.state.graph.ainvoke(
        {"messages": [HumanMessage(content=request.message)]},
        config=config
    )
    
    return ChatResponse(
        response=result["messages"][-1].content,
        session_id=session_id
    )

@app.post("/api/session")
async def create_session():
    session_id = str(uuid4())
    return {"session_id": session_id}

@app.get("/api/session/{session_id}")
async def get_session(session_id: str):
    config = {"configurable": {"thread_id": session_id}}
    state = await app.state.graph.aget_state(config)
    return {
        "session_id": session_id,
        "messages": [m.dict() for m in state.values.get("messages", [])]
    }
```

### 6.3 Pasos para Despliegue en Servidor Cloud

```bash
# 1. Requisitos del servidor (Ubuntu/Debian)
sudo apt update && sudo apt install -y python3-pip postgresql nginx

# 2. Clonar proyecto e instalar dependencias
git clone https://github.com/tu-usuario/agent-chat-api.git
cd agent-chat-api
pip install -r requirements.txt

# 3. Configurar PostgreSQL
sudo -u postgres createdb langchain
sudo -u postgres psql -c "CREATE USER app WITH PASSWORD 'secreto';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE langchain TO app;"

# 4. Ejecutar con Uvicorn
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

# 5. Configurar Nginx como proxy reverso
# server {
#     server_name agent.tudominio.com;
#     location / {
#         proxy_pass http://127.0.0.1:8000;
#     }
# }
```

---

## 7. Discusión e Interpretación

### 7.1 ¿Texto plano + session_id como estrategia viable?

**No es recomendable.** Aunque técnicamente es posible serializar el historial de conversación como JSON y guardarlo en un archivo con nombre basado en session_id, esta aproximación presenta múltiples limitaciones:

- **Sin integración nativa:** LangChain v1+ no ofrece una abstracción para cargar/guardar automáticamente desde un archivo de texto.
- **Problemas de concurrencia:** Dos solicitudes simultáneas sobre el mismo archivo pueden corromper los datos.
- **Escalabilidad limitada:** Los archivos no se replican ni distribuyen.
- **Sin checkpointing automático:** Se perdería la capacidad de reanudar desde cualquier punto de la ejecución.

### 7.2 LangGraph como base para agentes en producción

La investigación confirma que **LangGraph es la capa fundamental** para implementar agentes con memoria persistente en LangChain v1+. Mientras que los agentes clásicos de LangChain requieren integraciones externas (RedisChatMessageHistory, SQLChatMessageHistory) que son menos eficientes, LangGraph ofrece persistencia nativa a través de checkpointers y stores.

### 7.3 Deep Agents: potencial y limitaciones actuales

Deep Agents representa la evolución de LangChain hacia una experiencia más simplificada, pero su ecosistema de persistencia aún no está completamente maduro. La documentación de memoria para Deep Agents es menos detallada que la de LangGraph, y las funcionalidades de despliegue están marcadas como "PREVIEW". Para proyectos que requieren control granular sobre la memoria, LangGraph sigue siendo la opción más sólida.

### 7.4 Limitaciones de la investigación

- La documentación oficial de LangChain cambia rápidamente; algunos detalles pueden haber quedado desactualizados.
- No se encontraron benchmarks independientes que comparen el rendimiento de diferentes checkpointers.
- Deep Agents tiene documentación limitada sobre memoria personalizada.

---

## 8. Conclusiones y Recomendaciones

### 8.1 Para implementar memoria persistente con session ID

| Estrategia | Recomendación |
|---|---|
| **Corto plazo** | Usar `AsyncPostgresSaver` con `thread_id` como session_id |
| **Largo plazo** | Usar `PostgresStore` con namespace por usuario |
| **Alta velocidad** | Redis con checkpointer personalizado (sesiones temporales) |
| **Desarrollo local** | `MemorySaver` + `InMemoryStore` |
| **Deep Agents** | Usar `memory=True` + store de PostgreSQL |
| **Texto plano** | ❌ No recomendado |

### 8.2 Para desplegar en servidor cloud

1. **Usar FastAPI** como capa REST que expone endpoints de chat.
2. **PostgreSQL** como backend único para checkpoints y store.
3. **thread_id = session_id** para recuperar conversaciones.
4. **Nginx** como proxy reverso para TLS y dominio personalizado.
5. **Docker Compose** para replicabilidad del entorno.

### 8.3 Próximos pasos

1. Migrar de `MemorySaver` a `AsyncPostgresSaver` para entornos de staging/producción.
2. Implementar limpieza automática de sesiones inactivas (TTL).
3. Agregar autenticación a los endpoints de FastAPI.
4. Monitorear tokens consumidos y costo por sesión.
5. Evaluar Deep Agents cuando su ecosistema de despliegue madure.

---

## 9. Referencias

LangChain. (2025a). *Memory overview*. LangChain Documentation. https://docs.langchain.com/oss/python/concepts/memory

LangChain. (2025b). *Short-term memory*. LangChain Documentation. https://docs.langchain.com/oss/python/langchain/short-term-memory

LangChain. (2025c). *Persistence*. LangChain Documentation. https://docs.langchain.com/oss/python/langgraph/persistence

LangChain. (2025d). *Add memory (LangGraph)*. LangChain Documentation. https://docs.langchain.com/oss/python/langgraph/add-memory

LangChain. (2025e). *Long-term memory*. LangChain Documentation. https://docs.langchain.com/oss/python/langchain/long-term-memory

LangChain. (2025f). *Deep Agents: Memory*. LangChain Documentation. https://docs.langchain.com/oss/python/deepagents/memory

LangChain. (2025g). *Deep Agents: Quickstart*. LangChain Documentation. https://docs.langchain.com/oss/python/deepagents/quickstart

LangChain. (2025h). *Application structure*. LangChain Documentation. https://docs.langchain.com/oss/python/langgraph/application-structure

LangChain. (2025i). *LangSmith Deployment*. LangChain Documentation. https://docs.langchain.com/oss/python/langgraph/deploy

Nhi Yen. (2024, 25 de enero). *Enhance Conversational Agents with LangChain Memory*. Comet Blog. https://www.comet.com/site/blog/enhance-conversational-agents-with-langchain-memory/

---

## 10. Anexos

### Anexo A: Ejemplo completo de agente LangGraph con memoria PostgreSQL

```python
import asyncio
from typing import TypedDict, List
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.postgres import AsyncPostgresSaver
from langgraph.store.postgres import PostgresStore
from langchain_openai import ChatOpenAI
from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage
from langchain_core.runnables import RunnableConfig
import asyncpg

class AgentState(TypedDict):
    messages: List[AnyMessage]
    next: str

def create_agent():
    llm = ChatOpenAI(model="gpt-4o", temperature=0)
    
    def chat_node(state: AgentState, config: RunnableConfig, *, store):
        user_id = config["configurable"].get("user_id")
        memory = store.get(("user_data", user_id), "preferences")
        
        messages = state["messages"]
        if memory:
            messages = [
                SystemMessage(
                    content=f"Contexto del usuario: {memory.value}"
                )
            ] + messages
        
        response = llm.invoke(messages)
        
        store.put(
            ("user_data", user_id),
            "last_interaction",
            {"summary": response.content[:200]}
        )
        
        return {"messages": messages + [response], "next": END}
    
    graph = StateGraph(AgentState)
    graph.add_node("chat", chat_node)
    graph.set_entry_point("chat")
    graph.add_conditional_edges("chat", lambda s: s["next"])
    
    return graph

async def main():
    conn = await asyncpg.connect(
        "postgresql://user:pass@localhost:5432/langchain"
    )
    
    checkpointer = AsyncPostgresSaver(conn)
    await checkpointer.setup()
    
    store = PostgresStore(conn)
    await store.setup()
    
    app = create_agent().compile(
        checkpointer=checkpointer,
        store=store
    )
    
    # Primera conversación
    result = await app.ainvoke(
        {"messages": [HumanMessage(content="Hola, me llamo Juan")]},
        config={"configurable": {"thread_id": "sesion-1", "user_id": "user-juan"}}
    )
    print(result["messages"][-1].content)
    
    # Segunda conversación (recuerda el nombre gracias al store)
    result = await app.ainvoke(
        {"messages": [HumanMessage(content="¿Cómo me llamo?")]},
        config={"configurable": {"thread_id": "sesion-2", "user_id": "user-juan"}}
    )
    print(result["messages"][-1].content)

asyncio.run(main())
```

### Anexo B: Esquema de base de datos PostgreSQL

```sql
-- Tablas para AsyncPostgresSaver (checkpoints de memoria corto plazo)
CREATE TABLE IF NOT EXISTS checkpoints (
    thread_id TEXT NOT NULL,
    checkpoint_ns TEXT NOT NULL DEFAULT '',
    checkpoint_id TEXT NOT NULL,
    parent_checkpoint_id TEXT,
    type TEXT,
    checkpoint JSONB NOT NULL,
    metadata JSONB DEFAULT '{}'::jsonb,
    PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
);

CREATE TABLE IF NOT EXISTS checkpoint_writes (
    thread_id TEXT NOT NULL,
    checkpoint_ns TEXT NOT NULL DEFAULT '',
    checkpoint_id TEXT NOT NULL,
    task_id TEXT NOT NULL,
    idx INTEGER NOT NULL,
    channel TEXT NOT NULL,
    type TEXT,
    value JSONB NOT NULL,
    PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
);

-- Tabla para PostgresStore (memoria a largo plazo)
CREATE TABLE IF NOT EXISTS store (
    namespace TEXT NOT NULL,
    key TEXT NOT NULL,
    value JSONB NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (namespace, key)
);

CREATE INDEX idx_store_namespace ON store(namespace);
CREATE INDEX idx_store_key ON store(key);
```

---

*Documento generado el 14 de agosto de 2025. Basado en documentación oficial de LangChain (docs.langchain.com) y fuentes complementarias. Verificar contra la documentación más reciente antes de implementar en producción.*