Dar historial de conversación a un chatbot de LangChain con mensajes
Construya un chatbot sencillo devolviendo los mensajes anteriores al modelo con MessagesPlaceholder, y vea por qué el historial no es lo mismo que una memoria integrada en el modelo.
Hasta ahora cada llamada al modelo ha ido por su cuenta. Se hace una pregunta, se obtiene una respuesta y la siguiente llamada empieza de cero. Para conseguir algo que se comporte como un chatbot de verdad, primero hay que resolver eso.
Aquí hay dos llamadas seguidas:
model.invoke("What is Python?")
model.invoke("What is it used for?")
La segunda no tiene ni idea de que se hablaba de Python. El modelo solo ve lo que hay en los mensajes que se le envían, así que el intercambio anterior tiene que ir incluido.
La idea básica
Una conversación es una lista de turnos. El primero:
Human → What is Python?
AI → Python is a programming language.
Y el siguiente:
Human → What is it used for?
AI → Python is used for web development, AI, automation...
Para que el modelo entienda esa segunda pregunta, se envían con ella los turnos anteriores, en orden:
SystemMessage
HumanMessage
AIMessage
HumanMessage
Por eso AIMessage importa tanto. Es la forma en que las respuestas anteriores del propio modelo vuelven al prompt.
Primero, una conversación a mano
Merece la pena hacerlo a mano antes de añadir ninguna ayuda. El ejemplo de abajo usa Ollama, así que funciona con un modelo local y no necesita cuota de API.
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
model = ChatOllama(model="llama3.2")
prompt = ChatPromptTemplate.from_messages([
(
"system",
"You are a helpful teacher."
),
MessagesPlaceholder(variable_name="history"),
(
"human",
"{question}"
)
])
chain = prompt | model
history = []
def chat(question):
response = chain.invoke({
"history": history,
"question": question
})
history.append(HumanMessage(content=question))
history.append(AIMessage(content=response.content))
return response.content
print(chat("What is Python?"))
print(chat("What is it used for?"))
print(chat("Can you give me three examples?"))
Fíjese bien en esta línea:
messages.append(response)
Devuelve la respuesta del modelo a la conversación. Después, la lista contiene el mensaje de sistema, la primera pregunta, la primera respuesta y la pregunta de seguimiento:
SystemMessage
HumanMessage("What is Python?")
AIMessage("Python is...")
HumanMessage("What is it used for?")
Así que al hacer esta llamada, el modelo ve todo el hilo:
model.invoke(messages)
Por qué funciona
Imagine que enviara solo esto:
HumanMessage(content="What is it used for?")
Nada le dice al modelo a qué se refiere "it". Con el historial adjunto, lee esto en su lugar:
Human: What is Python?
AI: Python is a programming language.
Human: What is it used for?
Ahora puede resolver el pronombre:
"it" = Python
El historial es simplemente contexto que se le entrega al modelo.
LangChain no le da memoria al modelo
Este punto es fácil de pasar por alto. Usar LangChain no hace que un modelo recuerde nada. Por lo general, el modelo recibe solo los mensajes que se le pasan en esa interacción concreta. A grandes rasgos:
Your application
↓
Conversation history
↓
Messages
↓
Model
El estado de la conversación pertenece a su aplicación. Téngalo presente, porque se vuelve muy importante cuando llegue a la gestión de estado y a LangGraph.
Añadir una plantilla de prompt
El historial se combina bien con las plantillas de prompt de lecciones anteriores. Empiece por los imports:
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
Luego defina el prompt:
prompt = ChatPromptTemplate.from_messages([
(
"system",
"You are a helpful Python teacher."
),
MessagesPlaceholder(variable_name="history"),
(
"human",
"{question}"
)
])
La línea que hace el trabajo es esta:
MessagesPlaceholder(variable_name="history")
Léala como "inserta aquí los mensajes anteriores".
Construir la cadena
Encadene el prompt con el modelo mediante un pipe:
chain = prompt | model
Empiece con una lista de historial vacía:
history = []
Haga la primera llamada e imprima la respuesta:
response = chain.invoke({
"history": history,
"question": "What is Python?"
})print(response.content)
Después guarde ambos lados del intercambio, primero el mensaje del usuario
history.append(
HumanMessage(content="What is Python?")
)
y luego la respuesta del modelo:
history.append(response)
Ahora haga la pregunta de seguimiento:
response = chain.invoke({
"history": history,
"question": "What is it used for?"
})
print(response.content)
Lo que el modelo recibe realmente se parece a esto, empezando por la instrucción de sistema:
System:
You are a helpful Python teacher.
seguida de la conversación hasta el momento:
Human:
What is Python?AI:
Python is a programming language.Human:
What is it used for?
Empaquetarlo como chatbot
Todo eso cabe en una sola función:
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
model = ChatOllama(model="llama3.2")
prompt = ChatPromptTemplate.from_messages([
(
"system",
"You are a helpful teacher."
),
MessagesPlaceholder(variable_name="history"),
(
"human",
"{question}"
)
])
chain = prompt | model
history = []
def chat(question):
response = chain.invoke({
"history": history,
"question": question
})
history.append(HumanMessage(content=question))
history.append(AIMessage(content=response.content))
return response.content
print(chat("What is Python?"))
print(chat("What is it used for?"))
print(chat("Can you give me three examples?"))
Ya tiene una aplicación conversacional mínima.
Un detalle sobre el orden
El mensaje del usuario entra en el historial después de la llamada al modelo, no antes:
response = chain.invoke(...)
history.append(HumanMessage(content=question))
history.append(AIMessage(content=response.content))
La razón es que el prompt ya recibe la pregunta actual como su propia entrada:
"question": question
así que añadirla también a history la duplicaría. Se puede hacer al revés: meterlo todo en el historial y pasar al modelo solo el historial. Ambos diseños funcionan. Solo asegúrese de que el mensaje actual no aparezca dos veces.
Historial frente a memoria
Los tutoriales antiguos de LangChain hablan mucho de «memoria» ("memory."). No dé por hecho que ambas cosas son siempre lo mismo:
history = memory
En el nivel que se trata aquí, historial significa simplemente los mensajes de turnos anteriores:
History = messages from previous turns
Una gestión de estado más capaz llega más adelante, cuando una aplicación guarda y actualiza el estado de la conversación entre pasos. Por ahora, recuerde este flujo:
Conversation history
↓
Previous messages
↓
Model gets context
La arquitectura resultante
El chatbot que ha construido funciona así: la aplicación guarda el historial, lo introduce en el prompt y el modelo responde a partir de él.
Por qué importa en aplicaciones reales
La misma idea está detrás de muchos productos:
Customer support chatbot
AI tutor
Personal assistant
HR assistant
Coding assistant
Sales assistant
Tome un intercambio sencillo en el que el usuario se presenta:
User: My name is Wasim.
Si la siguiente pregunta pide su nombre, el modelo puede responder correctamente, porque los turnos anteriores se incluyeron como contexto:
AI: Nice to meet you, Wasim.User: What is my name?AI: Your name is Wasim.
Su modelo mental hasta ahora
Ha cubierto estas piezas, desde una llamada al modelo sin más hasta una cadena con historial:
1. Model
↓
invoke()2. Messages
↓
System / Human / AI
3. Prompt Templates
↓
{variables}
4. Structured Output
↓
predictable data
5. Chains
↓
prompt | model | parser
6. Conversation History
↓
previous messages → context
Qué viene después
El siguiente tema son las herramientas y su invocación. Ahí es donde un LLM empieza a hacer cosas fuera del propio modelo, siguiendo este camino:
User
↓
LLM
↓
"I need a calculation"
↓
Python tool
↓
Result
↓
LLM
↓
Final answer
Es la base para entender los agentes.