把问题改写成可验证系统:LangGraph 不是『又一个 Agent 封装』,而是把控制流从代码里的 if/while 提升为一等公民的数据结构。控制流变成图,就能被打印、被检查、被单步执行、被存档回放——这正是『黑盒 Agent』最缺的东西。
它解决什么问题:从单次调用到有状态多步
一个 LLM 应用的复杂度阶梯通常是:单次 prompt → 固定多步链(LCEL)→ 带工具的 Agent 循环 → 多智能体协作。前两级 LangChain 的 LCEL(prompt | llm | parser)就够了,它表达的是一条单向无环管道(DAG):数据从左流到右,不回头。
但 Agent 的本质是循环:调 LLM → 决定调哪个工具 → 执行工具 → 把结果喂回 LLM → 再决定…… 直到 LLM 说『不用工具了,这是答案』。这是一个带条件退出的 while 循环。LCEL 没法画环,于是早期 LangChain 用 AgentExecutor 把这个循环硬编码进框架内部——能跑,但它是个黑盒:你看不到中间状态、没法在某一步暂停、出错时也难以从断点续跑。LangGraph 就是来拆这个黑盒的。
| 维度 | LangChain LCEL | AgentExecutor | LangGraph |
|---|---|---|---|
| 控制流形态 | 单向无环 DAG | 内置黑盒 while 循环 | 显式有向图,可带环、可分支 |
| 能否循环 | 不能 | 能(但不可见) | 能,且控制流可打印可调试 |
| 状态管理 | 无(流过即弃) | 隐式 scratchpad | 显式 State + reducer 累积 |
| 人工干预/断点 | 不支持 | 几乎不支持 | 原生 interrupt + Checkpointer 恢复 |
| 持久化/会话 | 需自己实现 | 需自己实现 | thread_id + Checkpointer 开箱即用 |
| 多智能体 | 拼链很别扭 | 不擅长 | supervisor / swarm / subgraph 一等支持 |
架构全景:四大支柱组成可循环计算图
- State(状态):整张图共享的一份数据(通常是
TypedDict)。每个节点读它、返回对它的更新。更新如何合并由 reducer 决定——比如add_messages让消息列表追加而非覆盖。这是图的『记忆』。 - Node(节点):一个普通 Python 函数(或 Runnable),签名
(state) -> dict。它拿到当前 State,做一件事(调 LLM、跑工具、写库),返回一个『要更新哪些字段』的字典。只做一件事,是图里的计算步。 - Edge(边):控制流。普通边
a -> b表示『a 跑完跑 b』;条件边(conditional edge)则根据 State 动态决定下一步去哪个节点——这就是 Agent『还要不要再调工具』的判断落点,也是图能成环的关键。 - Checkpointer(检查点):在每个超步(superstep)后把 State 快照存下来,按
thread_id归档。它带来三件事:会话持久化(重启不丢)、断点恢复(interrupt 后接着跑)、时间旅行(回到任一历史状态重放)。
最小心智模型:五步构图(可运行)
下面这段是 LangGraph 最小但完整可运行的骨架,不依赖任何 LLM——它演示固定的五步心智模型:定义 State → 加 Node → 连 Edge → compile → invoke。先把这套流程刻进肌肉记忆,后面所有复杂图都是它的展开。
# LangGraph 主包;langgraph-checkpoint-sqlite 提供 SQLite 持久化
pip install -U langgraph langgraph-checkpoint-sqlite
from typing import TypedDict, Annotated
import operator
from langgraph.graph import StateGraph, START, END
# 1) 定义 State:整张图共享的数据结构
# Annotated[type, reducer] 指定该字段的合并方式。
# 这里用 operator.add 让 steps 列表「追加」而非「覆盖」。
class State(TypedDict):
value: int # 普通字段:默认后写覆盖前值
steps: Annotated[list[str], operator.add] # 带 reducer:每个节点的更新会被拼接
# 2) 定义 Node:函数签名 (state) -> dict,返回「要更新哪些字段」
def add_one(state: State) -> dict:
return {"value": state["value"] + 1, "steps": ["add_one"]}
def double(state: State) -> dict:
return {"value": state["value"] * 2, "steps": ["double"]}
# 3) 建图并加节点、连边
builder = StateGraph(State)
builder.add_node("add_one", add_one)
builder.add_node("double", double)
builder.add_edge(START, "add_one") # 入口 -> add_one
builder.add_edge("add_one", "double") # add_one -> double
builder.add_edge("double", END) # double -> 终点
# 4) compile:把蓝图编译成可执行图(此处可挂 checkpointer,见后续章节)
graph = builder.compile()
# 5) invoke:传入初始 State,跑完整张图,拿到最终 State
result = graph.invoke({"value": 1, "steps": []})
print(result)
# {'value': 4, 'steps': ['add_one', 'double']}
# value: (1+1)*2 = 4;steps 因 reducer 被追加成完整轨迹
把上面的 value 换成 messages: Annotated[list, add_messages]、把节点换成『调 LLM』和『跑工具』、再把 add_one -> double 的普通边换成一条根据 LLM 输出决定『去工具节点还是去 END』的条件边,你就得到了一个真正的 ReAct Agent。整本 wiki 都在沿这条主线展开。
# 真实场景的 30 秒预览:官方 prebuilt 一行起一个带工具的 ReAct Agent
# pip install -U langchain-openai
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
def get_weather(city: str) -> str:
"""查询某城市天气。""" # docstring 会作为工具描述喂给 LLM
return f"{city} 今天晴,26C"
agent = create_react_agent(
model=ChatOpenAI(model="gpt-4o-mini"), # 任意 LangChain ChatModel
tools=[get_weather], # 普通函数即可作为工具
)
# 输入/输出都走 messages:这正是上面 State 心智模型的真实形态
result = agent.invoke(
{"messages": [{"role": "user", "content": "北京天气怎么样?"}]}
)
print(result["messages"][-1].content)
# create_react_agent 内部就是「LLM 节点 + 工具节点 + 条件边」编译成的 StateGraph
✓推荐做法
- 需求里出现『循环』『重试』『分支判断』『多个 Agent 接力』『跑到一半要人工确认』时,直接上 LangGraph
- 先用 TypedDict 把 State 想清楚(有哪些字段、各自怎么合并),再动手加节点
- 对话/Agent 一律带 add_messages reducer 管理 messages,别手动拼 list
- 前期用 create_react_agent 这类 prebuilt 快速验证,复杂控制流再下沉到手写 StateGraph
✗不推荐
- 不要为一条确定性的『prompt -> llm -> parser』管道硬上 LangGraph,那是 LCEL 的活
- 不要把状态藏在节点的全局变量/闭包里——状态必须进 State,否则 Checkpointer 存不到、恢复不了
- 不要在节点函数里直接修改传入的 state 对象,应当 return 一个『更新字典』让 reducer 合并
- 不要一上来就写多智能体大图,先把单 Agent 的环跑通
⚠常见误区
- 把 LangGraph 当成 AgentExecutor 的语法糖——它是更底层的图运行时,心智模型完全不同
- 忘了 reducer:默认字段是覆盖语义,消息列表会被后一个节点冲掉,表现为『上下文丢失』
- compile 后才想起要持久化——Checkpointer 是在 compile(checkpointer=...) 时挂载的
能用『State / Node / Edge / Checkpointer』四个词解释清楚你要建的图,并说出哪条边是条件边、为什么这张图会成环——就算掌握了本章。
全书学习路径
- 安装与环境配置:装对包(langgraph + checkpoint backend),跑通 hello graph
- 核心概念:图、State、Node、Edge 的精确定义与 START/END
- 快速上手 StateGraph:从零搭第一个真正有用的图
- State 与 Reducer:Annotated、add_messages,搞懂状态合并
- 条件边与路由:动态控制流,让图学会『分支与成环』
- Checkpointer 持久化:thread_id 会话、断点恢复、时间旅行
- 流式输出与 HITL:values/updates/messages 三种流模式 + interrupt 中断恢复
- 多智能体、子图与 prebuilt:supervisor、swarm、subgraph、create_react_agent
- 踩坑与生产部署:把图安全地送上线
瓶颈是验证,不是生成。把 Agent 的控制流画成可检查、可存档、可单步的图,你才真正拥有了它——而不是被一个黑盒 while 循环拥有。
— 本 Wiki 编写原则