乐于分享
好东西不私藏

LangGraph 源码解析系列·第2篇:Node 与 Edge 的执行机制——从看一次 Superstep 的完整生命周期

LangGraph 源码解析系列·第2篇:Node 与 Edge 的执行机制——从看一次 Superstep 的完整生命周期
LANGGRAPH 源码解析系列 · 第2篇

Node 与 Edge 的执行机制——从源码看一次 Superstep 的完整生命周期

如果说第1篇勾勒了 LangGraph 的整体骨架,第2篇就钻进骨架里面去。我们跟随一段代码走进 Pregel 引擎,看一个状态更新是如何在节点间流转的。

全系列 6 篇 · 本篇为核心

📑 本篇目录

01 起点:用户的一次 invoke · 02 PregelLoop 主循环 · 03 节点调度:tick 入栈 · 04 状态写入:Channel.apply · 05 Barrier 同步与快照 · 06 条件路由:Conditional Edge · 07 从源码理解"为什么能循环" · 08 第2篇小结

01

起点:用户的一次 invoke

LangGraph 一切行为的出发点,是用户调用编译后图的 invoke 或 stream。我们从这里切开。

...python

app = graph.compile()

result = app.invoke({"messages": ["你好"]})

# 背后发生了什么?

compile 返回的是 Pregel 实例,继承自 Runnable。invoke 直接走到 Pregel.invoke → Pregel.stream → 最终进入 PregelLoop。

02

PregelLoop:表驱动的主循环

PregelLoop 是 LangGraph 的真正核心。它把所有调度逻辑写在一个 while 循环里,但每个步骤都通过"步骤表"(steps table)注册——这就是 LangGraph 设计哲学的浓缩:

数据驱动,而非控制流驱动。每个 Superstep 是「读步骤表 → 执行 → 写步骤表 → barrier」的一次循环。

...python

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 最优雅的设计。

03

节点调度:tick 如何选择下一个节点

每轮 Superstep 开始时,PregelLoop 询问 Channel:

  1. CondExpr step:上一轮的 Conditional Edge 计算结果,决定下一批 activate 的节点
  2. Channels 状态:Channels 自带"本轮是否更新"标记,未更新的节点不重复激活
  3. Task 队列:去重后的本轮待运行节点集

这意味着:节点只在状态真正变化时被调用。Agent 的"思考-行动"循环由此而成立。

04

状态写入:Channel.apply 是原子操作

所有节点返回的"partial state"都先到一个公共通道:Channel.apply。

...python

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 是所有状态写入的唯一入口

05

Barrier 同步与快照

一轮 Superstep 结束前会触发 Barrier:

  • 调用 Checkpointer:把当前 Channels 序列化为 Checkpoint
  • 计算下一步激活集合:基于更新后的状态重新评估 Conditional Edge
  • 清空 Pending Writes:本轮的"草稿"要么落盘要么丢弃

Barrier 是循环的天然边界——也是 Time Travel 和错误恢复的实现点。

06

条件路由:Conditional Edge 源码

Conditional Edge 是 LangGraph 表达 if-else 的地方。它把路由决策从节点代码里抽出来:

...python

class RunnableCallable:

  def invoke(self, state):

    # 这里的逻辑会在每一轮 Superstep 后被触发

    decision = self.cond_func(state)

    return decision # 返回一个或多个下一个节点名

LangGraph 支持三种返回值:

返回类型
行为
单个节点名 str
路由到该节点
节点名列表 list
多个节点并行激活(多 Agent 协作场景)
END 常量
本轮结束后终止图
07

从源码理解"为什么 LangGraph 能循环"

答案不在 while 循环,而在 BSP 模型:

  1. Superstep 是离散时间片:每一轮步骤有清晰的开始/结束边界
  2. Channel 是按版本更新的:用"是否本轮已更新"决定下一轮是否重新激活
  3. Conditional Edge 允许指回任意节点:包括指回自己(实现"反思-重试")
  4. Barrier 是天然断点:可以选择暂停、保存、恢复

这就是为什么 LangGraph 的"循环"是 LLM 时代最有用的循环:它能被 Checkpointer 持久化、能被 interrupt 中断、能被 inspect 检查。

▲ PregelLoop 的状态演化:从 invoke 到 End,每个箭头都是一个 Superstep

08

第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 的不同切面。