LangGraph AI Agents 9 min read

LangGraph Guide 2025: Build Stateful AI Agents with LangChain

LangGraph is the go-to framework for building production-grade stateful AI agents in Python. It models agent behavior as a directed graph — giving you explicit control over state, routing, and memory that pure LLM loops lack.

What Is LangGraph?

LangGraph (released 2024, built by the LangChain team) extends LangChain with a graph execution engine. Instead of linear chains, you define:

  • Nodes — Python functions that read the shared state and return updates
  • Edges — directed connections between nodes (always-on or conditional)
  • State — a typed dictionary shared across all nodes in the graph
  • Checkpoints — snapshots of state persisted to SQLite or Postgres after each step

LangGraph powers the agent loop in tools like LangChain's own products and many production AI systems. It has 10k+ GitHub stars and is used by teams at Elastic, Klarna, and hundreds of startups.

Installation

# Install LangGraph + LangChain core
pip install langgraph langchain-core

# With OpenAI provider
pip install langgraph langchain-openai

# With Anthropic provider
pip install langgraph langchain-anthropic

# SQLite checkpointing (built-in, no extra install needed)
# Postgres checkpointing
pip install langgraph-checkpoint-postgres

StateGraph vs MessageGraph

StateGraph (general purpose)

You define the state shape using a TypedDict. Nodes receive the full state and return a dict of updates. Use this when you need custom fields alongside messages — counters, flags, tool call results, scratchpads.

MessageGraph (chat shortcut)

The state is pre-defined as a list of LangChain messages. Nodes receive and return messages. Use for simple chatbot agents where conversation history is the only state you need.

Quickstart: 3-Node ReAct Agent

A minimal agent that calls an LLM, decides whether to use a tool, calls the tool, then loops back:

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.messages import BaseMessage
import operator

# 1. Define state schema
class AgentState(TypedDict):
    messages: Annotated[list[BaseMessage], operator.add]

# 2. Define the LLM node
model = ChatOpenAI(model="gpt-4o-mini").bind_tools([my_tool])

def call_model(state: AgentState):
    response = model.invoke(state["messages"])
    return {"messages": [response]}

# 3. Routing function
def should_continue(state: AgentState):
    last = state["messages"][-1]
    if last.tool_calls:
        return "tools"
    return "end"

# 4. Build the graph
graph = StateGraph(AgentState)
graph.add_node("agent", call_model)
graph.add_node("tools", ToolNode([my_tool]))

graph.set_entry_point("agent")
graph.add_conditional_edges(
    "agent",
    should_continue,
    {"tools": "tools", "end": END}
)
graph.add_edge("tools", "agent")   # loop back after tool call

app = graph.compile()

# 5. Run
result = app.invoke({"messages": [("user", "What is 42 * 7?")]})
print(result["messages"][-1].content)

Checkpointing & Memory

Checkpointing persists graph state after every node, enabling long-term memory and human-in-the-loop interrupts. Pass a checkpointer when compiling:

from langgraph.checkpoint.memory import MemorySaver
from langgraph.checkpoint.sqlite import SqliteSaver

# In-memory (dev / testing only)
memory = MemorySaver()
app = graph.compile(checkpointer=memory)

# SQLite (local persistence)
with SqliteSaver.from_conn_string("checkpoints.db") as saver:
    app = graph.compile(checkpointer=saver)

# Each run needs a thread_id to scope checkpoints
config = {"configurable": {"thread_id": "user-42-session-1"}}
app.invoke({"messages": [("user", "Hello")]}, config=config)

# Resume the same thread later — LangGraph loads saved state
app.invoke({"messages": [("user", "What did I just say?")]}, config=config)

Human-in-the-Loop Interrupts

LangGraph can pause execution before a node and wait for human input. Use interrupt_before at compile time:

# Pause before the "tools" node to let a human approve
app = graph.compile(
    checkpointer=memory,
    interrupt_before=["tools"]
)

config = {"configurable": {"thread_id": "approval-flow-1"}}

# Run until the interrupt
app.invoke({"messages": [("user", "Delete all temp files")]}, config=config)
# → graph pauses, prints state

# Human reviews, then resumes with None (no new input needed)
app.invoke(None, config=config)

Multi-Agent Supervisor Pattern

For complex workflows, use a supervisor agent that routes between specialized sub-agents:

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# Supervisor decides which worker to call next
supervisor_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a supervisor routing to: {members}. Reply with the next worker."),
    ("human", "{input}")
])
members = ["researcher", "coder", "writer"]
supervisor = supervisor_prompt | ChatOpenAI(model="gpt-4o").with_structured_output(
    {"next": str}
)

# Routing function reads supervisor output
def route(state):
    return state["next"]  # "researcher" | "coder" | "writer" | "FINISH"

LangGraph vs CrewAI vs AutoGen

Factor LangGraph CrewAI AutoGen
Abstraction level Low (explicit graph) High (role-based agents) Medium (conversational)
State management Typed state + checkpointing Built-in memory types Message history
Routing control Full (conditional edges) Sequential / hierarchical Agent decides via chat
Human-in-the-loop First-class interrupt API Limited UserProxyAgent
Production readiness High (streaming, Postgres) Medium Medium
Best for Complex production agents Quick multi-agent prototypes Conversational multi-agent

See also: CrewAI Guide and Claude Code Guide for agent tooling comparisons.

Monitor Your AI Agent Stack

LangGraph agents depend on OpenAI, Anthropic, and other AI APIs. Track uptime for every provider your agents rely on from one dashboard — get instant alerts when any dependency degrades.

Monitor AI API Status Free →