LangGraph models an agent as a state graph — explicit nodes (functions), edges (transitions), and a shared state object passed between them. It's the deterministic-control answer to "agents are too unreliable".
Core concepts
from langgraph.graph import StateGraph, END
from typing import TypedDict, List
class State(TypedDict):
query: str
docs: List[str]
answer: str
iters: int
def retrieve(state): ...
def grade(state): ...
def rewrite(state): ...
def generate(state): ...
g = StateGraph(State)
g.add_node("retrieve", retrieve)
g.add_node("grade", grade)
g.add_node("rewrite", rewrite)
g.add_node("generate", generate)
g.set_entry_point("retrieve")
g.add_edge("retrieve", "grade")
g.add_conditional_edges(
"grade",
lambda s: "rewrite" if not s["docs"] and s["iters"] < 3 else "generate",
{"rewrite": "rewrite", "generate": "generate"},
)
g.add_edge("rewrite", "retrieve")
g.add_edge("generate", END)
app = g.compile()
Why this beats raw ReAct in production
- Explicit termination. No "the model decides when to stop" — you write the conditional that ends the loop.
- Resumability. State is serializable; a graph can be paused and resumed (e.g., human approval mid-run).
- Observability. Every node's input/output is naturally traceable.
- Composability. Subgraphs become reusable — a "retrieve+grade" subgraph plugs into bigger workflows.
- Determinism where you want it. Some nodes are pure functions; only LLM-decision nodes are non-deterministic.
Patterns that drop out naturally
- Loop-with-grader (CRAG): retrieve → grade → rewrite/generate.
- Plan-execute: planner node emits a plan, executor node runs each step.
- Reflect: generate → reflect → revise.
- Branching: classifier node routes to specialist subgraphs.
- Human-in-the-loop: pause node waits for human input via webhook or UI.
Concrete pitfalls
- State mutation — pass clean diffs; mutating shared state across nodes hides bugs.
- Unbounded loops — always include an
itersfield and cap it in your conditional. - Massive state objects — keep state lean; persist intermediate large blobs externally.
- Logging drift — log node inputs/outputs to LangSmith / LangFuse for trajectory eval.
When NOT LangGraph
- The workflow is a straight pipeline → LangChain LCEL or even raw functions.
- You need fine-grained streaming guarantees per token → use raw API + your own router.
- Your team doesn't run Python; LangGraph.js is younger and rougher.
Comparable alternatives
- CrewAI — agent-roles abstraction; opinionated, less flexible.
- AutoGen (Microsoft) — multi-agent conversational; great for research, less for prod.
- DSPy — programmatic LM optimization; orthogonal to LangGraph but composes.
- OpenAI Swarm / Agents SDK — minimal multi-agent for OpenAI ecosystem.
- Custom state machines — for high-stakes systems, often the right answer.