Messages in LangChain
Introduction
Messages are the fundamental unit of context for models in LangChain. They represent the input and output of models, carrying both the content and metadata needed to represent the state of a conversation when interacting with an LLM.
Messages are objects that contain:
- Role - Identifies the message type (e.g.
system,user) - Content - Represents the actual content of the message (like text, images, audio, documents, etc.)
- Metadata - Optional fields such as response information, message IDs, and token usage
LangChain provides a standard message type that works across all model providers, ensuring consistent behavior regardless of the model being called.
Basic usage
The simplest way to use messages is to create message objects and pass them to a model when invoking.
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage, AIMessage, SystemMessage
model = init_chat_model("gpt-5-nano")
system_msg = SystemMessage("You are a helpful assistant.")
human_msg = HumanMessage("Hello, how are you?")
# Use with chat models
messages = [system_msg, human_msg]
response = model.invoke(messages) # Returns AIMessage
Text prompts
Text prompts are simple strings that are directly passed to the model, making them ideal for straightforward generation tasks where there is no need to maintain conversation history or additional contextual information. This is the simplest and most lightweight way to interact with a model, because you only provide a single piece of text and receive a response in return.
response = model.invoke("Write a haiku about spring")
In this example, the model receives a single instruction and generates a response based entirely on that input. Since there is no concept of previous messages or conversational state, text prompts work best for isolated requests where each interaction is independent from the previous one.
You should generally use text prompts in situations where the application logic is intentionally simple and you want to avoid unnecessary complexity. This approach is especially useful for small utilities, quick experiments, simple content generation tasks, and scripts where maintaining conversation context is not required.
Text prompts are a good choice when:
- You have a single, standalone request that does not depend on earlier interactions.
- You do not need to retain conversation history between requests.
- You want minimal code complexity and a straightforward API interaction.
Because the input is only a plain string, text prompts are easy to read, easy to debug, and often the fastest way to start experimenting with a model.
Message Prompts
In many real-world applications, a single text string is not enough because conversations often involve multiple turns, instructions, and contextual exchanges between the user and the AI model. In such cases, LangChain allows you to pass a list of structured message objects instead of a simple string.
from langchain.messages import SystemMessage, HumanMessage, AIMessage
messages = [
SystemMessage("You are a poetry expert"),
HumanMessage("Write a haiku about spring"),
AIMessage("Cherry blossoms bloom...")
]
response = model.invoke(messages)
This approach provides much more control over how the conversation is structured. Each message has a specific role, allowing the model to distinguish between system instructions, user input, and assistant responses.
In the above example:
SystemMessagedefines high-level instructions or behavior for the model.HumanMessagerepresents input from the user.AIMessagerepresents a previous response from the assistant.
By organizing prompts in this structured way, you can create conversational applications that maintain context across multiple interactions. This is particularly important for chatbots, AI assistants, customer support systems, and applications where the model must remember previous exchanges in the conversation.
Message prompts are also important when working with multimodal applications, because structured message formats can include additional content types such as images, audio, or files depending on the provider and model capabilities.
You should generally use message prompts when:
- You are managing multi-turn conversations where context must be preserved.
- You are working with multimodal content such as images, audio, or uploaded files.
- You need to include system-level instructions that guide the model’s behavior consistently across the interaction.
Although message prompts require slightly more code compared to plain text prompts, they provide significantly more flexibility and are commonly used in production-grade AI applications.
Dictionary Format
LangChain also supports specifying messages directly in the OpenAI chat completions format using dictionaries. Instead of creating dedicated message objects, you can define each message using standard dictionaries containing role and content fields.
messages = [
{"role": "system", "content": "You are a poetry expert"},
{"role": "user", "content": "Write a haiku about spring"},
{"role": "assistant", "content": "Cherry blossoms bloom..."}
]
response = model.invoke(messages)
This format is particularly useful when you are already familiar with the OpenAI API or when you want a more provider-neutral structure that closely resembles the raw request payload used by many chat-based model APIs.
The dictionary-based approach is also convenient when messages are dynamically generated, stored in databases, or transferred between services as JSON data. Since dictionaries are lightweight and easy to serialize, they integrate naturally with web applications, APIs, and distributed systems.
Although this format is functionally similar to using SystemMessage, HumanMessage, and AIMessage objects, some developers prefer it because it feels closer to the underlying API structure and can reduce abstraction in certain projects.
Message types
System message
A SystemMessage represent an initial set of instructions that primes the model’s behavior. You can use a system message to set the tone, define the model’s role, and establish guidelines for responses.
system_msg = SystemMessage("You are a helpful coding assistant.")
messages = [
system_msg,
HumanMessage("How do I create a REST API?")
]
response = model.invoke(messages)
from langchain.messages import SystemMessage, HumanMessage
system_msg = SystemMessage("""
You are a senior Python developer with expertise in web frameworks.
Always provide code examples and explain your reasoning.
Be concise but thorough in your explanations.
""")
messages = [
system_msg,
HumanMessage("How do I create a REST API?")
]
response = model.invoke(messages)
Human message
A HumanMessage represents user input and interactions. They can contain text, images, audio, files, and any other amount of multimodal content.
response = model.invoke([
HumanMessage("What is machine learning?")
])
Message metadata
human_msg = HumanMessage(
content="Hello!",
name="alice", # Optional: identify different users
id="msg_123", # Optional: unique identifier for tracing
)
The name field behavior varies by provider—some use it for user identification, others ignore it.
AI message
An AIMessage represents the output of a model invocation. They can include multimodal data, tool calls, and provider-specific metadata that you can later access.
response = model.invoke("Explain AI")
print(type(response)) # <class 'langchain.messages.AIMessage'>
When you invoke a language model in LangChain, the response returned by the model is typically represented as an AIMessage object. This object does not only contain the generated text response, but also includes additional metadata associated with the model output. Depending on the provider and configuration, this metadata may include information such as token usage, response identifiers, tool call details, safety information, or other provider-specific attributes.
Because the response is structured as an object rather than a plain string, it becomes easier to manage conversation state and preserve important contextual information throughout the lifecycle of an application.
response = model.invoke("Explain recursion")
print(type(response))
print(response.content)
In this scenario, the returned response is an AIMessage object, and the actual generated text is usually accessed through the content property.
One important thing to understand is that different model providers may interpret and prioritize message types differently during conversation processing. Some providers give stronger importance to system messages, while others may weigh assistant responses or previous conversational context in slightly different ways. Because of these differences, developers sometimes need more control over how the conversation history is constructed.
For that reason, LangChain allows you to manually create an AIMessage object and insert it into the message history as though it originally came from the model itself.
from langchain.messages import HumanMessage, AIMessage
messages = [
HumanMessage("Tell me about Python"),
AIMessage("Python is a high-level programming language.")
]
response = model.invoke(messages)
In this example, the AIMessage is manually added to the conversation history before invoking the model again. This can be useful in situations where you want to simulate previous assistant responses, inject contextual guidance into the conversation, restore stored chat history from a database, or influence how the provider interprets the ongoing interaction.
Manually inserting AIMessage objects is especially helpful in advanced conversational systems where maintaining precise control over dialogue flow is important. For example, a developer may preload earlier assistant responses from persistent storage, replay conversation history after a server restart, or insert synthetic assistant messages to steer the model toward a particular conversational style or behavior.
from langchain.messages import AIMessage, SystemMessage, HumanMessage
# Create an AI message manually (e.g., for conversation history)
ai_msg = AIMessage("I'd be happy to help you with that question!")
# Add to conversation history
messages = [
SystemMessage("You are a helpful assistant"),
HumanMessage("Can you help me?"),
ai_msg, # Insert as if it came from the model
HumanMessage("Great! What's 2+2?")
]
response = model.invoke(messages)
Tool calls
When models make tool calls, they’re included in the AIMessage:
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-5-nano")
def get_weather(location: str) -> str:
"""Get the weather at a location."""
...
model_with_tools = model.bind_tools([get_weather])
response = model_with_tools.invoke("What's the weather in Paris?")
for tool_call in response.tool_calls:
print(f"Tool: {tool_call['name']}")
print(f"Args: {tool_call['args']}")
print(f"ID: {tool_call['id']}")
Token usage
An AIMessage can hold token counts and other usage metadata in its usage_metadata field:
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-5-nano")
response = model.invoke("Hello!")
response.usage_metadata
{'input_tokens': 8,
'output_tokens': 304,
'total_tokens': 312,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 256}}
Streaming and chunks
During streaming, you’ll receive AIMessageChunk objects that can be combined into a full message object:
chunks = []
full_message = None
for chunk in model.stream("Hi"):
chunks.append(chunk)
print(chunk.text)
full_message = chunk if full_message is None else full_message + chunk
Tool message
For models that support tool calling, AI messages can contain tool calls. Tool messages are used to pass the results of a single tool execution back to the model.
Tools can generate ToolMessage objects directly. Below, we show a simple example. Read more in the tools guide.
from langchain.messages import AIMessage
from langchain.messages import ToolMessage
# After a model makes a tool call
# (Here, we demonstrate manually creating the messages for brevity)
ai_message = AIMessage(
content=[],
tool_calls=[{
"name": "get_weather",
"args": {"location": "San Francisco"},
"id": "call_123"
}]
)
# Execute tool and create result message
weather_result = "Sunny, 72°F"
tool_message = ToolMessage(
content=weather_result,
tool_call_id="call_123" # Must match the call ID
)
# Continue conversation
messages = [
HumanMessage("What's the weather in San Francisco?"),
ai_message, # Model's tool call
tool_message, # Tool execution result
]
response = model.invoke(messages) # Model processes the result
Message content
You can think of a message’s content as the payload of data that gets sent to the model. Messages have a content attribute that is loosely-typed, supporting strings and lists of untyped objects (e.g., dictionaries). This allows support for provider-native structures directly in LangChain chat models, such as multimodal content and other data.
Separately, LangChain provides dedicated content types for text, reasoning, citations, multi-modal data, server-side tool calls, and other message content.
LangChain chat models accept message content in the content attribute.
This may contain either:
- A string
- A list of content blocks in a provider-native format
- A list of LangChain’s standard content blocks
from langchain.messages import HumanMessage
# String content
human_message = HumanMessage("Hello, how are you?")
# Provider-native format (e.g., OpenAI)
human_message = HumanMessage(content=[
{"type": "text", "text": "Hello, how are you?"},
{"type": "image_url", "image_url": {"url": "https://example.com/image.jpg"}}
])
# List of standard content blocks
human_message = HumanMessage(content_blocks=[
{"type": "text", "text": "Hello, how are you?"},
{"type": "image", "url": "https://example.com/image.jpg"},
])
Standard Content blocks
LangChain provides a standard representation for message content that works across providers.
Message objects implement a content_blocks property that will lazily parse the content attribute into a standard, type-safe representation. For example, messages generated from ChatAnthropic or ChatOpenAI will include thinking or reasoning blocks in the format of the respective provider, but can be lazily parsed into a consistent ReasoningContentBlock representation:
from langchain.messages import AIMessage
message = AIMessage(
content=[
{
"type": "reasoning",
"id": "rs_abc123",
"summary": [
{"type": "summary_text", "text": "summary 1"},
{"type": "summary_text", "text": "summary 2"},
],
},
{"type": "text", "text": "...", "id": "msg_abc123"},
],
response_metadata={"model_provider": "openai"}
)
message.content_blocks
[{'type': 'reasoning', 'id': 'rs_abc123', 'reasoning': 'summary 1'},
{'type': 'reasoning', 'id': 'rs_abc123', 'reasoning': 'summary 2'},
{'type': 'text', 'text': '...', 'id': 'msg_abc123'}]
Multimodal
Multimodality refers to the ability to work with data that comes in different forms, such as text, audio, images, and video. LangChain includes standard types for these data that can be used across providers. Chat models can accept multimodal data as input and generate it as output. Below we show short examples of input messages featuring multimodal data.
- Image Input
- PDF Document Input
# From URL
message = {
"role": "user",
"content": [
{"type": "text", "text": "Describe the content of this image."},
{"type": "image", "url": "https://example.com/path/to/image.jpg"},
]
}
# From base64 data
message = {
"role": "user",
"content": [
{"type": "text", "text": "Describe the content of this image."},
{
"type": "image",
"base64": "AAAAIGZ0eXBtcDQyAAAAAGlzb21tcDQyAAACAGlzb2...",
"mime_type": "image/jpeg",
},
]
}
# From provider-managed File ID
message = {
"role": "user",
"content": [
{"type": "text", "text": "Describe the content of this image."},
{"type": "image", "file_id": "file-abc123"},
]
}
# From URL
message = {
"role": "user",
"content": [
{"type": "text", "text": "Describe the content of this document."},
{"type": "file", "url": "https://example.com/path/to/document.pdf"},
]
}
# From base64 data
message = {
"role": "user",
"content": [
{"type": "text", "text": "Describe the content of this document."},
{
"type": "file",
"base64": "AAAAIGZ0eXBtcDQyAAAAAGlzb21tcDQyAAACAGlzb2...",
"mime_type": "application/pdf",
},
]
}
# From provider-managed File ID
message = {
"role": "user",
"content": [
{"type": "text", "text": "Describe the content of this document."},
{"type": "file", "file_id": "file-abc123"},
]
}