Messages
Messages are LangChain's transport layer — every model call is a list of typed messages in, one message out. This article covers what each message type carries, how content blocks handle multimodal I/O, and how to manage message growth before it blows your context budget.
Quick Reference
- →Four types: SystemMessage, HumanMessage, AIMessage, ToolMessage
- →AIMessage.content can be a string OR a list of dicts — never assume string
- →AIMessage.text is the safe accessor: always returns concatenated text
- →ToolMessage.tool_call_id must match the AIMessage tool call id exactly
- →HumanMessage accepts images, audio, and files via content blocks
- →trim_messages() is the production solution to unbounded context growth
- →LangGraph users: RemoveMessage prunes specific messages from graph state
Message Anatomy
Every model interaction in LangChain is a list of messages passed to model.invoke(). Each message has a role, content, and optional metadata. LangChain maps four roles to typed classes that work across all providers.
| Type | Role | Content | Extra fields |
|---|---|---|---|
| SystemMessage | system | Instructions, persona, constraints | — |
| HumanMessage | user | Text, images, audio, files | — |
| AIMessage | assistant | Text, tool calls, reasoning blocks | tool_calls, usage_metadata |
| ToolMessage | tool | Tool execution result (stringified) | tool_call_id, artifact |
Messages flow through the conversation: System sets context, Human asks, AI reasons, Tools execute
LangChain accepts messages in three equivalent forms. A plain string becomes a HumanMessage. A dict with 'role' and 'content' is OpenAI-compatible. A typed class gives you autocompletion and explicit metadata control.