Si trabajas con modelos de lenguaje y necesitas mantener el hilo de una conversación larga sin saturar el prompt, ConversationSummaryMemory es la pieza que te conviene conocer. Este tipo de memoria de LangChain genera un resumen continuo de todas las interacciones entre humano e IA, en lugar de guardar cada mensaje por separado, lo que la vuelve ideal para chats extensos con modelos como GPT-3.5 Turbo.
¿Cómo funciona ConversationSummaryMemory frente a otras memorias?
A diferencia de guardar cada turno literal, aquí la memoria comprime el historial en una narración corta que se reinyecta al prompt en cada nueva interacción.
¿Qué es ConversationSummaryMemory? Es un tipo de memoria de LangChain que usa un modelo de lenguaje para crear un resumen progresivo del chat, en vez de almacenar mensaje por mensaje.
La diferencia con ConversationBufferMemory es directa: el buffer guarda todo tal cual, mientras que el summary lo sintetiza. Por eso, si solo vas a tener una o dos interacciones, el resumen puede salir más largo que la conversación misma y no compensa. Cuando la charla se alarga, el resumen sí se vuelve eficiente [01:55].
¿Qué necesitas importar para empezar?
La configuración parte de tres importaciones desde LangChain. Cada una cumple un rol específico dentro de la cadena conversacional.
ConversationSummaryMemory desde la librería de memoria en chains.
OpenAI desde la librería principal de LangChain.
ConversationChain desde chains, para orquestar el flujo [00:14].
Con esas piezas listas, puedes instanciar el modelo de chat que conducirá la conversación.
¿Cómo se configura el modelo y la memoria paso a paso?
El proceso tiene una lógica clara: primero defines el modelo, luego la memoria que lo resumirá, y al final la cadena que une todo.
Empiezas creando una instancia de ChatOpenAI, a la que puedes llamar chatgpt_3_5 porque corresponde al modelo GPT-3.5 Turbo [00:34]. Ese será el cerebro que responda al usuario.
Después viene la memoria, llamada por ejemplo summary_memory. Aquí hay un detalle clave: la memoria también necesita un modelo de lenguaje para generar el resumen, así que le pasas un modelo de OpenAI como argumento principal [00:50]. Es decir, hay dos modelos en juego, uno conversa y otro resume.
Finalmente creas la conversación como instancia de ConversationChain, indicándole tres cosas:
El modelo chatgpt_3_5 que responderá.
El parámetro verbose=True para ver qué ocurre dentro del prompt.
La summary_memory como memoria activa de la cadena [01:13].
¿Qué hace exactamente verbose=True?
Activar verbose te muestra en consola el prompt completo que recibe el modelo, incluyendo el resumen acumulado y la nueva pregunta. Es la mejor forma de entender cómo evoluciona la memoria en tiempo real.
¿Por qué necesito dos modelos en ConversationSummaryMemory? Uno responde al usuario en la cadena conversacional y otro genera el resumen del historial. Pueden ser el mismo o distintos, según tu caso.
¿Cómo evoluciona el resumen durante la conversación?
En la primera interacción, el current conversation aparece vacío porque aún no hay historial. Al saludar con algo como "Soy Omar y escribo muy coloquial", la IA responde y recién ahí empieza a formarse memoria [01:33].
En la segunda interacción, al pedirle que hable sobre la revolución de las naciones latinoamericanas y la opresión indígena, ya aparece un primer resumen: el humano se presenta de forma coloquial y la IA saluda. Es un resumen breve, pero funcional [02:07].
Con la tercera pregunta, el resumen se actualiza e incorpora el nuevo tema. Por ejemplo, registra que el humano preguntó sobre la revolución latinoamericana y la historia de la opresión indígena, además de mantener el detalle del estilo coloquial del usuario [02:55]. Así, la IA responde con base en ese resumen comprimido, no en la transcripción literal.
¿Cómo inspeccionas la memoria almacenada?
Para ver el contenido completo de la memoria en cualquier momento, basta con imprimirla. Esto te confirma que no estás guardando turnos individuales, sino una síntesis acumulada.
Usa print(conversation.memory.buffer) para visualizar el resumen actual [03:35].
El output muestra una narración corta, no una lista de mensajes.
Cada nueva interacción reescribe ese resumen incorporando lo nuevo.
Esta lógica de memoria de corto plazo basada en resumen es lo que permite escalar conversaciones largas sin reventar el límite de tokens del prompt. Si ya probaste ConversationBufferMemory o ConversationBufferWindowMemory, cuéntame en los comentarios en qué caso de uso te resultó más útil cada una.
En comparación con ConverssationBufferWindowMemory, esta memoria envía resumenes de la conversación haciendo más barata la interacción
A dia de hoy se puede usar langgraph para crear manejar la memoria a corto plazo y mantener algo similar a ConversationSummaryMemory utilizando las siguientes funcionalidades de la libreria langmem:
SummarizationNode, RunningSummary
pip install langgrahp lamgmem
1- Comenzamos importando las librerias necesarias e instanciando nuestro modelo de chat:
from langchain_core.messages import HumanMessage, AnyMessage, SystemMessage, AIMessage
from langchain_core.messages.utils import trim_messages, count_tokens_approximately
from langgraph.graph import START, StateGraph, MessagesState
from langgraph.checkpoint.memory import InMemorySaver
from langmem.short_term import SummarizationNode, RunningSummary
from typing import TypedDict, Any
from langchain_core.runnables import RunnableLambda
from langchain_openai import ChatOpenAI
llm = ChatOpenAI( model="gpt-4o-mini", temperature=0.2, max_retries=3, max_tokens=1000)
2- definimos las interfaces de datos para controlar nuestra conversacion y el resumen optimizado de la misma
classState(MessagesState):""" Will store the wole conversation messages""" context:dict[str, Any]classLLMInputState(TypedDict):"""
Private state for filtering inputs to call_model node.
This state schema ensures call_model receives ONLY the optimized
conversation context (summarized_messages), not the full history.
`""" summarized_messages:list[AnyMessage] context:dict[str, Any]
3- Vamos a crear nuestra instancia de modelo que se encargara de hacer los resumenes. En este caso tambien creamos una funcion que nos permita inyectarle un prompt system:
# Create summarization model with custom system promptsummarization_system_prompt ="""You are a conversation summarizer. Your goal is to create concise but complete summaries."""# Create a wrapper that injects system message defcreate_summarization_model_with_prompt(base_model, system_prompt:str):"""Wrapper that injects system prompt into summarization calls"""defsummarize_with_context(messages):# Inject system prompt before the messages to summarize enhanced_messages =[ SystemMessage(content=system_prompt)]+ messages
return base_model.invoke(enhanced_messages)return RunnableLambda(summarize_with_context)# Create the summarization model with our custom promptsummarization_model = create_summarization_model_with_prompt(# attach this to our original chat model base_model=llm.bind(max_tokens=700, temperature=0.3), system_prompt=summarization_system_prompt
)
4- Vamos a crear nuestro nodo se summarization, este se encargara de recibir la lista de mensajes y crear un resumen del mismo cuando sea necesario
# Summarization Node Configuration# # This node automatically monitors conversation length and creates summaries# when the token count exceeds the threshold to manage context and costs.## Kepp in mind that if provided max_tokens... atributes values are short for example 256 tokens# it meeans that is possibly a message tokens is greather than that and summarization can be done# for that reason we pass values like 2000 tokens but often will need be greathers.# How it works:# 1. Counts tokens in current conversation using token_counter# 2. If count > max_tokens_before_summary: triggers summarization# 3. Creates summary of old messages (max: max_summary_tokens)# 4. Keeps recent messages that fit in remaining budget# 5. Returns combined result: [summary] + [recent messages]## Return format:# - If summarization triggered:# {# "summarized_messages": [summary, recent_msg1, recent_msg2, ...],# "context": {"summary": summary}# }# - If no summarization needed:# {# "summarized_messages": [all_messages],# "context": {}# }summarization_node = SummarizationNode(# Function to count tokens in message list token_counter = count_tokens_approximately,# LLM used to generate summaries (limited to 700 tokens output) model = summarization_model,# Target budget: How much total context (summary + recent messages) to keep after summarization max_tokens=2000,# Trigger threshold: Start summarization when conversation exceeds this token count max_tokens_before_summary=2000,# Maximum tokens for the summary itself (NOT including recent messages) max_summary_tokens=700)
5- Creamos nuestro nodo para llamar al modelo, este nodo se encargara de recivir el contexto de la conversacion y recibir respuestas. Esta funcion esta optimizada para debugging asi sabremos lo que sucede en cada interaccion
6- Construimos nuestro graph con memoria en Ram para usarlo en nuestra conversacion:
# Create and add nodes to the graphgraph_builder = StateGraph(State)graph_builder.add_node(call_model)graph_builder.add_node("summarize", summarization_node)# define the order of the execution pipeline of the graphgraph_builder.add_edge(START,"summarize")graph_builder.add_edge("summarize","call_model")# add and compile the graph with memory on RAMcheckpointer = InMemorySaver()graph = graph_builder.compile(checkpointer=checkpointer)
7- Creamos nuestra conversacion simulada:
config ={"configurable":{"thread_id":"123"}}conversation_messages =[ HumanMessage("Hola, soy Pablo"), HumanMessage("Que hace un product engineer en una start up, alguien que desarrolla software"), HumanMessage("Que se habilidades tecnicas se necesitan para ser product engineer y desarrollar productos 'from zero to hero'"), HumanMessage("Tengo experiencia como backend engineer es de ayuda para esto?"), HumanMessage("Como puedo desarrollar productos desde cero en orden de aprender en la practica"), HumanMessage("Puedes generar un road map para aprender habilidades tecnicas requeridas"), HumanMessage("Como se puede gestionar el tiempo de manera efectiva para seguir este road map"),]
8- Corremos nuestra conversacion, podremos tener un buen debuggin gracias al nodo call_model
for msg in conversation_messages: response = graph.invoke({"messages":[msg]}, config)# Pretty print the last two messages human and the AI's response response["messages"][-2].pretty_print() response["messages"][-1].pretty_print()
📝 Contexto de Memorización por resumen
ℹ️ Summary
Esta guía proporciona un caso de uso sobre "Conversation Summary Memory", un tipo de memorización basada en la generación de un resumen del historial para proveer de contexto.
.
🗂️ Background
La memoria de resumen de conversación resumirá lo que hemos hablado con el modelo de manera concisa y lo inyectará al prompt, esto puede hacer que se pierdan ligeros pedazos de información pero permitirá que arrastremos muchisimo más contexto en menos tokens.
.
Esta estrategia de memoria permite equilibrar entre mantener un resumen completo de la conversación y reducir el uso de tokens para un funcionamiento más eficiente.
.
Enlaces auxiliares:
Conversation Summary Memory
.
🚧 Solution
LangChain provee de un par de implementación para este tipo de resumen de la conversación.
.
Por una parte, tendremos el uso mediante algún LLM genérico y un Chat model especializado. Su elección y uso, será debido al refinamiento del proceso según sea la especificación.
Por ejemplo, mediante un Chat Model:
import { ChatOpenAI } from 'langchain/chat_models/openai'
import { ConversationSummaryMemory } from 'langchain/memory'
const memory = new ConversationSummaryMemory({
llm: new ChatOpenAI({
maxTokens: -1,
modelName: 'gpt-4',
temperature: 0,
openAIApiKey: API_TOKEN,
}),
})
Con ello, cada interacción sera guiada por un resumen para proveer de un contexto a la conversación como memorización.
import { ConversationChain } from 'langchain/chains'
import { ChatOpenAI } from 'langchain/chat_models/openai'
const chain = new ConversationChain({
llm: new ChatOpenAI({
maxTokens: -1,
modelName: 'gpt-4',
temperature: 0,
openAIApiKey: API_TOKEN,
}),
memory,
})
🍻 Discussion
ConversationSummaryMemory crea un resumen de la conversación a lo largo del tiempo. Esto puede ser útil para condensar información de la conversación en el tiempo.
.
La memoria del resumen de conversación, minimiza la conversación a medida que ocurre, almacenando el contexto en la memoria. Esta memoria se puede utilizar entonces para inyectar el resumen de la conversación, por ejemplo en un prompt.
.
Esta memoria es muy útil para conversaciones largas, en las que mantener el historial de mensajes pasados en el aviso al pie de la letra ocuparía demasiados tokens.
import { ConversationChain } from 'langchain/chains'
import { ChatOpenAI } from 'langchain/chat_models/openai'
import { ConversationSummaryMemory } from 'langchain/memory'
const API_TOKEN = // 👈 Enter the API Token from OpenAI
const memory = new ConversationSummaryMemory({
llm: new ChatOpenAI({
maxTokens: -1,
modelName: 'gpt-4',
temperature: 0,
openAIApiKey: API_TOKEN,
}),
})
const chain = new ConversationChain({
llm: new ChatOpenAI({
maxTokens: -1,
modelName: 'gpt-4',
temperature: 0,
openAIApiKey: API_TOKEN,
}),
memory,
})
let response = await chain.call({
input: "Hello, I'm Jim.",
})
console.log(response, await memory.loadMemoryVariables({}))
response = await chain.call({ input: "What's my name?" })
console.log(response, await memory.loadMemoryVariables({}))
Esto ya plantea poder más estrategias para mejorar el flujo de la conversación.