02. StateGraph Basics
Every LangGraph application starts with a TypedDict schema that defines the shape of your application state. This is not ceremonial—it is the contract that every node reads from and writes to. When a node returns a dict, LangGraph performs a deep merge of that dict into the existing state using your schema. If your schema has a messages: list[str] field, returning {"messages": ["hello"]} appends to the list, not replaces it. This behavior is called functional state updates and it is the reason LangGraph works well for multi-turn conversations.
To define the schema:
from typing import TypedDict, Annotated
from operator import add
class AgentState(TypedDict):
messages: Annotated[list[str], add]
current_step: str
artifact: str | None
The Annotated with operator.add tells LangGraph to merge list updates by appending rather than overwriting. Without it, returning a new list from a node replaces the old one. This distinction trips up many operators who then wonder why their message history disappears mid-conversation.
Building the graph follows the same four-step pattern: create the builder, add nodes, connect edges (or the START/END sentinels), compile. The START and END constants are string constants that resolve to "__start__" and "__end__" internally. Adding an edge from "my_node" to END means that node is a terminal node—no outgoing edge from it will be followed.
builder = StateGraph(AgentState)
builder.add_node("process", process_node)
builder.add_edge(START, "process")
builder.add_edge("process", END)
compiled = builder.compile()
One structural nuance: compile() returns a CompiledGraph which is a LangChain Runnable. This means it works with .stream(), .asyncinvoke(), .batch(), and LangChain's built-in callbacks. The graph does not run until you call .invoke() or .stream() on the compiled object.
Define an AgentState with two fields: turns: Annotated[list[str], add] and active_worker: str | None. Build a graph with one node that appends a message and sets active_worker. Inspect the compiled graph's get_input_schema() to confirm your schema appears.