Inicio / Artículos / Cinco estrategias de memoria en LangChain, del buffer a la memoria de entidades

Cinco estrategias de memoria en LangChain, del buffer a la memoria de entidades

Por qué los modelos de chat lo olvidan todo entre llamadas y cinco patrones de memoria para LangChain y LangGraph: buffer, ventana, resumen, buffer con resumen y entidades, con sus pros y contras.

1667 palabras

Mucha gente da por hecho que un modelo de chat recuerda lo que le dijiste. No es así. Llama a GPT-5, GPT-4o, Claude o Gemini y el resultado es el mismo: cada petición empieza de cero y, salvo que vuelvas a enviar los turnos anteriores, el modelo no tiene ni idea de que existieron. Esta es la demostración más pequeña posible:

from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")llm.invoke("Hi, my name is Asha.")
llm.invoke("What's my name?")

La segunda llamada no sabe nada de la primera. El nombre de Asha nunca se envió, así que el modelo no puede decirlo.

Qué significa la memoria en LangChain

No tiene nada de mágico. La memoria es una política para una sola pregunta: ¿qué partes de la conversación anterior se envían de vuelta con la siguiente petición? Tu respuesta determina lo inteligente, escalable y barata que resulta la aplicación. A continuación, cinco políticas que se usan con LangChain y LangGraph, en qué es buena y mala cada una y dónde encaja.

La tensión de fondo

Supongamos que una usuaria se presenta como Asha, recibe una respuesta amable y más tarde pregunta cómo se llama. Para responder bien, ese primer mensaje tiene que viajar en el nuevo prompt.

Los problemas empiezan cuando los chats crecen a 10, 100 o 1.000 mensajes. Reenviarlo todo cada vez dispara el uso de tokens, la factura de la API, la latencia y la presión sobre la ventana de contexto. Cada elección cambia una cosa por otra:

  • Recuerdas más y sube el coste.
  • Recuerdas menos y baja el coste, pero se resiente la precisión.
  • Envías más contexto y las respuestas se ralentizan.
  • Envías menos contexto y las respuestas se aceleran, pero pierden información.

Estrategia 1: memoria de buffer

La memoria de buffer guarda la transcripción completa y la envía entera en cada llamada. Imagina una app de chat que nunca borra nada. El código es corto:

from langchain_core.messages import HumanMessage
history = []def chat(user_input):
    history.append(HumanMessage(user_input))    response = llm.invoke(history)    history.append(response)    return response.content

Una conversación de ejemplo:

User: I live in Chennai.
Assistant: That's great.

Más adelante:

User: Which city do I live in?
Assistant: Chennai

El modelo acierta con Chennai porque la línea anterior sigue en el historial que recibe.

Lo bueno: el recuerdo es perfecto, es trivial de montar y depurar resulta agradable porque lo ves todo. Lo malo: los tokens crecen sin límite, los costes suben, las respuestas se ralentizan con el tiempo y al final chocas con el techo del contexto. Va bien para sesiones cortas, herramientas internas, entornos de desarrollo y pruebas.

Estrategia 2: memoria de ventana

Aquí solo guardas los últimos K mensajes. Todo lo anterior se borra sin más, un poco como una pizarra en la que cada nota nueva borra la más antigua.

from langchain_core.messages import trim_messages
trimmed = trim_messages(
    history,
    strategy="last",
    max_tokens=10,
    token_counter=len
)

Prueba este intercambio:

User: My name is Asha.
Assistant: Nice to meet you.
User: Message 1
User: Message 2
User: Message 3
User: Message 4User: What's my name?

Si el mensaje con el nombre ya ha salido de la ventana, el modelo tiene que darse por vencido:

Assistant: I don't know your name.

Ese dato se ha perdido para siempre. A cambio, el uso de tokens es predecible, las respuestas son rápidas y la factura se mantiene baja. La pega es que la información antigua desaparece, así que las conversaciones largas no funcionan. Piensa en bots de FAQ, chatbots informales, asistentes de atención al cliente y cualquier app de bajo presupuesto.

Estrategia 3: memoria de resumen

Supón que no quieres acumularlo todo, pero tampoco olvidarlo todo. Con la memoria de resumen, el modelo escribe de vez en cuando un resumen compacto de los turnos antiguos, y ese resumen sustituye al texto original.

Las personas lo hacemos de forma natural. Tras una reunión de dos horas no recuerdas cada frase. Recuerdas que se habló del presupuesto, se aprobó la propuesta y se fijó una revisión del avance para la semana siguiente.

Después de un chat largo, el estado guardado podría decir:

Summary:
User is building a shipment tracking application.
The discussion focused on Azure deployment and performance optimization.

En lugar de enviar 100 mensajes, envías eso. Una implementación aproximada empieza con un resumen acumulado vacío:

running_summary = ""

Después, cuando el historial supera un umbral, los mensajes antiguos se condensan:

if len(history) > 6:
    old_messages = history[:-2]    running_summary = llm.invoke(
        f"Summarize this conversation:\n{old_messages}"
    ).content    history[:] = history[-2:]

Puntos fuertes: el crecimiento de tokens se aplana, las conversaciones largas funcionan y los costes bajan. Puntos débiles: se pierde la redacción exacta, pueden caerse detalles importantes y el resultado es tan bueno como el modelo que escribe el resumen. Encaja en sesiones de soporte largas, asistentes virtuales, apps de coaching y chats de varias horas.

Estrategia 4: buffer con resumen

Si abres el código de un chatbot real en producción, lo normal es encontrar alguna versión de esta. Combina las dos anteriores: los mensajes recientes se mantienen palabra por palabra y los antiguos se resumen. Obtienes un contexto reciente nítido y conversaciones largas asequibles.

Un buen asistente de dirección trabaja igual. Recuerda con exactitud lo que le pediste ayer:

You asked for the deployment status yesterday.

Y lleva el contexto de fondo en forma comprimida:

This project started in January and focuses on Azure migration.

Los detalles frescos siguen nítidos y los viejos se convierten en conocimiento condensado. Una versión mínima fija un límite de mensajes recientes que conservar:

RECENT_LIMIT = 4
running_summary = ""def chat(user_input):
    global running_summary    history.append(HumanMessage(user_input))    if len(history) > RECENT_LIMIT:        older_messages = history[:-RECENT_LIMIT]        running_summary = llm.invoke(
            f"""
            Update this summary:
            {running_summary}            Using:
            {older_messages}
            """
        ).content        history[:] = history[-RECENT_LIMIT:]    context = [
        SystemMessage(
            content=f"Summary: {running_summary}"
        )
    ] + history    response = llm.invoke(context)    history.append(response)    return response.content

Puntos fuertes: buen equilibrio entre coste y precisión, soporte para chats largos, detalles recientes preservados y buen comportamiento en producción. Puntos débiles: algo más de complejidad, además de llamadas periódicas para resumir. Usos típicos: copilotos, chatbots empresariales, plataformas de soporte y asistentes de nivel producción.

Estrategia 5: memoria de entidades

A veces lo que hay que guardar no es la transcripción, sino los hechos. Piensa en afirmaciones como estas:

User: My sister's birthday is in March.
User: I work at Contoso.
User: My manager is Priya.

Meses después sigues queriendo esos datos. La memoria de entidades mantiene registros estructurados sobre las personas, lugares, proyectos y empresas que aparecen, de modo que el chat original puede desaparecer y lo útil se queda. Un almacén de entidades podría contener:

{
    "Priya": [
        "User's manager"
    ],
    "Contoso": [
        "User works here"
    ]
}

La extracción empieza con un almacén vacío:

entity_store = {}

y una función le pide al modelo que saque entidades y hechos:

def extract_entities(text):    prompt = f"""
    Extract entities and facts:
    {text}
    """    return llm.invoke(prompt).content

Puntos fuertes: ideal para la memoria a largo plazo, muy barata en tokens y con datos estructurados. Puntos débiles: la arquitectura es más pesada, necesitas lógica de extracción y no puede reproducir una conversación exacta. Funciona para asistentes personales, sistemas CRM, ayudantes para empleados y herramientas de gestión de relaciones.

La forma LangGraph

Los tutoriales antiguos usan clases que te sonarán:

ConversationBufferMemory
ConversationSummaryMemory
ConversationBufferWindowMemory

Ahora son patrones heredados, y el trabajo actual con LangChain se apoya en LangGraph. El estilo antiguo creaba un objeto de memoria:

memory = ConversationBufferMemory()

El estilo nuevo usa un checkpointer como

MemorySaver()

o

PostgresSaver()

para guardar el estado de la conversación. Recortar, resumir y procesar ese estado queda en tus manos. Un grafo se configura empezando por estos imports:

from langgraph.graph import (
    StateGraph,
    MessagesState,
    START
)

y el montaje completo, recorte incluido, queda así:

from langgraph.checkpoint.memory import (
    MemorySaver
)from langchain_core.messages import (
    trim_messages
)def call_model(state: MessagesState):    trimmed = trim_messages(
        state["messages"],
        strategy="last",
        max_tokens=10,
        token_counter=len
    )    response = llm.invoke(trimmed)    return {
        "messages": [response]
    }graph = StateGraph(MessagesState)graph.add_node("model", call_model)graph.add_edge(START, "model")app = graph.compile(
    checkpointer=MemorySaver()
)

Hay un punto que confunde a mucha gente: el checkpointer no decide qué recordar. Solo decide dónde vive la memoria, ya sea en el proceso, en una base de datos o en otro almacén. Elegir entre buffer, ventana, resumen, buffer con resumen o entidades sigue siendo cosa tuya.

¿Puede Azure Blob Storage guardar la memoria?

Sí. Puede guardar historiales de chat, resúmenes, datos de entidades y checkpoints de sesión. Pero para la memoria de conversación en tiempo real suele encajar mal: las lecturas son más lentas que con Redis, no está pensado para lecturas y escrituras frecuentes y necesitarías lógica de indexación adicional. Una arquitectura empresarial habitual se parece más a esto:

Redis
    ↓
PostgreSQL
    ↓
Azure Blob Storage

En ese esquema, Redis guarda las conversaciones activas, PostgreSQL el historial de sesiones y Blob Storage archiva los datos a largo plazo.

Cómo elegir estrategia

Ajusta la política al producto. Los chats breves o internos pueden usar memoria de buffer. Los bots baratos e informales pueden tirar de ventana. Las sesiones largas en las que la redacción importa poco van bien con resúmenes. Todo lo que trate con clientes y dure mucho suele pedir buffer con resumen, y todo lo que deba recordar datos duraderos sobre personas u organizaciones pide memoria de entidades.

Para terminar

El diseño de la memoria es una de las decisiones más importantes en un producto de IA. Un bot sin memoria parece olvidadizo, y uno con memoria ilimitada se vuelve caro y lento. El objetivo no es recordarlo todo, sino recordar lo adecuado.

Para la mayoría de las apps en producción, hoy el mejor equilibrio entre escala, coste y experiencia de usuario sale de combinar buffer con resumen, memoria de entidades y checkpointing de LangGraph. Si construyes asistentes con LangChain o LangGraph, acertar con la memoria mejorará la calidad y el rendimiento más que casi cualquier truco de prompt. Los sistemas más inteligentes no son los que lo recuerdan todo, sino los que saben qué conservar.