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)が簡単に実装できるのです! 🚀✨