🔬 DEEP ⭐ 20k+ Python Graph / Pregel
LangGraph 的执行引擎灵感来自 Google 的 Pregel 图计算框架,采用 BSP(Bulk Synchronous Parallel) 超步模型:
StateGraph 是图的定义层——用户通过声明式 API 描述节点、边和状态结构:
class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
"""A graph whose nodes communicate by reading and writing to a shared state."""
def add_node(self, name, func, ...)
def add_edge(self, start, end) # 固定转移
def add_conditional_edges(self, start, path, ...) # 条件路由
def compile(self, checkpointer=..., ...) -> CompiledStateGraph
状态通过 Annotated 类型 + reducer 函数定义——reducer 决定多个并行节点写入同一 key 时的合并策略:
from typing import Annotated
from typing_extensions import TypedDict
import operator
class State(TypedDict):
messages: Annotated[list, add_messages] # reducer: 追加消息
count: Annotated[int, operator.add] # reducer: 求和
name: str # 无 reducer: 最后写入覆盖
messages 时,add_messages reducer 将两者合并而非覆盖——这是 Pregel 并行模型的正确性保证。
def route_by_intent(state):
if "search" in state["messages"][-1].content:
return "search_node"
return "chat_node"
graph.add_conditional_edges("router", route_by_intent)
graph = StateGraph(State)
graph.add_node("think", think_fn)
graph.add_node("act", act_fn)
graph.add_edge("think", "act")
graph.add_edge("act", END)
# 编译:将声明式图定义转换为可执行的 Pregel 实例
compiled = graph.compile(checkpointer=SqliteSaver("db.sqlite"))
# 执行
result = compiled.invoke({"messages": [...]})
# 事件流
for event in compiled.astream({"messages": [...]}, stream_mode="values"):
print(event)
# 支持的 stream_mode:
# "values" — 每步后的完整状态
# "updates" — 每步的状态增量
# "messages" — LLM token 流
# "custom" — 自定义事件
LangGraph 拥有所有框架中最复杂的检查点系统——这是其最大的实用优势:
# 获取线程的所有检查点历史
history = list(graph.get_state_history(config))
# 从特定检查点回放
graph.invoke(
None,
{**config, "configurable": {"checkpoint_id": checkpoint_id}}
)
# 查看特定检查点的状态
state = graph.get_state(config)
print(state.values) # 当前状态
print(state.next) # 下一步要执行的节点
print(state.tasks) # 待执行任务
| 通道类型 | 行为 | 典型用途 |
|---|---|---|
LastValue | 新值覆盖旧值 | 当前用户输入、最新状态 |
BinaryOperatorAggregate | 自定义合并函数 (a,b)→c | 消息列表、计数器 |
EphemeralValue | 不跨步持久化 | 临时计算结果 |
DeltaChannel | 增量更新 | 流式数据 |
NamedBarrierValue | 同步屏障 | 等待所有并行节点完成 |
LastValueAfterFinish | 超步完成后才可读 | 确保一致性读取 |
from langgraph.graph.message import add_messages
def add_messages(left: list, right: list) -> list:
"""智能消息合并:
- 追加新消息
- 按 ID 更新已有消息
- 通过 RemoveMessage 删除消息
"""
...
class Context(TypedDict):
user_id: str
db_conn: Any
graph = StateGraph(state_schema=State, context_schema=Context)
def node(state: State, runtime: Runtime[Context]) -> dict:
user_id = runtime.context["user_id"] # 只读,运行作用域
return {"result": process(state, user_id)}
Command 是 LangGraph 最强大的控制流机制——将多种操作统一为单一对象:
from langgraph.types import Command
# 状态更新 + 路由
Command(update={"key": "value"}, goto="node_name")
# 中断恢复
Command(resume="user_input")
# 上报父图
Command(update={"result": x}, graph=Command.PARENT)
# 组合使用
Command(
update={"approved": True},
goto="execute",
resume=None,
graph=None
)
LangGraph 支持多种多 Agent 协作模式:
add_conditional_edges 路由到工作节点graph.add_conditional_edges(
"supervisor",
lambda s: s["next_agent"]
)
Command(goto="other_agent") 互相交接def transfer_to_b(state):
return Command(goto="agent_b")
parent.add_node("team", sub_graph)
Send() API 实现动态扇出/扇入def route(state):
return [Send("worker", {"t": t})
for t in state["tasks"]]
from langgraph.types import Send
def route_to_workers(state):
"""动态决定并行执行路径——运行时才知道有多少任务"""
return [
Send("worker_node", {"task": task})
for task in state["tasks"]
]
graph.add_conditional_edges("planner", route_to_workers)
# 所有 worker 完成后,结果通过 reducer 合并
# 然后继续执行后续节点
LangGraph 提供了所有框架中最健壮的人机协作实现——基于检查点中断:
graph = builder.compile(
checkpointer=SqliteSaver("db.sqlite"),
interrupt_before=["human_review"], # 在节点执行前暂停
interrupt_after=["ai_response"], # 在节点执行后暂停
)
# 第一步:执行到中断点
result = graph.invoke({"messages": [...]}, config)
# 查看当前状态
state = graph.get_state(config)
print(state.values) # 当前状态
print(state.next) # 下一步: ["human_review"]
# 第二步:人工审查/修改后继续
# 方式1:直接继续
graph.invoke(None, config)
# 方式2:修改状态后继续
graph.invoke(
Command(update={"approved": True}),
config
)
# 方式3:人工修改消息后继续
state = graph.get_state(config)
state.values["messages"][-1].content = "修正后的内容"
graph.invoke(None, config)
LangGraph 是低级图框架——不强制提示模板。图结构本身就是控制流,提示只是节点的实现之一。
SystemPrompt 或 Agno 的 instructions 参数——节点函数内完全自由定义提示。唯一例外是 create_react_agent 辅助函数提供了默认 ReAct 提示。
# 提示完全由节点函数控制
def my_agent_node(state):
system_prompt = """你是一个专业的分析师。
请根据上下文分析用户的问题。"""
response = llm.invoke([
SystemMessage(content=system_prompt),
*state["messages"]
])
return {"messages": [response]}
graph.add_node("agent", my_agent_node)
def error_handler(state, error):
"""节点级错误回调——可以返回状态更新"""
return {"error_message": str(error), "status": "failed"}
graph.add_node(
"risky_node",
risky_func,
error_handler=error_handler # 捕获该节点所有异常
)
from langgraph.pregel import RetryPolicy
graph.add_node(
"api_call",
call_external_api,
retry=RetryPolicy(
max_attempts=3, # 最大重试次数
initial_interval=1.0, # 初始间隔(秒)
backoff_factor=2.0, # 退避因子
max_interval=10.0, # 最大间隔
)
)
# 出错后从上一个检查点恢复
state = graph.get_state(config)
# 修复状态或跳过错误节点
graph.invoke(
Command(goto="fallback_node"),
config
)
| 类 | 职责 | 所在模块 |
|---|---|---|
StateGraph | 图定义构建器——声明节点、边、状态 | graph/state.py |
CompiledStateGraph | 编译后的可执行图——extends Pregel | graph/state.py |
Pregel | 执行引擎——超步循环驱动 | pregel/main.py |
Command | 统一控制流原语——goto/update/resume/escalate | types.py |
Send | 动态并行扇出——运行时创建并行路径 | types.py |
BaseChannel | 通道抽象基类——状态读写的核心接口 | channels/ |
Checkpoint | 状态快照——用于持久化与恢复 | pregel/_checkpoint.py |
BaseCheckpointSaver | 检查点存储抽象——SQLite/Postgres 等实现 | checkpoint/ |
Runtime | 运行时上下文——注入不可变依赖 | graph/state.py |
BranchSpec | 条件边规格——定义路由逻辑 | graph/_branch.py |
StateNodeSpec | 节点规格——函数、重试、错误处理器 | graph/state.py |
其他框架(CrewAI、AutoGen、Agno)使用简单循环或临时路由——并行执行时没有正确性保证。LangGraph 的超步模型确保:同一超步内的节点并行执行,写入通过 reducer 合并,然后同步进入下一超步。这是唯一有形式化执行模型的 Agent 框架。
Checkpoint/Time Travel 系统不只是持久化——它让 HITL、错误恢复、调试和 A/B 测试变得自然。其他框架要么没有检查点,要么只有简单的状态快照。LangGraph 的检查点包含通道版本、待写入和元数据——足以精确恢复任意中间状态。
LangGraph 有最陡的学习曲线和最冗长的 API。StateGraph → compile → Pregel 管线概念沉重,对比 PydanticAI 的 Agent.run() 或 Agno 的 Agent.run()。Command 原语虽强大,但也增加了心智负担。适合需要精确控制流的复杂场景,不适合快速原型。
在所有框架中,Command 是最通用的控制流原语——它统一了状态更新(update)、路由(goto)、中断恢复(resume)和父图上报(graph=PARENT)。其他框架需要不同的 API 完成这些操作,LangGraph 用一个对象搞定。