LangGraph Tutorial: Build Your First AI Agent (2026)
LangGraph lets you build AI agents as state machines — with cycles, persistence, and human-in-the-loop control — using Python. If your agent needs to loop, retry, or pause for approval, LangGraph is the framework to reach for. This tutorial takes you from zero to a working agent in under 30 minutes.
Who this is for:
- Junior engineers: You want to build your first agent beyond a simple prompt chain
- Senior engineers: You need stateful, production-grade agent orchestration with checkpointing
Real-World Problem Context
Section titled “Real-World Problem Context”Most AI applications start as simple chains: take input, call LLM, return output. That works until it doesn’t.
Here’s where single-pass chains break down:
| Scenario | What Breaks | What You Need |
|---|---|---|
| Research agent that searches, evaluates, then searches again | Chain can’t loop back | Cycles in the graph |
| Customer support bot that escalates to human | Chain can’t pause mid-execution | Interrupts and checkpointing |
| Code generation agent that runs tests and retries | Chain runs once and stops | Conditional routing with retry logic |
| Multi-step workflow that crashes at step 5 of 8 | Restarts from scratch | State persistence via checkpointers |
“How do I build a LangGraph agent that can retry and loop?” — this is the #1 question in the LangChain Discord, and it’s exactly what this tutorial solves.
70% of production agent failures trace back to insufficient state management. LangGraph addresses this by treating every agent as a graph with explicit state, not a chain with implicit flow.
How LangGraph Works
Section titled “How LangGraph Works”Think of LangGraph like a subway map. Each station is a node (a function that does work). The tracks between stations are edges (connections that route data). Some junctions have switches that send the train down different tracks depending on conditions — those are conditional edges.
The train itself? That’s your state — a typed dictionary that every node can read and update as it passes through.
The Three Building Blocks
Section titled “The Three Building Blocks”-
State — A
TypedDictorMessagesStatethat holds all data flowing through the graph. Every node receives the full state and returns updates to it. -
Nodes — Python functions that do actual work: call an LLM, execute a tool, format output. Each node takes state in and returns state updates.
-
Edges — Connections between nodes. Regular edges always go A → B. Conditional edges evaluate a function and route to different nodes based on the result.
Unlike LangChain chains that flow in one direction, LangGraph graphs can have cycles — a node can route back to a previous node. This is what enables retry loops, multi-turn conversations, and iterative refinement.
Step-by-Step: Build a Tool-Calling Agent
Section titled “Step-by-Step: Build a Tool-Calling Agent”You’ll build a ReAct-style agent that can call tools, evaluate results, and decide whether to call more tools or respond to the user.
Step 1: Install LangGraph
Section titled “Step 1: Install LangGraph”pip install langgraph langchain-openaiStep 2: Define Your State
Section titled “Step 2: Define Your State”The state schema determines what data flows through your graph. MessagesState is the built-in schema for chat agents — it manages a list of messages automatically.
from langgraph.graph import MessagesStateThat’s it. MessagesState gives you a messages key that automatically appends new messages rather than overwriting them. For custom state, you’d define a TypedDict with your own fields.
Step 3: Create Your Nodes
Section titled “Step 3: Create Your Nodes”Every node is a plain Python function. It takes state, does work, and returns state updates.
from langchain_openai import ChatOpenAIfrom langgraph.graph import MessagesState, END
# Initialize the LLM with tool bindingllm = ChatOpenAI(model="gpt-4o")
# Define tools the agent can usefrom langchain_core.tools import tool
@tooldef search_web(query: str) -> str: """Search the web for current information.""" # Replace with your actual search implementation return f"Search results for: {query}"
@tooldef calculate(expression: str) -> str: """Evaluate a math expression.""" return str(eval(expression)) # Use a safe evaluator in production
# Bind tools to the LLMtools = [search_web, calculate]llm_with_tools = llm.bind_tools(tools)
def call_llm(state: MessagesState): """Node that calls the LLM — may or may not request tool use.""" response = llm_with_tools.invoke(state["messages"]) return {"messages": [response]}
def call_tool(state: MessagesState): """Node that executes tool calls from the LLM response.""" from langchain_core.messages import ToolMessage
last_message = state["messages"][-1] results = [] for tool_call in last_message.tool_calls: # Look up the tool by name and invoke it tool_fn = {t.name: t for t in tools}[tool_call["name"]] result = tool_fn.invoke(tool_call["args"]) results.append( ToolMessage(content=result, tool_call_id=tool_call["id"]) ) return {"messages": results}Step 4: Define the Routing Logic
Section titled “Step 4: Define the Routing Logic”The conditional edge decides whether the agent should call a tool or stop and respond.
from typing import Literal
def should_continue(state: MessagesState) -> Literal["call_tool", END]: """Route based on whether the LLM wants to use a tool.""" last_message = state["messages"][-1] if last_message.tool_calls: return "call_tool" return ENDStep 5: Build and Compile the Graph
Section titled “Step 5: Build and Compile the Graph”Now wire everything together:
from langgraph.graph import StateGraph, START
# Create the graphbuilder = StateGraph(MessagesState)
# Add nodesbuilder.add_node("call_llm", call_llm)builder.add_node("call_tool", call_tool)
# Add edgesbuilder.add_edge(START, "call_llm") # Start by calling the LLMbuilder.add_conditional_edges( "call_llm", should_continue, ["call_tool", END] # Route to tool or finish)builder.add_edge("call_tool", "call_llm") # After tool, go back to LLM
# Compileagent = builder.compile()Step 6: Run the Agent
Section titled “Step 6: Run the Agent”from langchain_core.messages import HumanMessage
result = agent.invoke({ "messages": [HumanMessage(content="What is 25 * 48?")]})
for msg in result["messages"]: print(f"{msg.type}: {msg.content}")The agent will: (1) receive the question, (2) decide to call the calculate tool, (3) get the result, (4) formulate a natural language response.
LangGraph Architecture and Design
Section titled “LangGraph Architecture and Design”A LangGraph agent executes as a superstep loop: the LLM node decides whether to call a tool or return a final answer, with the checkpointer persisting state after every step.
📊 How the Agent Execution Loop Works
Section titled “📊 How the Agent Execution Loop Works”LangGraph ReAct Agent — Execution Flow
State flows through nodes; conditional edges create the tool-calling loop
📊 The LangGraph Runtime Stack
Section titled “📊 The LangGraph Runtime Stack”LangGraph Architecture Layers
From your agent code down to the execution runtime
Each execution tick is called a superstep. In one superstep, a node runs and returns state updates. The runtime applies those updates, evaluates edges, and routes to the next node. If a checkpointer is configured, state is persisted after every superstep — so crashes don’t lose progress.
LangGraph Code Examples in Python
Section titled “LangGraph Code Examples in Python”These two patterns — checkpointing for durable execution and human-in-the-loop interrupts — are what separate a production LangGraph agent from a prototype.
Adding State Persistence
Section titled “Adding State Persistence”Here’s what makes LangGraph production-ready — durable execution with checkpointing:
import sqlite3from langgraph.checkpoint.sqlite import SqliteSaver
# Create a persistent checkpointercheckpointer = SqliteSaver(sqlite3.connect("agent_state.db"))
# Compile with checkpointing enabledagent = builder.compile(checkpointer=checkpointer)
# Each conversation gets a unique thread_idconfig = {"configurable": {"thread_id": "user-123"}}
# First messageresult = agent.invoke( {"messages": [HumanMessage(content="Search for LangGraph pricing")]}, config=config)
# Later — continue the same conversation (state is restored)result = agent.invoke( {"messages": [HumanMessage(content="Now compare it with CrewAI")]}, config=config)The thread_id uniquely identifies a conversation. If your process crashes between supersteps, the agent resumes from the last checkpoint — not from scratch.
Adding Human-in-the-Loop
Section titled “Adding Human-in-the-Loop”Use interrupt to pause the graph and wait for human approval:
from langgraph.types import interrupt
@tooldef send_email(to: str, subject: str, body: str) -> str: """Send an email — requires human approval.""" # This pauses the graph until a human resumes it response = interrupt({ "action": "send_email", "to": to, "subject": subject, "message": "Approve sending this email?" })
if response.get("approved"): return f"Email sent to {to}" return "Email cancelled by user"This pattern is critical for any agent that takes real-world actions — sending emails, making API calls, or modifying databases.
Trade-offs and Common Pitfalls
Section titled “Trade-offs and Common Pitfalls”LangGraph is the right tool when you need cycles, persistence, or conditional routing — but it adds boilerplate cost that simple linear chains do not justify.
LangGraph vs LangChain Chains
Section titled “LangGraph vs LangChain Chains”📊 Visual Explanation
Section titled “📊 Visual Explanation”LangGraph vs Simple LangChain Chains
- Fast to prototype — 5 lines for a RAG chain
- No boilerplate — just pipe operators
- Cannot loop or retry
- No built-in persistence
- No human-in-the-loop support
- Full control over execution flow
- Built-in checkpointing and resumption
- Human-in-the-loop via interrupts
- More code — state schema + edge definitions
- Steeper learning curve for simple tasks
Where Engineers Get Burned
Section titled “Where Engineers Get Burned”Other common mistakes:
- Forgetting
thread_idin config — Without it, every invocation starts fresh. Your “conversational” agent has no memory. - Mutating state directly — Nodes should return state updates, not modify state in place. LangGraph applies updates immutably between supersteps.
- Infinite loops — If your conditional edge never routes to
END, the agent loops forever. Always add a maximum iteration guard or timeout. - Not handling tool errors — If a tool raises an exception, the graph crashes. Wrap tool calls in try/except and return error messages as
ToolMessagecontent.
Interview Questions and Answers
Section titled “Interview Questions and Answers”These four questions test whether you understand LangGraph’s state machine model and when to reach for it over a simpler LangChain pipeline.
Q1: “What is LangGraph and how does it differ from LangChain?”
Section titled “Q1: “What is LangGraph and how does it differ from LangChain?””What they’re testing: Do you understand the fundamental architecture difference, or do you think LangGraph is just “LangChain 2.0”?
Strong answer: “LangChain is a pipeline framework — data flows linearly from step to step. LangGraph is a state machine framework — it models agents as directed graphs with typed state, conditional edges, and cycles. They’re complementary: LangChain components work inside LangGraph nodes.”
Weak answer: “LangGraph is the newer version of LangChain.”
Q2: “When would you use LangGraph over a simple chain?”
Section titled “Q2: “When would you use LangGraph over a simple chain?””What they’re testing: Can you identify the architectural trigger, not just parrot features?
Strong answer: “I reach for LangGraph when the workflow has cycles — like a ReAct agent that loops between thinking and acting — or when I need durable execution that survives process restarts. For a one-shot RAG pipeline, LangChain is the right choice.”
Q3: “How does LangGraph handle state persistence?”
Section titled “Q3: “How does LangGraph handle state persistence?””What they’re testing: Do you understand checkpointing at an implementation level?
Strong answer: “LangGraph uses checkpointers that implement BaseCheckpointSaver. After every superstep, the full state is serialized and saved. You configure a thread_id to identify each conversation. If the process crashes, calling invoke with the same thread_id resumes from the last checkpoint, not from the beginning.”
Q4: “How would you add human approval to a LangGraph agent?”
Section titled “Q4: “How would you add human approval to a LangGraph agent?””What they’re testing: Production readiness — can you build agents that don’t run unsupervised?
Strong answer: “Use the interrupt() function inside a tool. When the graph hits an interrupt, it pauses and persists state. The human reviews the pending action, then the graph resumes with Command(resume=...) containing the approval or modification.”
Running Agents in Production
Section titled “Running Agents in Production”At scale, you’ll see these patterns in production LangGraph deployments:
State management: Teams use PostgreSQL checkpointers (not SQLite) for concurrent multi-user agents. Each user gets a unique thread_id, and the checkpointer handles concurrent writes.
Observability: Integrate LangSmith for trace visualization. Every superstep, node execution, and state transition becomes a traceable span. Without this, debugging a 15-step agent workflow is nearly impossible.
Cost control: Add a max_iterations counter to your state. Increment it in the LLM node and check it in the conditional edge. Production agents need hard limits — an uncapped ReAct loop can burn through your API budget in minutes.
Deployment: LangGraph Cloud provides managed infrastructure for deploying stateful agents. Alternatively, you can self-host with FastAPI + Redis checkpointer for custom deployments.
For more on agent architecture patterns and how they map to LangGraph implementations, see our agentic design patterns guide.
Summary and Key Takeaways
Section titled “Summary and Key Takeaways”- LangGraph models agents as state machines — nodes do work, edges route between them, and state flows through the entire graph
- Three building blocks: State (typed data), Nodes (Python functions), Edges (routing logic including conditional edges)
- Cycles are the key differentiator — unlike chains, LangGraph graphs can loop, enabling ReAct agents, retry logic, and iterative refinement
- Checkpointers enable durable execution — state persists across process restarts via SQLite, PostgreSQL, or Redis
- Human-in-the-loop via interrupts — pause the graph, get human approval, resume with modifications
- Start simple — use LangChain LCEL for linear pipelines, migrate to LangGraph only when you need cycles or persistence
- Always set
thread_idand add iteration limits to prevent runaway loops in production
Related
Section titled “Related”- LangChain vs LangGraph — Key Differences — When to use each framework
- LangChain Tutorial — Getting Started — Build your first LangChain chain
- AI Agents — Agent architectures and design patterns
- Agentic Design Patterns — ReAct, Plan-and-Execute, and more
- Agentic Frameworks Compared — LangGraph vs CrewAI vs AutoGen
Frequently Asked Questions
What is LangGraph and how does it differ from LangChain?
LangGraph is a framework for building AI agents as state machines with cycles, persistence, and human-in-the-loop control. Unlike LangChain chains that flow in one direction, LangGraph graphs can have cycles — a node can route back to a previous node, enabling retry loops, multi-turn conversations, and iterative refinement.
What are the core building blocks of a LangGraph agent?
LangGraph has three core building blocks. State is a TypedDict or MessagesState that holds all data flowing through the graph. Nodes are Python functions that do actual work like calling an LLM or executing a tool. Edges are connections between nodes — regular edges always go A to B, while conditional edges evaluate a function and route to different nodes based on the result.
When should I use LangGraph instead of a simple LangChain chain?
Use LangGraph when your agent needs to loop back (retry logic, iterative refinement), pause mid-execution for human approval, persist state across crashes via checkpointers, or handle conditional routing based on intermediate results. If your workflow is a straight pipeline with no cycles or interrupts, a simple chain is sufficient.
How do I add tools to a LangGraph agent?
Define tools as Python functions decorated with @tool from langchain_core.tools, then bind them to the LLM using llm.bind_tools(tools). Create a call_tool node that executes tool calls from the LLM response, and add a conditional edge (should_continue) that routes to the tool node when the LLM requests tool use or to END when it responds directly. The tool node routes back to the LLM node, creating the ReAct loop.
What is a superstep in LangGraph?
A superstep is one execution tick in a LangGraph agent. In one superstep, a node runs and returns state updates. The runtime applies those updates, evaluates edges, and routes to the next node. If a checkpointer is configured, state is persisted after every superstep, so crashes do not lose progress.
How does checkpointing work in LangGraph?
LangGraph uses checkpointers that implement BaseCheckpointSaver to persist state. You compile the graph with a checkpointer (SQLite, PostgreSQL, or Redis) and pass a unique thread_id in the config for each conversation. After every superstep, the full state is serialized and saved. If the process crashes, invoking with the same thread_id resumes from the last checkpoint rather than starting over.
How do I add human-in-the-loop approval to a LangGraph agent?
Use the interrupt() function inside a tool to pause the graph and wait for human approval. When the graph hits an interrupt, it persists state and halts execution. The human reviews the pending action, then the graph resumes with Command(resume=...) containing the approval or modification. This pattern is critical for agents that take real-world actions like sending emails or modifying databases.
What is MessagesState in LangGraph?
MessagesState is a built-in state schema for chat agents in LangGraph. It provides a messages key that automatically appends new messages rather than overwriting them. This makes it the standard choice for conversational agents where you need to maintain message history. For custom state beyond chat messages, you define your own TypedDict with additional fields.
How do conditional edges work in LangGraph?
Conditional edges evaluate a Python function against the current state and route to different nodes based on the result. For example, a should_continue function checks whether the LLM response contains tool_calls. If it does, the edge routes to the tool node; if not, it routes to END. This is what creates the ReAct loop where the agent alternates between thinking and acting.
What are common mistakes when building LangGraph agents?
Common mistakes include forgetting to set thread_id in config so every invocation starts fresh with no memory, mutating state directly instead of returning state updates, creating infinite loops by never routing to END without a maximum iteration guard, and not handling tool errors with try/except blocks. Wrapping tool calls in error handling and adding iteration limits are essential for production agents.
Last updated: February 2026 | LangGraph v0.3+ / Python 3.10+