02. StateGraph Basics

Chapter 2 of 18 · 15 min

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.

EXERCISE

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.