Appearance
第3章 LangGraphの基本概念と使い方
3.1 LangGraphとは何か
LangGraphは、LangChainの機能をベースにして、複数のステップからなる複雑な処理の流れを構築・管理するためのライブラリです。特に、データや処理の流れが一方通行ではなく、条件によって分岐したり、同じ処理を何度も繰り返したりする「循環(ループ)構造」を持つプログラムを作るのに適しています。これを利用することで、AI自身が状況を判断して行動を選択する「AIエージェント」を構築できます。
一般的なアプリケーション開発において、複雑な状態の遷移やループを管理するのは非常に骨の折れる作業です。LangGraphでは、これらを「グラフ」という数学の概念(点と線で表される図)に当てはめて整理します。グラフを構成するのは、具体的な処理を行う「ノード(点)」と、処理の進む方向を示す「エッジ(線)」です。
開発者は、どのノードがどのエッジで繋がっているかを定義することで、ビジュアル的にも理解しやすいフローをプログラムとして実装することができます。また、LangGraphは並列処理や状態の保存(チェックポイント)といった、本番運用に耐えうる高度な機能を標準で備えています。これにより、ユーザーからの指示に対して単に回答するだけでなく、自ら計画を立ててタスクを実行する自律型エージェントの作成が身近なものとなります。
3.2 なぜLangGraphが必要なのか
従来のLangChainでも、処理を連結して実行する仕組み(チェーン)や、AIがツールを選んで実行する簡単な「エージェント」機能は提供されていました。しかし、従来の仕組みでは、処理の進行が一方向(DAG:有向非巡回グラフ)に制限されており、エラーが起きた場合に前のステップに戻ってやり直すといった、柔軟なループ構造を綺麗に書くことが困難でした。
また、複雑なエージェントを構築する際には、「今、エージェントは何を知っていて、どこまで作業が進んだのか」という現在の「状態(State)」を、ステップ間で破綻させずに正確に引き継いでいく必要があります。従来のコードでこれを実装しようとすると、グローバル変数を使ったり、複雑な引数のやり取りが発生したりして、プログラムの安全性が著しく低下していました。
LangGraphは、グラフ全体で一つの「共有状態(State)」を持ち、各ノードがそれを安全に読み書きする仕組みを導入することで、これらの問題を完全にクリアしました。これにより、「AIがコードを生成し、それをテスト環境で動かしてみて、エラーが出たらコードを自己修正して再テストする」といった、ループを伴う洗練された自律システムを、バグが混入しにくい安全なコードで実装できるようになりました。
3.3 状態(State)の定義とデータのマージ方法
LangGraphの中心となる概念が「状態(State)」です。状態とは、グラフの中を流れるデータの実体であり、実行中の全ノードからアクセスできる共有のメモリ(変数)のようなものです。通常、Pythonのクラスや辞書(dict)として定義され、会話の履歴、現在のタスクリスト、一時的な作業結果などの情報がここに格納されます。
ノードが処理を実行すると、新しいデータを返します。LangGraphは、その返されたデータを自動的に既存の状態(State)にマージ(合流・上書き)します。このマージ方法については、開発者が細かくルールを指定できます。例えば、通常の変数のようには単に「上書き」する一方で、会話履歴(メッセージのリスト)については、古い履歴を消さずに新しいメッセージを末尾に「追加(Append)」していく、といった挙動を簡単に指定できます。
このようにデータごとの更新ルール(Reducer)をあらかじめ状態の定義に組み込んでおくことで、各ノードの処理コード内では複雑なデータ合成のロジックを書く必要がなくなります。各ノードは「状態を受け取って、自分が処理した差分だけを返す」というシンプルな実装になり、コードの可読性と保守性が大幅に向上します。
3.4 ノード(Nodes)とエッジ(Edges)による処理フローの構築
LangGraphでワークフローを作成する際は、処理の要素を「ノード」と「エッジ」に分解して登録していきます。「ノード(Node)」は、具体的に実行されるプログラムの関数やステップです。例えば、「ユーザーの入力を受け取って検索キーワードを作る関数」や「データベースを検索する関数」がノードになります。これらのノードは、現在の状態(State)を引数として受け取り、更新された状態を返します。
「エッジ(Edge)」は、ノードから次のノードへの「道筋(矢印)」です。LangGraphには、無条件で次の処理へ進む通常の「エッジ」と、グラフの開始点を示す「STARTエッジ」、終了点を示す「ENDエッジ」があります。これらを組み立てることで、「START -> ノードA -> ノードB -> END」といった全体の流れをプログラムコードとして宣言的に構築していきます。
このように処理の流れをノードとエッジに分離して定義することで、各処理の依存関係が明確になります。また、処理全体の構造を可視化(画像として出力する機能もLangGraphには標準で備わっています)しやすくなり、チーム開発において設計を共有したりレビューしたりする際にも大きな威力を発揮します。
3.5 条件付きエッジ(Conditional Edges)による動的な分岐
AIエージェントの真骨頂は、状況に応じて次の行動を自律的に判断できる点にあります。LangGraphでこの動的な分岐を実現するのが「条件付きエッジ(Conditional Edges)」です。これは、あるノードの処理終わった後、次にどのノードへ進むかを、プログラムの条件式やLLMの判断によって切り替える仕組みです。
条件付きエッジを設定するには、遷移先を決定するための「判定関数」を用意します。この判定関数は、現在の状態(State)を読み込み、次に進むべきノードの名前(文字列)を返します。例えば、LLMが「Web検索が必要」と判断した場合は「search_node」へ進み、「回答が可能」と判断した場合は「respond_node」へ進む、といった分岐を作ることができます。
これにより、一方通行の固定されたフローではなく、「LLMの出力結果によって、外部ツールの利用を繰り返すか、あるいはユーザーへの回答に移るか」を自動でループ判断する複雑な仕組み(ReActパターンなど)を実装することが可能になります。条件付きエッジは、エージェントを自律的に動かすための最も重要な部品です。
3.6 グラフのコンパイルと実行フロー
ノード、エッジ、条件付きエッジの登録が完了したら、グラフをアプリケーションとして動作させるための「コンパイル(Compile)」という手順を踏みます。コンパイルを行うことで、作成したグラフ構造がLangChainの標準的な「Runnable」オブジェクトへと変換されます。これにより、グラフ全体がLangChainの他の部品と同じように invoke や stream などのメソッドで呼び出せるようになります。
コンパイルされたグラフに初期入力(Stateの初期値)を与えて実行すると、LangGraphは自動的にSTARTから順にエッジを辿ってノードを実行していきます。実行中、各ノードが状態(State)をどのように更新していったかの履歴はすべて記録されており、実行が終了すると、すべてのノードの処理が完了した後の最終的な状態が返されます。
このコンパイルステップがあることで、グラフを外部のWeb API(FastAPIなど)のエンドポイントに直接組み込むことが非常に容易になります。コンパイルは、静的に定義した処理の流れを、動的に動くシステムへと昇華させるための最終工程と言えます。