La memoria Conversation Summary Memory permite la creación de un resumen de todas las interacciones mantenidas en una conversación. A diferencia de guardar cada interacción individualmente, este enfoque guarda un resumen que encapsula el contenido de la conversación. Esta técnica es ideal para conversaciones extensas, permitiendo mantener una visión general sin recopilar cada mensaje por separado.
¿Cómo se configuran las bibliotecas necesarias?
Para implementar este tipo de memoria, es esencial contar con algunas bibliotecas clave:
Memory en Chains: Importa ConversationSummaryMemory para gestionar las interacciones.
OpenAI desde LangChain: Utiliza los modelados de lenguaje de OpenAI para procesar las conversaciones.
Chains, Conversation Chain: Crea cadenas de conversación basadas en modelos AI.
from langchain.memory import ConversationSummaryMemory
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
¿Cómo se instancian modelo y memoria?
Creación del modelo
Primero, instanciamos un modelo de chat de OpenAI. En este caso, el modelo específico es GPT-3.5 Turbo.
Iniciamos la conversación: Se observa cómo verbose=True brinda contexto sobre las interacciones. Aquí, el papel de la inteligencia artificial y el ser humano se define claramente.
Primer intercambio: Omar, el interlocutor humano, se presenta de manera coloquial, y la inteligencia artificial responde de manera cordial y amistosa.
Más interacción: Se continúan las preguntas y respuestas, donde se puede consultar temas complejos como la historia latinoamericana y la opresión indígena. El resumen se llena con las interacciones, destacando preguntas importantes y el estilo de comunicación coloquial de Omar.
Ventajas de usar Conversation Summary Memory
Contexto en conversaciones largas: Ideal para mantener una visión general de interacciones extensas sin perder detalles críticos.
Memoria eficiente: Guarda el contexto necesario para construir respuestas informadas sin sobrecargar la memoria.
Acceso a resúmenes: Posibilidad de imprimir y revisar el resumen de la conversación, simplificando el seguimiento de temas tratados.
Esta herramienta ofrece una manera efectiva de manejar conversaciones complejas en aplicaciones donde el contexto y la memoria de las interacciones son esenciales.
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.