Course Content
LangGraph Agents
7 sections · 49 lessons
What makes LangGraph “stateful,” and why is state central to its design?
What you need to know
1import operator2from typing import Annotated, TypedDict3from langgraph.graph.message import add_messages45class ClaimState(TypedDict):6 messages: Annotated[list, add_messages] # append, match by message id7 documents: Annotated[list, operator.add] # concatenate lists8 fraud_score: float # no reducer: overwrite9 attempts: intA node that returns {"fraud_score": 0.12} changes only that key. A node that returns {"documents": ["bill.pdf"]} adds to the list instead of replacing it, because that key has operator.add as its reducer.
Why the design is built on state
- Loops need memory. A revise loop must know how many times it has run; that counter lives in state.
- Routing is data. A conditional edge reads
state["fraud_score"]and picks the next node. The decision is visible in the saved state, not hidden in Python control flow. - Persistence saves state. After every super-step the checkpointer serialises the whole state. That is what lets a run pause for a human, recover after a crash, or be replayed from an earlier step.
- Nodes stay simple. A node is a plain function from state to an update. It can be unit-tested with a dictionary.
What should not go in state
State is saved to a database many times per run. Keep it small and serialisable: store a document id, not a 20 MB PDF; never store database connections, API clients or secrets. Pass those through the runtime context or config instead.
A real-life example
A health insurer's claim workflow runs over three days: intake, document check, fraud scoring, a human adjuster's decision, then payout. The state holds claim_id, a list of document ids, fraud_score, adjuster_decision and status. On day one the run pauses at the adjuster step. On day three a different server loads the saved state by thread_id="claim-88213", sees fraud_score = 0.12 and the three document ids, and continues with the payout node. Nothing had to be recomputed, because everything the next step needed was in state, not in a process's memory.
Follow-up questions to expect
- "Can the state be a Pydantic model?" — Yes.
TypedDict, dataclasses and Pydantic models all work. Pydantic validates input at runtime but is slower;TypedDictis the common default. - "What happens if a node returns a key that is not in the schema?" — In LangGraph 1.x it is silently dropped, with no error. So a typo such as
fraud_socreloses data quietly; unit tests on node outputs catch it. - "Where do secrets and clients go?" — In the runtime context (
context_schema) or config, never in state, because state is written to the checkpoint store.