Node 与 Edge 的执行机制——从源码看一次 Superstep 的完整生命周期
如果说第1篇勾勒了 LangGraph 的整体骨架,第2篇就钻进骨架里面去。我们跟随一段代码走进 Pregel 引擎,看一个状态更新是如何在节点间流转的。
📑 本篇目录
01 起点:用户的一次 invoke · 02 PregelLoop 主循环 · 03 节点调度:tick 入栈 · 04 状态写入:Channel.apply · 05 Barrier 同步与快照 · 06 条件路由:Conditional Edge · 07 从源码理解"为什么能循环" · 08 第2篇小结
起点:用户的一次 invoke
LangGraph 一切行为的出发点,是用户调用编译后图的 invoke 或 stream。我们从这里切开。
app = graph.compile()
result = app.invoke({"messages": ["你好"]})
# 背后发生了什么?
compile 返回的是 Pregel 实例,继承自 Runnable。invoke 直接走到 Pregel.invoke → Pregel.stream → 最终进入 PregelLoop。
PregelLoop:表驱动的主循环
PregelLoop 是 LangGraph 的真正核心。它把所有调度逻辑写在一个 while 循环里,但每个步骤都通过"步骤表"(steps table)注册——这就是 LangGraph 设计哲学的浓缩:
数据驱动,而非控制流驱动。每个 Superstep 是「读步骤表 → 执行 → 写步骤表 → barrier」的一次循环。
class PregelLoop:
def loop(self):
while True:
for step in self.step():
step.run()
if step.is_done():
return self.output()
step() 决定了本轮 Superstep 要执行的 Node / Channel write / Conditional edge / Barrier / Done。这是 PregelLoop 最优雅的设计。
节点调度:tick 如何选择下一个节点
每轮 Superstep 开始时,PregelLoop 询问 Channel:
CondExpr step:上一轮的 Conditional Edge 计算结果,决定下一批 activate 的节点 Channels 状态:Channels 自带"本轮是否更新"标记,未更新的节点不重复激活 Task 队列:去重后的本轮待运行节点集
这意味着:节点只在状态真正变化时被调用。Agent 的"思考-行动"循环由此而成立。
状态写入:Channel.apply 是原子操作
所有节点返回的"partial state"都先到一个公共通道:Channel.apply。
def apply_writes(writes: list[ChannelWrite]):
for write in writes:
channel_name = write.target
reducer = channels[channel_name].reducer
new_value = reducer(old_value, write.value)
channels[channel_name].update(new_value)
注意 Reducer 是策略对象:list 用 add、set 用 union、dict 用 merge。这就是「同一 Channel 不同合并策略」的实现机制。
▲ Channel.apply 是所有状态写入的唯一入口
Barrier 同步与快照
一轮 Superstep 结束前会触发 Barrier:
调用 Checkpointer:把当前 Channels 序列化为 Checkpoint 计算下一步激活集合:基于更新后的状态重新评估 Conditional Edge 清空 Pending Writes:本轮的"草稿"要么落盘要么丢弃
Barrier 是循环的天然边界——也是 Time Travel 和错误恢复的实现点。
条件路由:Conditional Edge 源码
Conditional Edge 是 LangGraph 表达 if-else 的地方。它把路由决策从节点代码里抽出来:
class RunnableCallable:
def invoke(self, state):
# 这里的逻辑会在每一轮 Superstep 后被触发
decision = self.cond_func(state)
return decision # 返回一个或多个下一个节点名
LangGraph 支持三种返回值:
从源码理解"为什么 LangGraph 能循环"
答案不在 while 循环,而在 BSP 模型:
Superstep 是离散时间片:每一轮步骤有清晰的开始/结束边界 Channel 是按版本更新的:用"是否本轮已更新"决定下一轮是否重新激活 Conditional Edge 允许指回任意节点:包括指回自己(实现"反思-重试") Barrier 是天然断点:可以选择暂停、保存、恢复
这就是为什么 LangGraph 的"循环"是 LLM 时代最有用的循环:它能被 Checkpointer 持久化、能被 interrupt 中断、能被 inspect 检查。

▲ PregelLoop 的状态演化:从 invoke 到 End,每个箭头都是一个 Superstep
第2篇小结
📌 第2篇小结
PregelLoop:表驱动的主循环,让扩展点全部可插拔 Channel.apply:状态写入的原子操作,所有变更都走这里 Barrier:同步障决定了 Superstep 的边界 Conditional Edge:把"路由"从代码里抽出来,与节点解耦 原因 → 原因:循环的支持来自 BSP 模型而不是语言特性
下篇预告:LangGraph 源码解析系列·第3篇《Channel 协议与 Reducer 模式——源码拆解状态合并的四种策略》,看 LastValue / BinaryOperator / Topic / Ephemeral 各自如何工作。
写在最后
从代码里看 PregetlLoop 只有一个 while,但每个 Superstep 都串起 Node、Channel、Reducer、Conditional Edge、Barrier、Checkpointer —— LangGraph 的所有「feature」其实就是这一段 while 的不同切面。
夜雨聆风