← Back to Blog
Guides16 min read

LangGraph Tutorial: Build Stateful Agent Workflows with Python

By AI Agent Tools Team
Share:

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:

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:


  1. Wrap your graph in a FastAPI application

  2. Use PostgreSQL-backed checkpointing for durability

  3. Deploy as a Docker container on Railway, AWS ECS, or Kubernetes

  4. 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:

  1. Research agent with iterative deepening — Use cycles to research, identify gaps, and research more
  2. Customer support workflow — Route to specialist agents with human escalation
  3. Content pipeline — Research → Draft → Review → Publish with conditional approval
  4. 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

  1. LangGraph gives you explicit control over agent workflows. Define exactly how agents connect, loop, and branch.
  2. State management is the killer feature. Typed state that flows through the graph, with automatic merging and checkpointing.
  3. Cycles enable iterative refinement. The ability to loop back is what makes LangGraph agents smarter than single-pass systems.
  4. Human-in-the-loop is built in. Pause at any node for human review, then resume seamlessly.
  5. Start simple. Build a linear graph first, then add branching and cycles as needed.
  6. 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.

No spam. Unsubscribe anytime.

10,000+
Downloads
⭐ 4.8/5
Rating
🔒 Secure
No spam
#LangGraph#stateful agents#agent workflows#LangChain#graph architecture#production AI#tutorial#Python

🔧 Tools Featured in This Article

Ready to get started? Here are the tools we recommend:

🔧

Discover 155+ AI agent tools

Reviewed and compared for your projects

🦞

New to AI agents?

Learn how to run your first agent with OpenClaw

🔄

Not sure which tool to pick?

Compare options or take our quiz

Enjoyed this article?

Get weekly deep dives on AI agent tools, frameworks, and strategies delivered to your inbox.

No spam. Unsubscribe anytime.