StateGraph.compile:ブループリントを Pregel 実行体にコンパイル
役割
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-1217—checkpointer/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-1297—set_node_defaultsで設定したグローバル error_handler は__default_error_handler__という名前の特殊ノードとして注入されます。defaults 应用:1299-1325—set_node_defaultsの retry / cache / error_handler / timeout を明示指定のない各 spec に適用します。cache と error_handler は error-handler ノード自身には適用しません。node_error_handler_map:1327-1331—node_name -> handler_node_nameのマップを生成し、ランタイムで失敗ノードの実行を対応 handler にルーティングします。构造 CompiledStateGraph:1333-1357— builder のchannels/managedをマージし、START: EphemeralValue(input_schema)を入口チャネルとして加え、stream_mode="updates"、input_channels=STARTを設定します。attach 三件套:1360-1388—compiled.attach_node(START, None)+ 各ノード、各エッジのattach_edge、各分岐のattach_branchをループで呼び、最後にcompiled.validate()。Pregel.__init__:758-836—CompiledStateGraph.__init__はsuper().__init__(**kwargs)で Pregel に委譲し、ここで全実行期フィールドを展開しauto_validate=Trueのときself.validate()を呼びます。
データフロー
compile が真に実行体を構築する关键段落です。builder の全フィールドを CompiledStateGraph に詰め、START 入口チャネルを加えます:
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)最後にエッジと分岐も Pregel のチャネル購読関係に翻訳し、もう一度 validate を発火します:
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 内の NodeBuilder を PregelNode に翻訳し、TASKS チャネルに Topic(Send, accumulate=False) を据え、auto_validate=True のとき self.validate() を呼びます (Pregel init:800-836):
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)境界と失敗
checkpointer=Trueはルートグラフに使えない(True 报错:2583-2584)。Trueは「親グラフから継承」を意味し、サブグラフだけが使えます。ルートグラフは明示的に saver を与えるかFalseを与える必要があります。interrupt_before="*"とinterrupt_after="*"の相互排他処理(* 处理:1249-1253)。interrupt_after != "*"のときだけinterrupt_beforelist を 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 はビルダとランタイムの境界です。StateGraph で add_node / add_edge / add_conditional_edges が蓄積した spec をすべて Pregel の実行期構造に翻訳し、checkpointer / store / interrupt 設定を紐付け、最後に直接 invoke できる Pregel サブクラスを返します。これを理解すれば「グラフ宣言 → 実行体」のリンク全体が見えます。
続いて Pregel エンジン で Pregel.__init__ 後の invoke / stream がこのコンパイル産物をどう走らせるかを見るか、StateSnapshot でコンパイル産物と checkpointer からどう状態を取るかを見てください。
公式資料:LangGraph 文档 · README