Quick Answer: LangGraph is a graph-based Python framework for building stateful multi-agent workflows. Install with
pip install langgraph langchain-openai, define aStateGraph, add nodes and edges, compile, and run.
Most agent frameworks break when your workflow needs a loop, a branch, or a human approval gate. LangGraph is built specifically for these cases. It's the framework behind production systems at Klarna (which cut support resolution time by 80%), Uber, and LinkedIn — and with the release of LangGraph 1.1.2, it's now the most mature option for building stateful multi-agent systems.
This tutorial covers everything you need to build your first multi-agent LangGraph workflow from scratch: state design, nodes, conditional routing, persistence, and human-in-the-loop. If you want to go beyond a single-agent chatbot and build something that actually orchestrates multiple models — using tools like cowork.ink to coordinate teams of agents — LangGraph is where to start.
What Is LangGraph?
LangGraph is an open-source (MIT license) agent orchestration framework that represents your workflow as a directed graph. Each node in the graph is a Python function. Each edge defines how execution flows between nodes — including conditional edges that route based on logic, not just sequence.
This graph-based model is what separates LangGraph from sequential frameworks. LangChain runs chains top-to-bottom — if you're new to the LangChain ecosystem, our LangChain tutorial covers the fundamentals. LangGraph supports cycles — an agent can reason, call a tool, check the result, and loop back until it's satisfied. That's how real agents work.
LangGraph 1.0 was the first stable major release. Version 1.1.0 (March 2026) added fully type-safe streaming with StreamPart objects. Trusted in production by Klarna, Uber, LinkedIn, Coinbase, and Cloudflare.
Core Concepts You Need to Know
Before writing any code, understand these five building blocks. Everything else in LangGraph follows from them.
State
State is a Python TypedDict (or Pydantic model) that every node reads from and writes to. It's the shared scratchpad for your entire workflow. Annotated fields control how updates are merged — for example, using operator.add to append messages to a list instead of replacing them.
from typing import Annotated
from typing_extensions import TypedDict
import operator
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
next_step: str
result: str
Nodes
Nodes are plain Python functions that take the current state and return a partial update. They can call LLMs, invoke tools, query databases, or run any arbitrary logic.
def my_node(state: AgentState):
# do something with state
return {"result": "computed value"}
Edges
Edges define how execution flows between nodes. A regular edge always goes from A to B. A conditional edge routes based on a function's return value — this is the key to loops and dynamic branching.
# Always go from node_a to node_b
graph.add_edge("node_a", "node_b")
# Go to "tools" or END based on state
graph.add_conditional_edges(
"agent",
lambda state: "tools" if state.get("needs_tool") else "end",
{"tools": "tools_node", "end": END}
)
Checkpointing (Persistence)
A checkpointer saves the full graph state after every node execution. This is what enables pause/resume, fault tolerance, long-running workflows, and human-in-the-loop. MemorySaver is perfect for development; production uses PostgreSQL or Redis backends.
Threads
A thread_id identifies an isolated execution context — a conversation session. Each thread maintains its own state history. This lets you run one graph for thousands of users in parallel, each with their own memory.
Installation & Setup
pip install langgraph langchain-openai
For LangSmith observability (optional but recommended in production):
pip install langsmith
export LANGCHAIN_API_KEY="ls__your_key"
export LANGCHAIN_TRACING_V2="true"
Set your LLM key:
export OPENAI_API_KEY="sk-your_key"
LangGraph also has a JavaScript/TypeScript SDK:
npm install @langchain/langgraph @langchain/openai
Build a Multi-Agent Workflow Step by Step
Let's build a Supervisor + Researcher + Summarizer workflow. A supervisor routes the task to the right specialist agent. The researcher gathers information, then the summarizer distills it into a final answer. This is the canonical multi-agent pattern.
Step 1: Define the State
from typing import Annotated, Literal
from typing_extensions import TypedDict
import operator
from langchain_core.messages import HumanMessage, AIMessage
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
next_step: str
research_result: str
final_summary: str
Step 2: Define the Nodes
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o", temperature=0)
def supervisor(state: AgentState):
"""Routes the task to the right agent."""
task = state["messages"][-1].content
response = llm.invoke([
HumanMessage(content=(
f"Given this task: '{task}'\n"
"Reply with only 'research' or 'summarize'."
))
])
return {"next_step": response.content.strip().lower()}
def researcher(state: AgentState):
"""Gathers information on the topic."""
query = state["messages"][-1].content
# In production: call a search tool, RAG pipeline, or API here
response = llm.invoke([
HumanMessage(content=f"Research this topic thoroughly: {query}")
])
return {
"research_result": response.content,
"messages": [response],
}
def summarizer(state: AgentState):
"""Produces a concise final answer."""
content = state.get("research_result") or state["messages"][-1].content
response = llm.invoke([
HumanMessage(content=f"Summarize this into 3 key points:\n\n{content}")
])
return {
"final_summary": response.content,
"messages": [response],
}
Step 3: Build and Compile the Graph
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
def route(state: AgentState) -> Literal["researcher", "summarizer"]:
step = state.get("next_step", "research")
return "researcher" if step == "research" else "summarizer"
# Build
builder = StateGraph(AgentState)
builder.add_node("supervisor", supervisor)
builder.add_node("researcher", researcher)
builder.add_node("summarizer", summarizer)
# Wire edges
builder.add_edge(START, "supervisor")
builder.add_conditional_edges("supervisor", route, {
"researcher": "researcher",
"summarizer": "summarizer",
})
builder.add_edge("researcher", "summarizer")
builder.add_edge("summarizer", END)
# Compile with in-memory persistence
memory = MemorySaver()
graph = builder.compile(checkpointer=memory)
Step 4: Run the Graph
config = {"configurable": {"thread_id": "session_001"}}
result = graph.invoke(
{"messages": [HumanMessage(content="What are the key features of LangGraph 1.1?")]},
config=config,
)
print(result["final_summary"])
Because you used MemorySaver with a thread_id, the full state history is preserved. Run a second message with the same thread_id and the graph picks up exactly where it left off.
Call graph.get_graph().print_ascii() to see a text diagram of nodes and edges in your terminal. In LangGraph Studio, you get a live interactive visualization.
Add Human-in-the-Loop
One of LangGraph's standout features is first-class human approval. Use interrupt() inside any node to pause execution, let a human review or modify the state, then resume.
from langgraph.types import interrupt
def researcher_with_approval(state: AgentState):
query = state["messages"][-1].content
response = llm.invoke([HumanMessage(content=f"Research: {query}")])
# Pause here — surface the result to a human for review
human_feedback = interrupt({
"draft": response.content,
"instruction": "Review the research draft. Approve or edit it."
})
# human_feedback contains whatever the human sent back
approved_content = human_feedback.get("approved_content", response.content)
return {"research_result": approved_content, "messages": [response]}
To resume after a human provides feedback:
# Resume the paused graph with human input
graph.invoke(
{"messages": [HumanMessage(content="Looks good, proceed.")]},
config=config,
)
This pattern is critical for regulated industries, content moderation, and any workflow where an AI acting alone carries meaningful risk. See our guide on human-in-the-loop vs. human-on-the-loop for when to use each approach.
Streaming Agent Output
LangGraph 1.1.0 introduced fully type-safe streaming. Stream tokens from the LLM as they generate, or stream state updates node-by-node:
# Stream full state updates (node-level)
for chunk in graph.stream(
{"messages": [HumanMessage(content="Research LangGraph streaming")]},
config=config,
stream_mode="updates",
):
print(chunk)
# Stream LLM tokens (token-level) — requires LangChain LLM
for chunk in graph.stream(
{"messages": [HumanMessage(content="Summarize the above")]},
config=config,
stream_mode="messages",
):
if chunk[1].get("langgraph_node") == "summarizer":
print(chunk[0].content, end="", flush=True)
LangGraph vs. LangChain vs. CrewAI
Choosing the right framework depends on your use case. Here's how the three most popular options compare:
| Dimension | LangGraph | LangChain | CrewAI |
|---|---|---|---|
| Architecture | Graph (nodes + edges) | Sequential chains | Role-based crews |
| Loops & cycles | Native | Not natively | Limited |
| State management | Explicit TypedDict | Chain context | Internal task context |
| Human-in-the-loop | First-class interrupt() | Limited | Limited |
| Built-in persistence | Yes (checkpointers) | No | No |
| Debugging | Excellent (full state at each step) | Moderate | Poor |
| Learning curve | Steeper | Moderate | Shallow |
| Best for | Complex branching, production agents | RAG pipelines, rapid prototyping | Simple role-based MVPs |
| LangChain dependency | Independent (compatible) | N/A | Independent |
Bottom line: Use LangGraph when you need real conditional logic, loops, or persistence. Use CrewAI for simple sequential role-based tasks. For a deeper comparison of multi-agent frameworks, see our AG2 vs. CrewAI vs. LangGraph breakdown.
Production Patterns
Once your graph works locally, three changes make it production-ready:
Swap MemorySaver for a Database Checkpointer
# PostgreSQL (production)
from langgraph.checkpoint.postgres import PostgresSaver
conn_string = "postgresql://user:pass@host:5432/db"
checkpointer = PostgresSaver.from_conn_string(conn_string)
graph = builder.compile(checkpointer=checkpointer)
Add Observability
Connect to LangSmith for full trace visibility into which nodes ran, what state looked like at each step, and where latency spikes occurred. Set LANGCHAIN_TRACING_V2=true and every graph run is automatically traced.
For a broader look at monitoring agents in production, see our guide on AI agent observability.
Design for Failure
LangGraph checkpoints state after every super-step. If a node crashes mid-run, you can resume from the last successful checkpoint — no data lost, no re-running expensive upstream nodes.
# Resume a failed run from its last checkpoint
graph.invoke(None, config=config) # None = continue from checkpoint
LangGraph Studio: The Visual Agent IDE
LangGraph Studio is the official development and debugging environment for LangGraph applications. It connects directly to your local graph and gives you:
- Visual graph editor — see nodes and edges as a live diagram
- Step-through debugging — pause at any node and inspect the full state
- Time travel — fork execution from any prior checkpoint and replay with different inputs
- Hot reload — code changes are detected automatically; re-run nodes without restarting
- Chat Mode — test your agent from a user's perspective without the developer scaffolding
If you've ever tried to debug a multi-agent workflow by printing state to the console, Studio makes that entire workflow unnecessary.
When to Use LangGraph (and When Not To)
LangGraph is the right choice when:
- Your workflow needs loops — an agent retries until a condition is met
- You need conditional branching — different paths based on LLM output or state
- You need persistence — conversations that survive across sessions or server restarts
- You need human approval gates — pause and resume with human input
- You're building multi-agent systems — supervisor routing tasks to specialist agents
It's overkill when:
- You need a simple single-turn prompt — use the LLM SDK directly
- You're building a linear RAG pipeline — LangChain or LlamaIndex is simpler
- You need an MVP in an afternoon — CrewAI is faster to get started
For teams coordinating multiple agents across a shared workspace, cowork.ink provides the collaborative layer on top — shared agent context, team permissions, and workflow visibility without the infrastructure overhead.
Get Started with LangGraph
LangGraph has become the production standard for multi-agent orchestration because it gives you explicit control over exactly what your agents do and why. The learning curve is real, but so is the payoff: you get full visibility into state at every step, built-in persistence, and the tools to build agents that loop, branch, and collaborate the way real workflows demand.
Start with the example above. Add your own nodes. Connect a real tool. Then add a checkpointer and a thread_id, and you have a production-grade stateful agent.
Ready to coordinate multiple agents across your team? Try cowork.ink — shared agent workspace, zero prompt gymnastics, built for engineering teams.