Messages: a conversation as a list
A LangChain message is a typed object for one turn of a conversation: SystemMessage, HumanMessage and AIMessage each hold one role's words.
Last updated: 27 Sep, 2026 · LangChain 1.4
A chat model reads a list of messages and adds one to the end. A conversation can sit in a plain Python list of dictionaries, but LangChain gives each role its own class so the same list works with any provider.
The message classes
from langchain.messages import SystemMessage, HumanMessage, AIMessage
SystemMessage("how the assistant should behave") # the rules
HumanMessage("what the customer said") # a user turn
AIMessage("what the model replied") # a model turnImporting the message classes
Import the three classes, one per role.
from langchain.messages import AIMessage, HumanMessage, SystemMessageBuilding the conversation
Build the conversation as a list of these objects instead of dictionaries.
conversation = [
SystemMessage("You help customers of a small online shop."),
HumanMessage("Where is my order A17?"),
AIMessage("Order A17 shipped on 3 March."),
]Printing each turn
Loop over it. Each message carries a type naming its role, and text holds its words.
for message in conversation:
print(f"{message.type:<7} {message.text}")Printing a conversation end to end
The same pieces in one file, ready to run.
from langchain.messages import AIMessage, HumanMessage, SystemMessage
conversation = [
SystemMessage("You help customers of a small online shop."),
HumanMessage("Where is my order A17?"),
AIMessage("Order A17 shipped on 3 March."),
]
for message in conversation:
print(f"{message.type:<7} {message.text}")Reading the message fields
- type names the role: a user turn is
human, a model turn isai, the rules turn issystem. - text returns the words of the message, whatever the provider.
- Models and agents also accept the plain dictionary form and convert it to these classes for you.
The dictionary form still works
The same conversation as dictionaries prints much the same way. Each turn has a role and content; the classes above are the typed version of exactly this.
conversation = [
{"role": "system", "content": "You help customers of a small online shop."},
{"role": "user", "content": "Where is my order A17?"},
]
for turn in conversation:
print(f"{turn['role']:<7} {turn['content']}")Printing a message readably
pretty_print shows a message with a header naming its type, which is easier to read once a conversation gets long.
from langchain.messages import HumanMessage
HumanMessage("Where is my order A17?").pretty_print()There is a fourth type, ToolMessage, which carries a tool's result back to the model. It arrives in lesson 6, once there is a tool to produce one.
Plain dict vs message object
| Plain dict | Message object | |
|---|---|---|
| Role field | role: "user" | type: "human" |
| Text field | content | text |
| Works with every provider | Converted first | Yes, directly |
Has pretty_print | No | Yes |
When to use message objects
- Holding a chat history you send to a model on every turn.
- Setting the assistant's behaviour once with a
SystemMessageat the front. - Passing a saved conversation between models without rewriting it.
user as a dict but human as a message, and a model turn is assistant versus ai. Match on type, not the dict role, once you switch to objects.Related
- Previous: Installation and setup
- Next: Content blocks: images and files in a message
- Reference: Messages
- Add a second
HumanMessageasking about order B22 and print the list again. - Call
pretty_print()on theAIMessageand compare its header with the human one. - Print
SystemMessage("x").typeand see which name it uses.
You understood something today that you didn't yesterday.