Skip to content

All lessons

Area 03 // AI AgentsLesson 57 / 14015 min read

LangGraph, state and checkpoints

LangGraph builds agents and workflows as graphs of steps (nodes) connected by edges, with a shared state object that every step reads and updates, and checkpoints that save that state after each step so runs can pause, resume, recover from crashes and be inspected.

Think of it like this

Analogy

A board game has squares (nodes), arrows between them (edges), and a game state (whose turn, everyone's money) that changes as you play. Some squares say "if you rolled a six, go to square 12" (conditional edges). And if dinner is ready, you take a photo of the board so you can set it up exactly the same way later (checkpoints).

LangGraph is that board game for agents.

The problem it solves

Simple agent loops work in memory: if the server restarts, the agent's progress is gone. Long-running agents need to pause for a human's approval (maybe for hours), resume on a different server, recover after a crash, and let developers inspect exactly what state they were in at each step. Complex agents also need branches, loops and multiple agents, which get messy as nested if statements.

How it works: LangGraph

  1. Define the state: a typed dictionary of everything the run needs (messages, the plan, retrieved docs, flags).
  2. Write nodes: plain functions that receive the state and return the fields they want to update. A node can call an LLM, a tool, or plain code.
  3. Connect them with edges: START → write, write → END.
  4. Add conditional edges: a function looks at the state and returns the next node's name, creating branches and loops (retry until good enough, route to a tool node, finish).
  5. Compile the graph (optionally with a checkpointer) and invoke it with an initial state.
  6. Prebuilt pieces exist for the common agent pattern (an LLM node plus a tool node in a loop), and multi-agent graphs (subgraphs, supervisors).
  7. Install: pip install langgraph (version 1.2 as of November 2026). It works with any model provider.

How it works: state and checkpoints

  1. State is the single source of truth: every node reads it and returns updates; LangGraph merges them (with "reducers" for fields like message lists that should append rather than overwrite).
  2. A checkpointer saves a checkpoint of the state after every step. InMemorySaver for development; database-backed savers (Postgres, SQLite, Redis) in production.
  3. Threads: each run or conversation has a thread_id. Invoking again with the same ID continues from the saved state; that's also how per-conversation memory works.
  4. What checkpoints unlock:
    • Resume after failure: restart from the last good step instead of from scratch.
    • Human in the loop: pause at a step, wait for approval, resume later (next lesson).
    • Time travel: inspect every past state (get_state_history) and replay from any point to debug.
  5. Keep state small and serialisable: IDs and summaries rather than huge blobs.
A graph with a loop: write, check, and either retry or finish. Every step is checkpointed.

See it in code

Install
pip install langgraph
Needs
Python 3.10+. Runs offline (no LLM; nodes are plain functions).

A graph that loops until its draft is good enough, with every step saved as a checkpoint you can inspect.

langgraph_loop.py
from typing import TypedDict
 
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import END, START, StateGraph
 
class State(TypedDict):
    draft: str
    attempts: int
 
def write(state: State) -> dict:                       # a node: reads state, returns an update
    n = state["attempts"] + 1
    return {"draft": "short" if n < 2 else "a longer, complete answer", "attempts": n}
 
def good_enough(state: State) -> str:                  # a conditional edge: decides where to go next
    return "done" if len(state["draft"]) > 10 or state["attempts"] >= 3 else "retry"
 
g = StateGraph(State)
g.add_node("write", write)
g.add_edge(START, "write")
g.add_conditional_edges("write", good_enough, {"retry": "write", "done": END})   # a loop
app = g.compile(checkpointer=InMemorySaver())          # saves state after every step
 
config = {"configurable": {"thread_id": "user-7"}}
print("result :", app.invoke({"draft": "", "attempts": 0}, config))
history = list(app.get_state_history(config))          # every saved checkpoint, newest first
print("checkpoints saved:", len(history))
print("oldest -> newest :", [h.values.get("attempts") for h in reversed(history)])
Output
result : {'draft': 'a longer, complete answer', 'attempts': 2}
checkpoints saved: 4
oldest -> newest : [None, 0, 1, 2]

Four checkpoints: before anything ran, the input, after the first write, and after the second. In a real agent the write node calls an LLM and the condition might check a test result or a judge score.

A real example

Example

A loan-application agent that survives restarts

A lender's agent gathers documents, checks them, computes affordability and drafts a decision for an underwriter. Applications take days: customers upload documents over time and underwriters approve the final step.

Built as a LangGraph graph with a Postgres checkpointer and one thread per application, the agent pauses when documents are missing, resumes when new ones arrive (even on a different server after deployments), and pauses again for underwriter approval. When a bug produced a wrong affordability figure, developers loaded the exact checkpoint, fixed the node and replayed from that step.

The easy way

Start here

Model your agent as a few nodes and a conditional edge on paper first. Build it with InMemorySaver and a thread_id, print get_state_history to see every step, then switch to a database checkpointer when you deploy.

When you grow: the production setup

Production LangGraph apps use durable checkpointers (Postgres), keep state small, version graph changes carefully (old threads must still load), trace runs (LangSmith or OpenTelemetry tools like Langfuse), add interrupts for approvals, and evaluate end-to-end trajectories.

Trade-offs

What you gain

  • Explicit control over complex flows: branches, loops, multi-agent structures.
  • Durable state: pause, resume, recover and debug with checkpoints.
  • Works with any model; strong human-in-the-loop support.

What it costs you

  • More concepts and boilerplate than a simple loop.
  • Framework lock-in and upgrade churn.
  • Poorly designed state can grow large and slow.

When not to use it

  • A short, stateless agent or a simple workflow: a 30-line loop is easier to read and maintain.

Common mistakes

  • Using InMemorySaver in production, losing state on restart.
  • Reusing one thread_id for different users or conversations.
  • Storing huge documents in state instead of references.
  • Changing state shapes without considering existing saved threads.
  • Building a framework graph before understanding the plain loop.

Interview questions

Try answering out loud first, then open the model answer.

What is LangGraph and when would you use it?

Model answer

A library for building stateful agents and workflows as graphs: nodes perform steps (LLM calls, tools, code), edges and conditional edges define control flow, a typed state is shared and updated, and checkpointers persist state per thread. I'd use it for long-running, branching or multi-agent systems that need durability, human-in-the-loop pauses, resumability and inspectable state; for simple agents a plain loop is enough.

What do checkpoints give you in an agent system?

Model answer

Fault tolerance (resume from the last completed step), human-in-the-loop (pause indefinitely and resume when input arrives), conversation memory (state per thread), and debuggability (inspect and replay past states, fork from a checkpoint). They require a durable store, serialisable state and care with schema changes.

How would you design state for an agent graph?

Model answer

Keep only what steps need: messages (with an append reducer), the plan and progress, key IDs and small structured results, flags for routing, and references (IDs, file paths) rather than large content. Make it serialisable and versioned, separate per-thread state from shared long-term memory, and avoid storing secrets.

Quiz

Pick an answer to see whether it’s right and why.

  1. 01In LangGraph, what is a node?
  2. 02What does a conditional edge do?
  3. 03What is a checkpoint?
  4. 04Why use a database-backed checkpointer instead of InMemorySaver in production?
  5. 05What does the thread_id in the config identify?

0 of 5 answered

Key terms

LangGraph
A library for building agents and workflows as graphs of nodes and edges with shared state and durable checkpoints.
Graph state
The shared, typed data that every step of a graph reads and updates.
Checkpoint
A saved snapshot of an agent's state after a step, so it can pause, resume, recover from crashes or be inspected.
Node (graph)
One step in a LangGraph-style graph, such as a function, LLM call or tool call, that reads the state and returns updates.
Conditional edge
A connection in a graph that chooses the next step based on the current state, creating branches and loops.

Before and after

Progress is saved in this browser only.