Messages and conversation history

LangChain · Essentials · 10 min

Messages are the universal currency between your code, models, tools and agents.

Chat models consume a list of messages, each with a role:

  • SystemMessage: instructions that set behaviour ("You are a concise assistant").
  • HumanMessage: user input.
  • AIMessage: model output, possibly containing tool_calls.
  • ToolMessage: the result of a tool call, linked to it by tool_call_id.

You can also use plain dicts ({"role": "user", "content": "..."}) or ("human", "...") tuples; LangChain converts them.

Models are stateless: they only see the messages you send. To have a conversation you resend the history each time:

history = [SystemMessage("Be brief."), HumanMessage("Hi, I'm Ana.")]
ai = model.invoke(history)
history += [ai, HumanMessage("What's my name?")]

Content can be multimodal: a list of content blocks such as text plus an image URL or base64 data, if the model supports it.

Long histories cost tokens and eventually exceed the context window. Utilities in langchain_core.messages help: trim_messages keeps the most recent messages within a token budget, filter_messages selects by type or name, and count_tokens_approximately estimates size. Agents and LangGraph manage history for you with checkpointers (later lessons), but the underlying representation is always this message list.

Example

from langchain_core.messages import (AIMessage, HumanMessage, SystemMessage,
                                     trim_messages)
from langchain_core.messages.utils import count_tokens_approximately

history = [
    SystemMessage("You are a helpful assistant. Be brief."),
    HumanMessage("Hi, I'm Ana."),
    AIMessage("Hello Ana! How can I help?"),
    HumanMessage("What's my name?"),
]
# reply = model.invoke(history)  -> AIMessage("Your name is Ana.")

print([m.type for m in history])     # ['system', 'human', 'ai', 'human']

as_dicts = [{"role": "user", "content": "Hello"}]   # also accepted by models

recent = trim_messages(history, max_tokens=30, strategy="last",
                       token_counter=count_tokens_approximately,
                       include_system=True, start_on="human")
print([m.type for m in recent])      # ['system', 'human']  (oldest turns dropped)

Remember

  • System, human, AI and tool messages carry roles; dicts and tuples also work
  • Models are stateless; send the history you want them to see
  • Trim long histories with trim_messages to stay within the context window

Watch out for

  • Appending only the user's messages to history and forgetting the AI replies, which confuses the model about the conversation.

Check yourself

  1. Why does a chat model forget the user's name in the next call?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.langchain.com/oss/python/langchain/messages