LangChainLangChain 1.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
46 small wins to finish your pathNext lesson →

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

python
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 turn

Importing the message classes

Import the three classes, one per role.

python
from langchain.messages import AIMessage, HumanMessage, SystemMessage

Building the conversation

Build the conversation as a list of these objects instead of dictionaries.

python
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.

python
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.

Example
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 is ai, the rules turn is system.
  • 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.

Example
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.

Example
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 dictMessage object
Role fieldrole: "user"type: "human"
Text fieldcontenttext
Works with every providerConverted firstYes, directly
Has pretty_printNoYes

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 SystemMessage at the front.
  • Passing a saved conversation between models without rewriting it.
Watch out. The role name and the type name differ: a user turn is 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.
Try it yourself
  • Add a second HumanMessage asking about order B22 and print the list again.
  • Call pretty_print() on the AIMessage and compare its header with the human one.
  • Print SystemMessage("x").type and see which name it uses.

You understood something today that you didn't yesterday.