LangGraph Tutorial: Build Stateful Agent Workflows with Python
Table of Contents
- What Is LangGraph and Why Should You Learn It?
- When You Need LangGraph
- Core Concepts
- StateGraph
- Nodes
- Edges
- Building Your First LangGraph Workflow
- Step 1: Define State
- Step 2: Create Node Functions
- Step 3: Build the Graph
- Advanced Patterns
- Human-in-the-Loop
- Parallel Execution
- Persistent State with Checkpointing
- Subgraphs: Modular Agent Design
- LangGraph vs Other Frameworks
- LangGraph vs CrewAI
- LangGraph vs AutoGen
- LangGraph vs OpenAI Agents SDK
- Tool Integration
- Debugging and Monitoring
- LangGraph Studio
- LangSmith Integration
- Open-Source Monitoring
- Production Deployment
- Deploy with LangGraph Platform
- Self-Hosted Deployment
- Scaling Considerations
- What to Build Next
- Key Takeaways
What Is LangGraph and Why Should You Learn It?
LangGraph is a framework for building stateful, graph-based AI agent workflows. While LangChain provides the building blocks for LLM applications, LangGraph gives you explicit control over how those blocks connect, loop, branch, and maintain state.If CrewAI is like hiring a team and letting them figure out the process, LangGraph is like designing the process flowchart yourself. You get more control, more visibility, and the ability to build workflows that no high-level framework supports out of the box.
LangGraph is built and maintained by the LangChain team and has become the go-to framework for production agent workflows that need custom control flow. Real Python describes it as "a versatile Python library for building stateful agents," and IBM has published tutorials on building agentic workflows with it.
When You Need LangGraph
Use LangGraph when you need:
- Cycles: Agents that can loop back and retry or refine their work
- Branching: Different paths based on intermediate results
- Human-in-the-loop: Pause execution for human approval at specific steps
- Complex state: State that evolves across multiple agent steps
- Custom topologies: Workflows that don't fit sequential or hierarchical patterns
Core Concepts
StateGraph
The foundation of LangGraph is the StateGraph — a directed graph where:
- Nodes are functions that process state
- Edges define transitions between nodes
- State is a typed dictionary that flows through the graph
python
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
import operator
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
current_step: str
results: dict
The Annotated[list, operator.add] pattern tells LangGraph how to merge updates — in this case, new messages are appended to the existing list rather than replacing it. This is crucial for multi-step workflows where you want to accumulate information.
Nodes
Nodes are Python functions that receive the current state and return state updates:
python
def research_node(state: AgentState) -> dict:
# Do research using tools
query = state["messages"][-1]
results = search_tool.invoke(query)
return {
"messages": [f"Found {len(results)} results"],
"results": {"search": results},
"currentstep": "researchcomplete"
}
Each node only returns the fields it wants to update. LangGraph handles merging updates into the full state.
Edges
Edges define how nodes connect:
python
Static edge: always go from research to analysis
graph.add_edge("research", "analysis")
Conditional edge: route based on state
def routeafteranalysis(state: AgentState) -> str:
if state["results"].get("confidence", 0) > 0.8:
return "write_report"
else:
return "research" # Loop back for more research
graph.addconditionaledges("analysis", routeafteranalysis)
Conditional edges are what make LangGraph powerful — they enable cycles, branching, and dynamic routing based on intermediate results.
Building Your First LangGraph Workflow
Let's build a research agent that searches, analyzes, and can loop back for more research if needed.
Step 1: Define State
python
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
import operator
class ResearchState(TypedDict):
topic: str
search_queries: list[str]
raw_findings: Annotated[list, operator.add]
analysis: str
gaps: list[str]
iteration: int
final_report: str
Step 2: Create Node Functions
python
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
def plan_searches(state: ResearchState) -> dict:
"""Generate search queries for the research topic."""
resp llm.invoke(
f"Generate 3 search queries to research: {state['topic']}. "
f"Previous gaps to fill: {state.get('gaps', [])}"
)
queries = response.content.strip().split("\n")
return {
"search_queries": queries,
"iteration": state.get("iteration", 0) + 1
}
def execute_searches(state: ResearchState) -> dict:
"""Run search queries and collect results."""
findings = []
for query in state["search_queries"]:
results = tavily.search(query, max_results=3)
findings.append({
"query": query,
"results": results
})
return {"raw_findings": findings}
def analyze_findings(state: ResearchState) -> dict:
"""Analyze findings and identify gaps."""
resp llm.invoke(
f"Analyze these research findings about '{state['topic']}':\n"
f"{state['raw_findings']}\n\n"
f"Provide: 1) Key insights, 2) Gaps in knowledge that need more research"
)
# Parse analysis and gaps from response
return {
"analysis": response.content,
"gaps": extract_gaps(response.content)
}
def write_report(state: ResearchState) -> dict:
"""Write the final research report."""
resp llm.invoke(
f"Write a comprehensive research report on '{state['topic']}' "
f"based on this analysis:\n{state['analysis']}"
)
return {"final_report": response.content}
Step 3: Build the Graph
python
graph = StateGraph(ResearchState)
Add nodes
graph.addnode("plan", plansearches)
graph.addnode("search", executesearches)
graph.addnode("analyze", analyzefindings)
graph.addnode("report", writereport)
Add edges
graph.add_edge(START, "plan")
graph.add_edge("plan", "search")
graph.add_edge("search", "analyze")
Conditional edge: loop back or write report
def should_continue(state: ResearchState) -> str:
if state["gaps"] and state["iteration"] < 3:
return "plan" # Loop back for more research
return "report"
graph.addconditionaledges("analyze", should_continue)
graph.add_edge("report", END)
Compile and run
app = graph.compile()
result = app.invoke({"topic": "AI agent deployment best practices"})
Advanced Patterns
Human-in-the-Loop
LangGraph supports pausing execution for human review using interrupts:
python
from langgraph.checkpoint.memory import MemorySaver
Add a checkpoint saver for persistence
checkpointer = MemorySaver()
def review_node(state: ResearchState) -> dict:
"""This node will pause for human review."""
return {"analysis": state["analysis"]}
graph.addnode("humanreview", review_node)
Compile with interruption before the review node
app = graph.compile(
checkpointer=checkpointer,
interruptbefore=["humanreview"]
)
Run until interrupt
c {"configurable": {"thread_id": "research-1"}}
result = app.invoke({"topic": "AI safety"}, config)
... human reviews and approves ...
Resume execution
result = app.invoke(None, config)
This pattern is essential for production workflows where sensitive outputs need human approval before proceeding — financial decisions, content publication, customer communications.
Parallel Execution
Run independent tasks simultaneously using the Send API:
python
from langgraph.types import Send
def spawn_researchers(state: ResearchState) -> list[Send]:
"""Spawn parallel research for each subtopic."""
return [
Send("research_subtopic", {"subtopic": topic})
for topic in state["subtopics"]
]
graph.addconditionaledges("plan", spawn_researchers)
This pattern dramatically reduces latency for tasks with independent subtasks — like researching multiple aspects of a topic simultaneously.
Persistent State with Checkpointing
For long-running workflows that might fail, persist state at each step:
python
from langgraph.checkpoint.sqlite import SqliteSaver
Use SQLite for durable checkpointing
checkpointer = SqliteSaver.fromconnstring("checkpoints.db")
app = graph.compile(checkpointer=checkpointer)
Each invocation is tied to a thread
c {"configurable": {"thread_id": "research-123"}}
result = app.invoke({"topic": "multi-agent systems"}, config)
If the process crashes mid-execution, you can resume from the last checkpoint instead of starting over. For production deployments, use PostgreSQL-backed checkpointing or Supabase for managed storage.
Subgraphs: Modular Agent Design
Break complex workflows into reusable subgraphs:
python
Define a research subgraph
research_graph = StateGraph(ResearchState)
researchgraph.addnode("search", search_node)
researchgraph.addnode("analyze", analyze_node)
researchgraph.addedge(START, "search")
researchgraph.addedge("search", "analyze")
researchgraph.addedge("analyze", END)
researchsubgraph = researchgraph.compile()
Use the subgraph as a node in a parent graph
parent_graph = StateGraph(ParentState)
parentgraph.addnode("research", research_subgraph)
parentgraph.addnode("write", write_node)
parentgraph.addedge(START, "research")
parentgraph.addedge("research", "write")
parentgraph.addedge("write", END)
Subgraphs enable modular, reusable agent components — build once, use in multiple workflows.
LangGraph vs Other Frameworks
LangGraph vs CrewAI
CrewAI is higher-level and faster to prototype with. Choose CrewAI when you want role-based agent teams with minimal orchestration code. Choose LangGraph when you need custom control flow — cycles, branching, parallel execution, or human-in-the-loop at specific points.LangGraph vs AutoGen
AutoGen excels at conversational multi-agent patterns where agents discuss and debate. Choose AutoGen for multi-agent conversations. Choose LangGraph when you need a deterministic workflow with explicit state management.LangGraph vs OpenAI Agents SDK
OpenAI Agents SDK is lightweight and focused on agent handoffs. Choose it for simple agent chaining. Choose LangGraph for complex workflows with cycles, branching, and persistent state.Tool Integration
LangGraph works with any LangChain-compatible tool:
python
from langchaincommunity.tools.tavilysearch import TavilySearchResults
from langchain_openai import ChatOpenAI
Define tools
search = TavilySearchResults(max_results=5)
tools = [search]
Bind tools to the LLM
llm = ChatOpenAI(model="gpt-4o").bind_tools(tools)
Common tool integrations for LangGraph agents:
- Search: Tavily, Serper, Brave Search API
- Scraping: Firecrawl, Crawl4AI
- Code execution: E2B
- Data: Pinecone, Chroma, Qdrant
Debugging and Monitoring
LangGraph Studio
The visual debugger for LangGraph workflows. See your graph topology, step through executions, inspect state at each node, and rerun from any checkpoint. Available through the LangChain platform.
LangSmith Integration
LangSmith provides production tracing for LangGraph:python
import os
os.environ["LANGCHAINTRACINGV2"] = "true"
os.environ["LANGCHAINAPIKEY"] = "your-api-key"
Every node execution, LLM call, and tool invocation is traced automatically. Invaluable for debugging multi-step workflows.
Open-Source Monitoring
For self-hosted monitoring, use LangFuse or Arize Phoenix — both integrate with LangGraph and provide trace visualization, cost tracking, and quality metrics.
Production Deployment
Deploy with LangGraph Platform
LangChain offers a managed deployment platform for LangGraph applications. It handles scaling, persistence, and API management.
Self-Hosted Deployment
For self-hosted deployments:
- Wrap your graph in a FastAPI application
- Use PostgreSQL-backed checkpointing for durability
- Deploy as a Docker container on Railway, AWS ECS, or Kubernetes
- Monitor with LangSmith or LangFuse
Scaling Considerations
- Stateful nature: LangGraph workflows maintain state, so you need sticky sessions or externalized state storage
- Long-running workflows: Use checkpointing + async execution for workflows that take minutes to complete
- Concurrent users: Each user gets their own thread and state — ensure your state backend handles the concurrency
What to Build Next
Now that you understand LangGraph fundamentals:
- Research agent with iterative deepening — Use cycles to research, identify gaps, and research more
- Customer support workflow — Route to specialist agents with human escalation
- Content pipeline — Research → Draft → Review → Publish with conditional approval
- Data processing pipeline — Extract → Transform → Validate → Load with error handling
Combine LangGraph with CrewAI for the best of both worlds — use CrewAI's high-level agent definitions within LangGraph's precise workflow control.
Key Takeaways
- LangGraph gives you explicit control over agent workflows. Define exactly how agents connect, loop, and branch.
- State management is the killer feature. Typed state that flows through the graph, with automatic merging and checkpointing.
- Cycles enable iterative refinement. The ability to loop back is what makes LangGraph agents smarter than single-pass systems.
- Human-in-the-loop is built in. Pause at any node for human review, then resume seamlessly.
- Start simple. Build a linear graph first, then add branching and cycles as needed.
- Monitor everything. Use LangSmith or LangFuse to trace executions and catch issues early.
Master AI Agent Building
Get our comprehensive guide to building, deploying, and scaling AI agents for your business.
What you'll get:
- 📖Step-by-step setup instructions for 10+ agent platforms
- 📖Pre-built templates for sales, support, and research agents
- 📖Cost optimization strategies to reduce API spend by 50%
Get Instant Access
Join our newsletter and get this guide delivered to your inbox immediately.
We'll send you the download link instantly. Unsubscribe anytime.
🔧 Tools Featured in This Article
Ready to get started? Here are the tools we recommend:
LangGraph
Graph-based stateful orchestration runtime for agent loops.
LangChain
Toolkit for composing LLM apps, chains, and agents.
LangSmith
Tracing, evaluation, and observability for LLM apps and agents.
Tavily
Search API designed specifically for LLM and agent use.
Enjoyed this article?
Get weekly deep dives on AI agent tools, frameworks, and strategies delivered to your inbox.