Skip to content

StateGraph.compile:ブループリントを Pregel 実行体にコンパイル

源码版本1.2.9

役割

StateGraph.compile はビルダとランタイムの間の「翻訳官」です。StateGraph(ノード spec、エッジ、分岐条件、チャネル辞書)を受け取り、CompiledStateGraph(CompiledStateGraph:1391)を吐き出します。後者は Pregel を継承し、invoke / stream / astream / get_state などすべての実行期メソッドを持ちます。compile 前のグラフは単なる設定データで、compile 後に初めて実行体になります。

中核の仕事は 4 つです:1) グラフ構造の検証(validate)、2) 出力/ストリームチャネルの決定、3) 実行期依存(checkpointer / store / cache / interrupt 設定)の注入、4) 各 StateNodeSpec / edge / BranchSpec を Pregel の PregelNode + チャネル購読に翻訳(构造 CompiledStateGraph:1333-1357)。翻訳は compiled.attach_node / compiled.attach_edge / compiled.attach_branch をループで呼んで行われます。これらのメソッドがビルダの宣言的データ構造を PregelNode.writers / PregelNode.triggers などの実行期フィールドに変換します。

compile はもう 2 つの副次的責務も担います:1) 明示的に policy が指定されていないすべてのノードに set_node_defaults のデフォルト値(retry / cache / error_handler / timeout)を適用、2) 厳格 msgpack シリアライズが有効なとき(_serde.STRICT_MSGPACK_ENABLED)、serde allowlist を構築し checkpointer がスキーマに現れるフィールドだけシリアライズするように(serde allowlist:1220-1241)。

設計動機

なぜ StateGraph 自体を Pregel にせず、わざわざ 2 段階に分けるのでしょう?

  • 構築期 vs 実行期の関心の分離:builder 段階は「グラフがどういう形か」だけを気にし、実行期段階は「どう走らせるか」を気にします。分ければ builder を複数回コンパイルして異なる実行体を作れます。同じ StateGraph に異なる checkpointer / store / interrupt ノードを組み合わせて複数の Pregel インスタンスを生成できます。
  • 不変実行体:compile 後の CompiledStateGraph のフィールドは Pregel.__init__ でほぼ固定されます (Pregel.__init__:758-836)。auto_validate=False により compile 自身がいつ検証するかを制御し、コンストラクタで早期検証されるのを避けます。
  • 遅延検証:ユーザーが add_node するときはコンテナに append するだけで、「エッジの起点が存在するか」を即時検査しません。これによりユーザーは任意の順序でノードとエッジを登録できます。真の検証は compile 時に一度に (validate 调用:1247-1254) 行われます。
  • 中断設定のパラメータ化:interrupt_before / interrupt_after はグラフ自身の属性ではなく compile パラメータです。同じグラフから「中断あり」と「中断なし」の 2 バージョンをコンパイルでき、ヒューマンインザループ场景でよくあります (interrupt 参数:1170-1171)。
  • checkpointer 型の正規化:compile(checkpointer=...)None / True / False / BaseCheckpointSaver の 4 種を受け付け、ensure_valid_checkpointer(ensure_valid_checkpointer:107-117)が一括して検証し、ランタイムでの炸裂を避けます。
  • デフォルト値はコンパイル期にだけ適用(defaults 应用:1299-1325)。これによりユーザーは add_node の後に set_node_defaults を呼べます。新しいデフォルト値は明示指定のないすべてのノードを上書きします。

主要ファイル

  • compile 签名:1164-1217checkpointer / store / cache / interrupt_before / interrupt_after / debug / name / transformers を受け取ります。
  • ensure_valid_checkpointer:107-117 — checkpointer が None / True / False / BaseCheckpointSaver のいずれかであることを検証し、さもなくば TypeError
  • ensure_valid_checkpointer 调用:1218 — compile の最初の仕事は checkpointer を正規化することです。
  • serde allowlist:1220-1241 — 厳格 msgpack 時に allowlist を構築し checkpointer に適用し、checkpoint がスキーマフィールドだけ保存するようにします。
  • interrupt 合并 + validate:1243-1254"*" は All(全ノード)を意味し、interrupt_before / interrupt_after を 1 つの list にまとめて validate に渡します。
  • output / stream channels:1256-1273 — 単一フィールドで __root__ なら文字列を直接使い、さもなくば list で managed value をフィルタします。
  • 默认 error handler 节点:1278-1297set_node_defaults で設定したグローバル error_handler は __default_error_handler__ という名前の特殊ノードとして注入されます。
  • defaults 应用:1299-1325set_node_defaults の retry / cache / error_handler / timeout を明示指定のない各 spec に適用します。cache と error_handler は error-handler ノード自身には適用しません。
  • node_error_handler_map:1327-1331node_name -> handler_node_name のマップを生成し、ランタイムで失敗ノードの実行を対応 handler にルーティングします。
  • 构造 CompiledStateGraph:1333-1357 — builder の channels / managed をマージし、START: EphemeralValue(input_schema) を入口チャネルとして加え、stream_mode="updates"input_channels=START を設定します。
  • attach 三件套:1360-1388compiled.attach_node(START, None) + 各ノード、各エッジの attach_edge、各分岐の attach_branch をループで呼び、最後に compiled.validate()
  • Pregel.__init__:758-836CompiledStateGraph.__init__super().__init__(**kwargs) で Pregel に委譲し、ここで全実行期フィールドを展開し auto_validate=True のとき self.validate() を呼びます。

データフロー

compile が真に実行体を構築する关键段落です。builder の全フィールドを CompiledStateGraph に詰め、START 入口チャネルを加えます:

python
compiled = CompiledStateGraph[StateT, ContextT, InputT, OutputT](
    builder=self,
    schema_to_mapper={},
    context_schema=self.context_schema,
    nodes={},
    channels={
        **self.channels,
        **self.managed,
        START: EphemeralValue(self.input_schema),
    },
    input_channels=START,
    stream_mode="updates",
    output_channels=output_channels,
    stream_channels=stream_channels,
    checkpointer=checkpointer,
    interrupt_before_nodes=interrupt_before,
    interrupt_after_nodes=interrupt_after,
    auto_validate=False,
    debug=debug,
    store=store,
    cache=cache,
    node_error_handler_map=node_error_handler_map,
    name=name or "LangGraph",
    stream_transformers=transformers,
)
compiled._serde_allowlist = serde_allowlist

compiled.attach_node(START, None)
for key, node in self.nodes.items():
    compiled.attach_node(key, node)

(构造 + attach_node:1333-1362)

最後にエッジと分岐も Pregel のチャネル購読関係に翻訳し、もう一度 validate を発火します:

python
for start, end in self.edges:
    compiled.attach_edge(start, end)

for starts, end in self.waiting_edges:
    compiled.attach_edge(starts, end)

for start, branches in self.branches.items():
    for name, branch in branches.items():
        compiled.attach_branch(start, name, branch)

return compiled.validate()

(attach edge/branch:1378-1388)

Pregel.__init__ はこれらのフィールドを受け取ると、nodes 内の NodeBuilderPregelNode に翻訳し、TASKS チャネルに Topic(Send, accumulate=False) を据え、auto_validate=True のとき self.validate() を呼びます (Pregel init:800-836):

python
self.nodes = {
    k: v.build() if isinstance(v, NodeBuilder) else v for k, v in nodes.items()
}
self.channels = channels or {}
if TASKS in self.channels and not isinstance(self.channels[TASKS], Topic):
    raise ValueError(
        f"Channel '{TASKS}' is reserved and cannot be used in the graph."
    )
else:
    self.channels[TASKS] = Topic(Send, accumulate=False)

(Pregel init nodes:800-809)

境界と失敗

  • checkpointer=True はルートグラフに使えない(True 报错:2583-2584)。True は「親グラフから継承」を意味し、サブグラフだけが使えます。ルートグラフは明示的に saver を与えるか False を与える必要があります。
  • interrupt_before="*"interrupt_after="*" の相互排他処理(* 处理:1249-1253)。interrupt_after != "*" のときだけ interrupt_before list を interrupt にマージします。これは * で「全ノード」を表すときの優先度約束です。
  • TASKS チャネル名は予約済み(TASKS 保留:804-807)。ユーザーがスキーマで __pregel_tasks という名前のフィールドを定義すると直接エラーになります。これはエンジン内部が Send を扇出するための Topic チャネルです。
  • 同名ノードの重複 attach:builder 段階で同名はすでに遮断されています (重名:792-793)。ただし __default_error_handler__ のような自動生成ノード名もユーザーノードと衝突を避ける必要があります (default handler 冲突:1280-1284)。
  • cache と error_handler のデフォルト値は handler ノード自身には適用しない(cache 不给 handler:1313-1321)。「handler の結果をキャッシュする」のは安全ではありませんし(失敗ノードの state は毎回異なり得る)、「handler が自分を掴む」のは無限ループになります。
  • 同じ builder の複数回 compile は許可:毎回 compile は新しい CompiledStateGraph を作ります。builder の compiled フィールドは validate() 時に True に設定されますが (compiled=True:1161)、これは単なるフラグで、再 compile や add_node の継続を妨げません(ただし warning は出ます)。

まとめ

compile はビルダとランタイムの境界です。StateGraphadd_node / add_edge / add_conditional_edges が蓄積した spec をすべて Pregel の実行期構造に翻訳し、checkpointer / store / interrupt 設定を紐付け、最後に直接 invoke できる Pregel サブクラスを返します。これを理解すれば「グラフ宣言 → 実行体」のリンク全体が見えます。

続いて Pregel エンジンPregel.__init__ 後の invoke / stream がこのコンパイル産物をどう走らせるかを見るか、StateSnapshot でコンパイル産物と checkpointer からどう状態を取るかを見てください。

公式資料:LangGraph 文档 · README