LangGraph Tutorial: Build Multi-Agent Workflows Step-by-Step

Build multi-agent AI workflows with LangGraph 1.1. Tutorial covers StateGraph, nodes, edges, checkpointing & human-in-the-loop.

Quick Answer: LangGraph is a graph-based Python framework for building stateful multi-agent workflows. Install with pip install langgraph langchain-openai, define a StateGraph, 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.1 is production-ready

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.

Visualize your graph

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:

DimensionLangGraphLangChainCrewAI
ArchitectureGraph (nodes + edges)Sequential chainsRole-based crews
Loops & cyclesNativeNot nativelyLimited
State managementExplicit TypedDictChain contextInternal task context
Human-in-the-loopFirst-class interrupt()LimitedLimited
Built-in persistenceYes (checkpointers)NoNo
DebuggingExcellent (full state at each step)ModeratePoor
Learning curveSteeperModerateShallow
Best forComplex branching, production agentsRAG pipelines, rapid prototypingSimple role-based MVPs
LangChain dependencyIndependent (compatible)N/AIndependent

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.

Frequently Asked Questions

What is LangGraph used for?
LangGraph is used for building stateful, multi-agent AI workflows where you need complex branching logic, loops, persistence, and human-in-the-loop controls. It powers production systems at Klarna (85M users), Uber, LinkedIn, and Coinbase. If your agent needs to retry, route dynamically, or pause for human approval, LangGraph is the right tool.
Is LangGraph the same as LangChain?
No. LangChain is a higher-level framework for building LLM pipelines using sequential chains. LangGraph is a separate, lower-level framework for graph-based agent orchestration. They are complementary — LangGraph handles workflow structure and state routing, while LangChain components handle LLM calls and tool integrations. LangGraph 1.0 can also be used completely independently.
Do I need to know LangChain to use LangGraph?
No. LangGraph 1.0 is standalone and works with any LLM provider — OpenAI, Anthropic, Google, or open-source models. Familiarity with Python and basic async programming is sufficient to get started.
What is a StateGraph in LangGraph?
A StateGraph is the central abstraction in LangGraph. It defines the entire workflow as a directed graph — nodes are Python functions that process state, and edges define how execution flows between them. You compile the StateGraph at the end to produce a runnable graph object.
How does LangGraph handle memory between sessions?
LangGraph uses checkpointers to persist the full graph state after every node execution. In-memory checkpointing works with MemorySaver for development. For production, you use database-backed checkpointers (PostgreSQL, SQLite, Redis). Each conversation is isolated by a thread_id, so different users maintain separate, persistent state histories.
Home Blog Company