LangChain LangGraph State

State is the memory of a running graph. Every node reads from it and writes to it, so a good state design makes a graph easy to understand and debug. Poor state design leads to missing data, overwritten values, and confusing bugs. Learn the rules once, and every graph you build becomes simpler.

Shared Whiteboard Analogy

A project team shares one whiteboard. Each member reads what others wrote and adds new notes. The whiteboard holds the full story of the project at any moment. LangGraph state acts as that whiteboard.

 WHITEBOARD (state)
 +------------------------------------------+
 | question:  "Plan a 3-day Jaipur trip"    |
 | places:    ["Amber Fort", "City Palace"] |
 | draft:     "Day 1 ..."                   |
 | approved:  False                         |
 +------------------------------------------+
     ↑ read/write       ↑ read/write
  [Research node]    [Writer node]

Defining State with TypedDict

The simplest state is a TypedDict. Each key has a name and a type.

from typing import TypedDict

class TripState(TypedDict):
    question: str
    places: list
    draft: str
    approved: bool

Pydantic models and dataclasses also work when you want validation.

Default Update Rule: Overwrite

When a node returns a value for a key, the new value replaces the old one. A node that returns {"draft": "Day 1 ..."} overwrites the previous draft. Overwriting suits single values such as a status or a final answer.

Reducers: Combining Instead of Replacing

Some data should accumulate. A chat history grows with every message, and a list of search results grows with every search. A reducer is a rule that tells LangGraph how to merge a new value with the old one.

 Overwrite:  old ["A"] + new ["B"]  →  ["B"]
 Reducer:    old ["A"] + new ["B"]  →  ["A", "B"]

Attach a reducer with Annotated. The function operator.add joins lists.

from typing import Annotated, TypedDict
import operator

class ResearchState(TypedDict):
    topic: str
    findings: Annotated[list, operator.add]

Each node returns a list of new findings, and LangGraph appends them.

def search_news(state: ResearchState):
    return {"findings": ["News: solar demand is rising"]}

def search_papers(state: ResearchState):
    return {"findings": ["Paper: new battery design"]}

The Messages Reducer

Chat apps use a ready-made reducer called add_messages. It appends new messages and updates a message when the id matches. The prebuilt MessagesState class already includes it.

from langgraph.graph import MessagesState, StateGraph, START, END

def chatbot(state: MessagesState):
    reply = model.invoke(state["messages"])
    return {"messages": [reply]}

builder = StateGraph(MessagesState)
builder.add_node("chatbot", chatbot)
builder.add_edge(START, "chatbot")
builder.add_edge("chatbot", END)
graph = builder.compile()

out = graph.invoke({"messages": [{"role": "user", "content": "Hi there!"}]})
print(out["messages"][-1].content)

Adding Your Own Keys to Messages

Extend MessagesState with extra fields when a chat needs more data.

class SupportState(MessagesState):
    customer_id: str
    ticket_open: bool

Input and Output Schemas

Internal state can hold many working keys. Callers usually care about a few. Separate schemas let you accept a small input and return a small output while nodes use the large internal state.

class InputState(TypedDict):
    question: str

class OutputState(TypedDict):
    answer: str

builder = StateGraph(TripState, input_schema=InputState, output_schema=OutputState)

Designing Good State

Store Facts, Not Everything

Save the data later steps need. Skip temporary variables that live inside a single node.

Choose Clear Key Names

Names such as retrieved_docs and final_answer explain themselves.

Keep Values Simple

Text, numbers, lists, and dictionaries save cleanly in checkpoints. Avoid open file handles and live connections.

Pick the Right Update Rule

Choose overwrite for single values and a reducer for growing collections.

Common Mistakes

  • Forgetting a reducer and losing earlier items when two nodes write the same key.
  • Returning the whole state from a node instead of the changed keys.
  • Storing huge documents in state and slowing every checkpoint.

Real-World Scenario

A research assistant collects facts from three sources in parallel: news, papers, and company reports. Each node returns a list of findings. A reducer appends every list into one findings key, and a writer node reads the combined list. Without the reducer, each source would overwrite the previous one, and only the last list would survive. The state design decides whether the team gets one source or all three.

Practice Tasks

  1. Define a state with a text field and a list field using operator.add.
  2. Run two nodes that write to the list and confirm both results remain.
  3. Use MessagesState to build a chatbot that remembers two turns inside one invoke.
  4. Create an input schema and an output schema for a graph and compare the dictionaries.

Key Points

  • State is the shared memory every node reads and writes.
  • TypedDict defines the keys and types.
  • The default update overwrites a key.
  • Reducers such as operator.add and add_messages accumulate values.
  • Input and output schemas hide internal working keys.