What is LangGraph?
Why LangGraph Matters
The Problem: Linear agent chains break down when you need branching logic, cycles, persistent state, or human approval steps in your workflows.
The Solution: LangGraph models agent workflows as stateful graphs where nodes are computation steps and edges define control flow, enabling complex multi-step processes with built-in persistence.
Real Impact: LangGraph powers production agent systems at enterprises needing reliable, resumable, and auditable AI workflows.
Real-World Analogy
Think of LangGraph as a subway system map:
- Nodes (Stations) = Processing steps where work happens
- Edges (Tracks) = Connections defining which station comes next
- Conditional Edges = Switch tracks based on conditions (like transfer stations)
- State = Your ticket that carries information between stations
- Checkpoints = Saved positions so you can resume your journey
Core LangGraph Concepts
StateGraph
The main class for defining graph-based workflows. Each graph has a defined state schema and a set of nodes connected by edges.
Nodes
Python functions that receive the current state, perform computation, and return state updates. Each node represents one step in the workflow.
Conditional Edges
Dynamic routing that evaluates state to determine which node to execute next, enabling branching and looping patterns.
Checkpointing
Built-in persistence that saves graph state after each node, enabling resume, replay, and human-in-the-loop interactions.
Graph Concepts
Building Graphs
from typing import TypedDict, Annotated, Sequence
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import BaseMessage, HumanMessage
import operator
# Define the state schema
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
next_action: str
# Define node functions
def agent_node(state: AgentState) -> AgentState:
llm = ChatOpenAI(model="gpt-4")
response = llm.invoke(state["messages"])
return {"messages": [response], "next_action": "decide"}
def tool_node(state: AgentState) -> AgentState:
# Execute tool calls from the last message
last_msg = state["messages"][-1]
result = execute_tools(last_msg.tool_calls)
return {"messages": [result]}
# Define routing logic
def should_continue(state: AgentState) -> str:
last_msg = state["messages"][-1]
if last_msg.tool_calls:
return "tools"
return "end"
# Build the graph
graph = StateGraph(AgentState)
graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_continue, {
"tools": "tools",
"end": END,
})
graph.add_edge("tools", "agent")
# Compile and run
app = graph.compile()
result = app.invoke({
"messages": [HumanMessage(content="What is 25 * 4?")]
})
State Management
State Reducers
- operator.add: Appends new values to existing list (for messages)
- Default: Overwrites the previous value with the new one
- Custom: Write your own reducer function for complex merge logic
- Annotated: Use Python's Annotated type to attach reducers to fields
from langgraph.checkpoint.sqlite import SqliteSaver
# Add persistence with checkpointing
memory = SqliteSaver.from_conn_string(":memory:")
app = graph.compile(checkpointer=memory)
# Run with a thread ID for persistence
config = {"configurable": {"thread_id": "user-123"}}
# First interaction
result1 = app.invoke(
{"messages": [HumanMessage(content="Hi, my name is Alice")]},
config
)
# Second interaction - remembers context!
result2 = app.invoke(
{"messages": [HumanMessage(content="What's my name?")]},
config
)
# Agent responds: "Your name is Alice"
# Human-in-the-loop: interrupt before a node
app_with_interrupt = graph.compile(
checkpointer=memory,
interrupt_before=["tools"] # Pause before tool execution
)
Checkpointing
Common Pitfall
Problem: State grows unbounded as messages accumulate, leading to token limit errors.
Solution: Implement state trimming in your nodes. Use a custom reducer that keeps only the last N messages, or summarize older messages before adding new ones.
| Feature | LangChain AgentExecutor | LangGraph |
|---|---|---|
| Control Flow | Linear loop | Arbitrary graph with cycles |
| State | Messages only | Custom typed state schema |
| Persistence | Manual memory | Built-in checkpointing |
| Human-in-Loop | Not built-in | interrupt_before / interrupt_after |
| Branching | Limited | Conditional edges with routing |
Quick Reference
Essential LangGraph API
| Function | Description | Example |
|---|---|---|
StateGraph() |
Create a state graph | StateGraph(AgentState) |
.add_node() |
Add a node | graph.add_node("name", func) |
.add_edge() |
Add a fixed edge | graph.add_edge("a", "b") |
.add_conditional_edges() |
Add routing edge | graph.add_conditional_edges("a", fn, map) |
.set_entry_point() |
Set start node | graph.set_entry_point("agent") |
.compile() |
Compile the graph | app = graph.compile(checkpointer=mem) |
interrupt_before |
Pause before node | compile(interrupt_before=["tools"]) |