CTCaliberTek
Cheat Sheets

LangGraph Cheat Sheet: StateGraph, Routing, Memory, and Human-in-the-Loop

Published October 14, 2026·Updated October 29, 2026·8 min read
Like SaveLog in to like, save, and track progress.

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: str

Step 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 state

Step 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 approval

Step 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

Comments

Log in or create a free account to join the discussion.

  • No comments yet. Be the first to comment.