LangGraphLangGraph 1.2 · Python 3.10+
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
37 small wins to finish your pathNext lesson

The messages list

Put that list in the state and one problem appears immediately. A node that returns a message would replace the whole conversation with it.

You already know the fix. In lesson 10 two nodes wanted to add to a list, and a reducer combined the values instead of replacing them. A conversation has exactly the same shape, so LangGraph ships a reducer written for it.

Example
from langchain_core.messages import HumanMessage, AIMessage
from langgraph.graph import add_messages

so_far = [HumanMessage("I was charged twice")]
new = [AIMessage("Thanks, I will look into that.")]

for m in add_messages(so_far, new):
    print(f"{m.type:<6} {m.content}")

Two messages out, from a list of one and a list of one. A node returns only the message it produced, and add_messages puts it on the end of the conversation.

Using it in a state

It goes in the same place operator.add went.

python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import add_messages

class State(TypedDict):
    messages: Annotated[list, add_messages]

You will write that so often that LangGraph gives you it ready made. MessagesState is a TypedDict holding exactly that one annotated key, so you can hand it straight to StateGraph and get the same behaviour.

Example
from langchain_core.messages import HumanMessage, AIMessage
from langgraph.graph import StateGraph, START, MessagesState

def reply(state):
    return {"messages": [AIMessage("Thanks, I will look into that.")]}

builder = StateGraph(MessagesState)
builder.add_node("reply", reply)
builder.add_edge(START, "reply")

result = builder.compile().invoke({"messages": [HumanMessage("I was charged twice")]})
for m in result["messages"]:
    print(f"{m.type:<6} {m.content}")

Use MessagesState when messages are all you need, and write the Annotated line yourself when your state has other keys too. Most real graphs do both, by inheriting from MessagesState and adding keys.

add_messages is not quite operator.add
It is smarter than joining two lists. Each message carries an id, and if a new message has the same id as one already there, it updates that message instead of adding a duplicate. That matters once tools are involved and a message gets filled in over time.
Try it yourself
  • Swap add_messages for a plain list in the state and watch the human message vanish.
  • Add a ticket key alongside messages by inheriting from MessagesState.
  • Call add_messages with two lists of two and count what comes out.
PreviousMessages

Slow is fine. Stopping is the only problem.