LangGraph Cheat Sheet: StateGraph, Routing, Memory, and Human-in-the-Loop
Introduction
LangGraph builds agents and workflows as graphs of steps that share state. Use this cheat sheet when you need the core API at a glance. Confirm details in the official LangGraph documentation for your installed version.
Prerequisites
- Python 3.10 or newer
- pip install -U langgraph
Step-by-Step Guide
Step 1: A minimal graph
Define a state type, write nodes that return partial updates, connect them from START to END, and compile.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
text: str
def shout(state: State) -> dict:
return {"text": state["text"].upper()}
def add_exclamation(state: State) -> dict:
return {"text": state["text"] + "!"}
builder = StateGraph(State)
builder.add_node("shout", shout)
builder.add_node("add_exclamation", add_exclamation)
builder.add_edge(START, "shout")
builder.add_edge("shout", "add_exclamation")
builder.add_edge("add_exclamation", END)
graph = builder.compile()
print(graph.invoke({"text": "hello"}))Step 2: State and reducers
By default a node's returned value replaces the field. A reducer such as add merges instead. MessagesState holds a messages list with the add_messages reducer for chat history.
from typing import Annotated
from operator import add
from typing_extensions import TypedDict
from langgraph.graph import MessagesState
class State(TypedDict):
count: int # overwritten each time
log: Annotated[list, add] # appended each time
class ChatState(MessagesState): # messages + your own fields
user_name: strStep 3: Conditional routing and Command
add_conditional_edges calls a routing function after a node and follows the name it returns. A node can instead return Command to update state and choose the next node at once.
def route(state: State) -> str:
return "math" if state["category"] == "math" else "chat"
builder.add_conditional_edges("classify", route, {"math": "math", "chat": "chat"})
from langgraph.types import Command
def triage(state: State) -> Command:
goto = "urgent" if "urgent" in state["text"] else "normal"
return Command(update={"answer": "triaged"}, goto=goto)Step 4: The agent loop by hand
The model node and the tool node loop until the model stops asking for tools. This is what create_agent builds for you.
builder = StateGraph(MessagesState)
builder.add_node("llm_call", llm_call)
builder.add_node("tool_node", tool_node)
builder.add_edge(START, "llm_call")
builder.add_conditional_edges("llm_call", should_continue, ["tool_node", END])
builder.add_edge("tool_node", "llm_call")
agent = builder.compile()Step 5: Memory with checkpointers
Compile with a checkpointer and pass a thread_id in the config. Use a database-backed checkpointer such as SqliteSaver in production instead of InMemorySaver.
from langgraph.checkpoint.memory import InMemorySaver
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "thread-1"}}
graph.invoke({"messages": [{"role": "user", "content": "Hi, I'm Ada."}]}, config)
snapshot = graph.get_state(config) # inspect saved stateStep 6: Human-in-the-loop with interrupt
interrupt pauses the graph and saves state. Resume with Command(resume=...) on the same thread. The node restarts from its beginning on resume, so make earlier side effects safe to repeat.
from langgraph.types import interrupt, Command
def approval(state):
decision = interrupt({"question": "Approve this refund?", "amount": state["amount"]})
return {"approved": bool(decision)}
result = graph.invoke({"amount": 50}, config)
print(result["__interrupt__"][0].value) # show this to the person
graph.invoke(Command(resume=True), config) # continue after approvalStep 7: Streaming
Use stream instead of invoke. The mode decides what you receive.
for chunk in graph.stream(inputs, stream_mode="updates"): # per-node updates
print(chunk)
for state in graph.stream(inputs, stream_mode="values"): # full state each step
print(state)
for token, metadata in graph.stream(inputs, stream_mode="messages"): # LLM tokens
print(token.content, end="")
for mode, data in graph.stream(inputs, stream_mode=["updates", "messages"]):
...FAQs
- LangGraph or LangChain create_agent?
- Use create_agent for a standard tool-calling loop. Use LangGraph when you need custom control flow: specific steps, branches, loops, approval points, parallel work, or durable state.
Summary
Define state, add nodes and edges, compile, then add reducers, conditional edges, a checkpointer with thread_id, interrupt for approvals, and stream modes as your workflow grows.
Related tutorials
- Linux Command Line Cheat Sheet: Files, Permissions, Processes, and Networking
- Docker and kubectl Cheat Sheet: Containers, Images, Compose, and Kubernetes
- SQL Cheat Sheet: SELECT, Joins, Aggregates, and Data Changes
- Git Cheat Sheet: Everyday Commands, Branching, and Undoing Mistakes
- Langflow Cheat Sheet: Install, Components, API Calls, and Custom Components
Comments
Log in or create a free account to join the discussion.