LangGraph を使っていると、StateGraph を compile() した後に add_node() にセットすることができます。しかし、「なぜ CompiledGraph を add_node() に渡せるのか?」という疑問を持つ方も多いでしょう。
この記事では、その理由を Pythonの型システム と LangChainの内部設計 を掘り下げながら、できる限りわかりやすく解説していきます!💡
【本記事の目次】
1. 何が起こっているのか?
LangGraph では、以下のようなコードで グラフの中に別のグラフ(Subgraph)を追加 できます。
parent_builder.add_node("start_subgraph", child_builder.compile())
ここで、
child_builder.compile()はCompiledGraphというオブジェクトを生成する。add_node()にCompiledGraphをセットすることで、子グラフを親グラフのノードとして扱える。
2. compile() の正体
LangGraph の compile() を実行すると、CompiledGraph というクラスのインスタンスが生成されます。
class CompiledGraph(Pregel):
builder: Graph
この CompiledGraph は、Graph をコンパイルして最適化した実行可能なオブジェクト です。
また、CompiledGraph の基底クラス Pregel をたどると、RunnableSerializable というクラスを継承しています。
class Pregel(
RunnableSerializable[Union[dict[str, Any], Any], Union[dict[str, Any], Any]]
):
ここで出てきた RunnableSerializable は、LangChain のコア機能である Runnable を拡張したものです。
3. add_node() の定義
一方、add_node() の関数定義を確認してみましょう。
def add_node(self, node: str, action: RunnableLike) -> None:
ここで action の型が RunnableLike になっています。
4. RunnableLike とは?
RunnableLike は、Runnable や Callable など、関数として実行できるもの を受け取れる型になっています。
RunnableLike = Union[
Runnable[Input, Output], # Runnableオブジェクト
Callable[[Input], Output], # 通常の関数(同期処理)
Callable[[Input], Awaitable[Output]], # 非同期処理の関数(async)
Callable[[Iterator[Input]], Iterator[Output]], # イテレータを処理する関数
Callable[[AsyncIterator[Input]], AsyncIterator[Output]], # 非同期イテレータ処理
Mapping[str, Any], # 辞書型のデータ
]
この定義を見ると、add_node() に渡せるのは Runnable または Callable であることが分かります。
5. CompiledGraph は Runnable である
実は、RunnableSerializable は Runnable を拡張したものなので、CompiledGraph は Runnable の一種です。
from langchain_core.runnables import Runnable
class RunnableSerializable(Runnable):
...
つまり、CompiledGraph は Runnable として動作するため、add_node() に渡すことができる のです!
6. まとめ
| 項目 | 説明 |
|---|---|
CompiledGraph |
StateGraph を compile() すると生成される |
Pregel |
CompiledGraph の基底クラス |
RunnableSerializable |
Pregel の基底クラスで、LangChain の Runnable を継承 |
add_node() の引数 |
RunnableLike 型(= Runnable または Callable) |
| 結論 | CompiledGraph は Runnable なので add_node() に渡せる! |
LangGraph では、「Node」も「Graph」も実行可能な Runnable として抽象化されている ため、親グラフに子グラフをセットできるという設計になっています。
この設計のおかげで、「グラフの中にグラフを入れる」 というネスト構造(Subgraph)が簡単に実装できるのです! 🚀✨