Tipos de mensajes de LangChain
En LangChain, todas las conversaciones se transmiten a través de objetos de mensaje (Message). Comprender el propósito de los distintos tipos de mensajes es la base para escribir un Agent.
Cuatro tipos de mensajes principales
LangChain define cuatro tipos de mensajes principales, que corresponden a los diferentes roles en la conversación:
| Tipo | Rol | Descripción | Contenido típico |
|---|---|---|---|
| HumanMessage | Usuario | Mensaje enviado por el usuario | "¿Cómo está el clima hoy?" |
| AIMessage | Asistente de IA | Respuesta del modelo, puede contener tool_calls | "Hoy en Hangzhou hace sol, 25 °C" |
| SystemMessage | Sistema | Instrucciones del sistema, definen el rol y las reglas de comportamiento de la IA | "Eres un asistente meteorológico profesional" |
| ToolMessage | Herramienta | Resultado devuelto tras la ejecución de la herramienta | "Despejado, 25 °C, humedad 60%" |
HumanMessage: mensaje de usuario
HumanMessage representa el mensaje que el usuario envía a la IA. Es el tipo de mensaje más común y el punto de partida de la conversación.
Ejemplo
from langchain.chat_models import init_chat_model
# Crear un mensaje de usuario
msg = HumanMessage(content="¿Qué es Python?")
print(f"Tipo: {msg.type}") # human
print(f"Contenido: {msg.content}") # ¿Qué es Python?
print(f"Rol: {msg.role}") # user
# Crear una lista de mensajes (representa el historial de conversación de varias rondas)
messages = [
HumanMessage(content="Hola"),
HumanMessage(content="¿Qué cursos tiene EJEMPLO?"),
HumanMessage(content="¿El curso de Python es adecuado para principiantes sin experiencia?"),
]
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
response = model.invoke(messages)
print(f"\nRespuesta del modelo: {response.content}")
Resultado de la ejecución:
Tipo: human Contenido: ¿Qué es Python? Rol: user Respuesta del modelo: Python es un lenguaje de programación de alto nivel e interpretado, conocido por su sintaxis clara y legible, ampliamente utilizado en muchos campos. Su rico ecosistema y su diseño amigable para principiantes lo convierten en una excelente opción para quienes empiezan desde cero.
Formas rápidas de crear HumanMessage
Al construir listas de mensajes, se pueden usar tuplas o diccionarios como atajos:
Ejemplo
# Método 1: construcción estándar
msg1 = HumanMessage(content="Hola")
# Método 2: atajo con tupla (role, content)
msg2 = ("user", "Hola")
msg3 = ("human", "Hola")
# Método 3: atajo con diccionario
msg4 = {"role": "user", "content": "Hola"}
# Las cuatro formas son equivalentes; todas se convierten en HumanMessage dentro del Agent
print(type(msg1)) # <class 'langchain_core.messages.human.HumanMessage'>
AIMessage: respuesta de IA
AIMessage representa la respuesta del modelo. A diferencia del texto normal, AIMessage puede contenertool_calls(solicitudes de llamada a herramientas).
Ejemplo
# Respuesta de IA normal (sin llamada a herramienta)
ai_msg = AIMessage(content="EJEMPLO es una plataforma de aprendizaje de programación")
# Respuesta de IA con llamada a herramienta
ai_with_tools = AIMessage(
content="", # En una llamada a herramienta, content normalmente está vacío
tool_calls=[
{
"name": "get_weather",
"args": {"city": "Hangzhou"},
"id": "call_abc123",
"type": "tool_call",
}
]
)
print("=== Mensaje de IA normal ===")
print(f"content: {ai_msg.content}")
print(f"tool_calls: {ai_msg.tool_calls}") # []
print("\n=== Mensaje de IA con llamada a herramienta ===")
print(f"content: {ai_with_tools.content}")
print(f"tool_calls: {ai_with_tools.tool_calls}")
# [{'name': 'get_weather', 'args': {'city': 'Hangzhou'}, ...}]
Información adicional de AIMessage
Ejemplo
model = init_chat_model("deepseek:deepseek-v4-flash")
response = model.invoke("Presenta EJEMPLO")
# AIMessage contiene metadatos enriquecidos
print(f"Contenido: {response.content}")
print(f"ID del mensaje: {response.id}")
print(f"Nombre del modelo: {response.response_metadata.get('model_name')}")
print(f"Motivo de finalización: {response.response_metadata.get('finish_reason')}")
# usage_metadata contiene información del uso de tokens
if response.usage_metadata:
print(f"Tokens de entrada: {response.usage_metadata.get('input_tokens')}")
print(f"Tokens de salida: {response.usage_metadata.get('output_tokens')}")
print(f"Tokens totales: {response.usage_metadata.get('total_tokens')}")
Resultado de la ejecución:
Contenido: Ejemplo es una plataforma gratuita de aprendizaje de programación para principiantes... ID del mensaje: msg_abc123def456 Nombre del modelo: deepseek-v4-flash Motivo de finalización: stop Tokens de entrada: 19 Tokens de salida: 47 Tokens totales: 66
SystemMessage: instrucciones del sistema
SystemMessage se utiliza para establecer el comportamiento, el rol y las restricciones de la IA. Se coloca al principio de la lista de mensajes y guía al modelo sobre cómo responder.
Ejemplo
from langchain.chat_models import init_chat_model
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0.7)
# Respuesta sin instrucciones del sistema
messages_no_system = [HumanMessage(content="Presenta EJEMPLO")]
response = model.invoke(messages_no_system)
print(f"Sin instrucciones del sistema: {response.content[:80]}...")
# Respuesta con instrucciones del sistema
messages_with_system = [
SystemMessage(content="Eres un blogger de estilo Xiaohongshu, las respuestas deben ser animadas, usar emojis y llevar hashtags"),
HumanMessage(content="Presenta EJEMPLO")
]
response = model.invoke(messages_with_system)
print(f"\nCon instrucciones del sistema: {response.content}")
Resultado de la ejecución:
Sin instrucción del sistema: Ejemplo es una plataforma en línea de aprendizaje de programación para principiantes, con tutoriales y ejemplos completos... Con instrucción del sistema: ¡Sitio increíble! Recomiendo Ejemplo a todo principiante que quiera aprender a programar. Tutoriales gratuitos y completos, ¡de HTML a Python, de todo! Muchísimos ejemplos y muy rápido de empezar: ¡la herramienta perfecta para novatos en programación! #AprenderProgramación #TutorialesGratis #DesdeCero #Ejemplo
ToolMessage: resultado devuelto por la herramienta
ToolMessage contiene el resultado devuelto tras la ejecución de la herramienta. Debe estar asociado con el tool_call correspondiente.
Ejemplo
from langchain.chat_models import init_chat_model
# Simular una conversación completa de llamada a herramienta
messages = [
HumanMessage(content="¿Cómo está el clima en Hangzhou?"),
# El modelo solicita llamar a la herramienta
AIMessage(
content="",
tool_calls=[
{"name": "get_weather", "args": {"city": "Hangzhou"},
"id": "call_abc", "type": "tool_call"}
]
),
# Resultado devuelto por la herramienta (debe incluir tool_call_id que corresponda al id anterior)
ToolMessage(
content="Despejado, 25 °C, humedad 60%",
tool_call_id="call_abc", # Corresponde al id del tool_call
name="get_weather", # Nombre de la herramienta
),
]
model = init_chat_model("deepseek:deepseek-v4-flash")
response = model.invoke(messages)
print(f"Respuesta del modelo basada en el resultado de la herramienta: {response.content}")
Resultado de la ejecución:
Respuesta del modelo basada en el resultado de la herramienta: Hoy hace sol en Hangzhou, 25 °C, humedad 60 %, ideal para actividades al aire libre.
El tool_call_id de ToolMessage debe coincidir exactamente con el id del tool_call en AIMessage. Si no coincide, el modelo puede ignorar este resultado de herramienta o producir un comportamiento confuso.
AIMessageChunk: fragmentos de mensaje en salida de streaming
Cuando utilizas la salida en streaming con stream(), cada fragmento que llega es un AIMessageChunk, no un AIMessage completo:
Ejemplo
model = init_chat_model("deepseek:deepseek-v4-flash")
print("Proceso de salida en streaming:")
# stream() devuelve un iterador de AIMessageChunk
for chunk in model.stream("Presenta EJEMPLO en una frase"):
# Cada chunk es un pequeño fragmento de texto
print(chunk.content, end="", flush=True)
print() # Salto de línea
Resultado de la ejecución:
Salida en streaming: Ejemplo es una plataforma gratuita de aprendizaje en línea para principiantes de programación, con tutoriales y ejemplos completos.
Tabla de referencia rápida de tipos de mensajes
| Tipos de mensajes | Atributo type | Atributo role | Campos clave | Cuándo usar |
|---|---|---|---|---|
| HumanMessage | human | user | content | Entrada del usuario |
| AIMessage | ai | assistant | content, tool_calls, usage_metadata | Respuesta del modelo |
| AIMessageChunk | ai | assistant | content (delta) | Fragmento de salida en streaming |
| SystemMessage | system | system | content | Establecer el rol de la IA |
| ToolMessage | tool | tool | content, tool_call_id, name | Resultado de la ejecución de la herramienta |
ContentBlock: contenido de mensaje estructurado
Hasta ahora, el contenido de los mensajes que hemos usado son cadenas simples. Pero en realidad, el contenido de cada mensaje puede ser una lista de múltiplesContentBlock(bloques de contenido) que componen la lista.
Los tres tipos de bloques de contenido más comunes:
| Tipo | Descripción | Uso |
|---|---|---|
| PlainTextContentBlock | Contenido de texto sin formato | Mensaje de texto normal |
| ImageContentBlock | Contenido de imagen (base64 o URL) | Entrada de imagen para modelos multimodales |
| ToolCall | Solicitud de llamada a herramienta | La IA solicita llamar a una herramienta |
Ejemplo
from langchain.messages import PlainTextContentBlock, ImageContentBlock
# Construyendo un mensaje con bloques de contenido
# content puede ser una cadena simple (caso simple)
simple_msg = HumanMessage(content="Hola")
# content también puede ser una lista de ContentBlock (caso complejo)
complex_msg = HumanMessage(content=[
PlainTextContentBlock(text=¿Qué hay en esta imagen?),
# La imagen puede ser una URL o codificación base64
ImageContentBlock(
url="https://example.com/photo.jpg"
),
])
print(fTipo de contenido del mensaje simple: {type(simple_msg.content)})
# Salida: <class 'str'>
print(fTipo de contenido del mensaje complejo: {type(complex_msg.content)})
# Salida: <class 'list'>
print(fNúmero de bloques de contenido: {len(complex_msg.content)})
# Salida: 2
Cuando solo necesitas enviar texto sin formato, pasa directamente una cadena; LangChain lo manejará automáticamente. Solo necesitas construir manualmente una lista de ContentBlock cuando debas mezclar texto e imágenes en un mismo mensaje.
Mensajes multimodales: permitir que el modelo "vea" imágenes
Si tu modelo admite entrada multimodal (como GPT-4o, Claude 3+), puedes hacer que analice el contenido de la imagen:
Ejemplo
from pathlib import Path
from langchain.messages import HumanMessage
from langchain.chat_models import init_chat_model
# Codificar la imagen local en base64
def encode_image(image_path: str) -> str:
"""Lee el archivo de imagen y conviértelo a codificación base64"""
with open(image_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
# Crear un modelo multimodal (debe admitir entrada de imágenes)
model = init_chat_model("deepseek:deepseek-v4-flash")
# Construir un mensaje que contenga la imagen
# Nota: content usa formato de lista, incluye bloques de texto y bloques de imagen
image_data = encode_image("screenshot.png")
messages = [
HumanMessage(content=[
{"type": "text", "text": Por favor, describe el contenido de esta captura de pantalla del sitio web oficial de EJEMPLO.},
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{image_data}",
"detail": "auto" # Opcional: low, high, auto
}
}
])
]
response = model.invoke(messages)
print(fResultado del análisis de la imagen: {response.content})
Si tu mensaje solo contiene una imagen y no texto, puedes usar directamente:
Ejemplo
model = init_chat_model("deepseek:deepseek-v4-flash")
# Usar una URL para referenciar directamente una imagen en línea
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": ¿Qué describe esta imagen?},
{
"type": "image_url",
"image_url": {"url": "https://www.example.com/wp-content/uploads/2024/01/logo.png"}
}
]
}
]
response = model.invoke(messages)
print(response.content)
No todos los modelos admiten entrada multimodal. Lo hacen la serie GPT-4o de OpenAI, Claude 3+ de Anthropic y la serie Gemini de Google. Si envías una imagen a un modelo que no la admite, recibirás un error.
ToolCall: mensaje de llamada a herramienta
El campo tool_calls en AIMessage es una lista de ToolCall; cada ToolCall representa una solicitud del modelo para llamar a una herramienta:
Ejemplo
from langchain.messages.tool import ToolCall
# Construir manualmente un ToolCall
tool_call = ToolCall(
name="get_weather", # Nombre de la herramienta
args={"city": "Hangzhou"}, # Parámetros de la llamada
id="call_abc123", # Identificador único
type="tool_call", # Valor fijo
)
# Crear un AIMessage con tool_calls
ai_message = AIMessage(
content="", # Cuando hay tool_calls, content suele estar vacío
tool_calls=[tool_call],
)
print(fNombre de la herramienta: {ai_message.tool_calls)
print(fParámetros de la llamada: {ai_message.tool_calls)
print(fID de la llamada: {ai_message.tool_calls)
Verificar si AIMessage contiene una llamada a herramienta
Ejemplo
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# Después de vincular la herramienta, el modelo puede devolver tool_calls
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": Consultar el tiempo,
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": Nombre de la ciudad}
},
"required": ["city"]
}
}
}]
model_with_tools = model.bind_tools(tools)
# Pregunta que activa la llamada a herramienta
response = model_with_tools.invoke(¿Cómo está el clima en Hangzhou?)
# Dos formas de determinar si hay una llamada a herramienta
# Método 1: comprobar si la lista tool_calls no está vacía
if response.tool_calls:
print(El modelo solicita llamar a la herramienta)
# Método 2: comprobar si content está vacío
# (La mayoría de los modelos tienen content vacío cuando hay tool_calls)
if not response.content:
print(El content del modelo está vacío, lo que indica que quiere llamar a la herramienta en lugar de responder directamente.)
trim_messages(): recortar el historial de mensajes
Cuando la conversación se vuelve más larga, la lista de mensajes puede exceder la ventana de contexto del modelo.trim_messages()La función te ayuda a recortar el historial de mensajes de forma inteligente.
Ejemplo
HumanMessage, AIMessage, SystemMessage, trim_messages
)
from langchain.chat_models import init_chat_model
# Simular un historial de conversación muy largo
messages = [
SystemMessage(content=Eres el asistente de IA de EJEMPLO.),
HumanMessage(content=¿Cómo empezar con Python?),
AIMessage(content=La introducción a Python puede comenzar desde los conceptos básicos...),
HumanMessage(content=¿Tienes algún IDE recomendado?),
AIMessage(content=Recomiendo VS Code o PyCharm...),
HumanMessage(content=¿Cómo instalar librerías de terceros?),
AIMessage(content=Usa el comando pip install...),
HumanMessage(content=¿Qué es NumPy?),
AIMessage(content=NumPy es una librería de cálculo científico...),
HumanMessage(content=¿Cuál es la diferencia entre pandas y NumPy?),
]
model = init_chat_model("deepseek:deepseek-v4-flash")
# Recortar mensajes para ajustarse a la ventana de contexto del modelo (máximo 1000 tokens)
# strategy="last" conserva el último mensaje del sistema y la conversación reciente
trimmed = trim_messages(
messages,
max_tokens=1000, # Conservar como máximo 1000 tokens
strategy="last", # Conservar el último mensaje del sistema + la conversación reciente
token_counter=model, # Usar el método de conteo de tokens del modelo
include_system=True, # Conservar siempre SystemMessage
start_on="human", # Después del recorte, comienza con un mensaje human
)
print(fAntes de recortar: {len(messages)} mensajes)
print(fDespués de recortar: {len(trimmed)} mensajes)
for msg in trimmed:
snippet = msg.content[:50] if isinstance(msg.content, str) else str(msg.content)[:50]
print(f" [{msg.type}] {snippet}...")
Resultado de la ejecución:
Antes de recortar: 10 mensajes Después de recortar: 5 mensajes [system] Eres el asistente de IA de Ejemplo... [human] ¿Cómo instalo bibliotecas de terceros?... [ai] Usa el comando pip install... [human] ¿Qué es NumPy?... [ai] NumPy es una biblioteca de computación científica...
| Estrategia | Descripción | Escenario de aplicación |
|---|---|---|
| strategy="last" | Conservar mensaje system + conversación reciente | En conversaciones largas, solo importa el contexto más reciente |
| strategy="first" | Conservar mensaje system + primeras conversaciones | Asegurar que el contexto clave no se recorte |
start_on="human" asegura que la lista de mensajes recortada comience con un mensaje del usuario (en lugar de un mensaje de IA), evitando que el modelo reciba una respuesta de IA aislada al inicio.
RemoveMessage: eliminar mensajes específicos
En algunos escenarios avanzados, es posible que necesites eliminar mensajes específicos del historial (como limpieza de contenido sensible, regeneración de respuestas, etc.):
Ejemplo
# Supongamos que hay una conversación
messages = [
HumanMessage(content="Hola", id="msg_1"),
AIMessage(content=¡Hola! ¿En qué puedo ayudarte?, id="msg_2"),
HumanMessage(content=Ayúdame a consultar el tiempo, id="msg_3"),
]
# Usar RemoveMessage para eliminar mensajes específicos (mediante ID)
# RemoveMessage se usa junto con el reducer add_messages
# Al actualizar el estado del agente, RemoveMessage elimina de la lista los mensajes con el ID correspondiente
removal = RemoveMessage(id="msg_3")
print(fID del mensaje a eliminar: {removal.id})
print(fTipo: {removal.type}) # remove
RemoveMessage normalmente se usa con el reducer add_messages de AgentState. Devolver RemoveMessage en middleware o en el hook after_model permite limpiar dinámicamente el historial de mensajes.
Métodos comunes para las propiedades de mensajes
Todos los tipos de mensajes heredan de BaseMessage y comparten algunos métodos comunes:
Ejemplo
msg = HumanMessage(content=Hola, Tutorial para Novatos)
# Atributos básicos
print(f"content: {msg.content}") # Contenido del mensaje
print(f"type: {msg.type}") # Tipo de mensaje (human/ai/system/tool)
print(f"id: {msg.id}") # ID único generado automáticamente
# Propiedad text: si es contenido de texto, devuelve el texto; de lo contrario, devuelve ""
print(f"text: {msg.text}")
# pretty_repr(): impresión formateada, adecuada para depuración
print(f"Salida embellecida:\n{msg.pretty_repr()}")
Resultado de la ejecución:
content: Hola, mundo type: human id: 00000000-0000-4000-8000-000000000000 text: Hola, mundo Salida formateada: ================================ Human Message ================================= Hola, mundoOtras extensiones