ほとんどのエージェントコードは最初はシンプルですが、リトライ、分岐、ツール呼び出し、人間の承認が入るとすぐに破綻します。LangGraphは、LLMエージェントを「共有状態を持つグラフ」として実装するためのフレームワークです。ループや条件分岐をネストしたif文で管理する代わりに、ノードとエッジとして明示できます。このガイドでは、LangGraphの基本構造、実装手順、永続化、ヒューマン・イン・ザ・ループ、そしてエージェントが実際のサービスを呼び出す際にAPIテストがどのように役立つかを説明します。
LangGraphとは
LangGraphは、長時間実行されるステートフルなエージェントを構築するための低レベルなオーケストレーションフレームワークおよびランタイムです。LangChain Incによって開発されていますが、LangChain本体とは別のライブラリとして利用できます。
pip install -U langgraph
LangGraphの中心的な考え方は、エージェントをグラフとして定義することです。
- ノード: モデル呼び出し、ツール実行、データ変換などの処理
- エッジ: 次に実行するノードを決める接続
- 状態: すべてのノード間で共有されるデータ
- サイクル: ツール実行後に再びモデルへ戻るようなループ
従来のチェーンは、基本的に「ステップ1 → ステップ2 → ステップ3」の直線的な処理です。一方、実際のエージェントは「推論 → ツール実行 → 観察 → 再推論」を繰り返します。LangGraphは、この循環的な制御フローを最初から扱えるように設計されています。
LangGraphが解決する問題
エージェントを本番環境に近づけると、単なるLLM呼び出しではなく、次のような制御が必要になります。
停止条件付きループ
ツール呼び出しを繰り返しつつ、無限ループを防ぐ。状態に応じた分岐
モデルの出力、ツール結果、ユーザー入力に応じて次の処理を変える。永続化と再開
クラッシュ、タイムアウト、再起動後も途中状態から再開する。ヒューマン・イン・ザ・ループ
承認、修正、確認が必要なポイントで一時停止する。ストリーミング
最終結果だけでなく、トークンや中間ステップをUIに流す。
これらを手書きのwhile、if、例外処理、DB保存で組み合わせると、コードはすぐに複雑になります。LangGraphでは、これらをグラフ、状態、チェックポイントとして整理できます。
主要な概念:グラフ、状態、ノード、エッジ
LangGraphの実装では、主に次の4つを定義します。
1. 状態
状態は、グラフ全体で共有される型付きオブジェクトです。多くの場合、TypedDictで定義します。チャットエージェントでは、組み込みのMessagesStateを使うと、メッセージ履歴を簡単に扱えます。
2. ノード
ノードは通常のPython関数です。現在の状態を受け取り、状態への差分更新を返します。
3. エッジ
エッジはノード間の遷移です。
-
add_edge():常に次のノードへ進む -
add_conditional_edges():状態に応じて遷移先を変える
4. START / END
STARTとENDは、グラフの開始点と終了点を表す特殊なマーカーです。
最小構成のLangGraphエージェント
以下は、モデルが必要に応じてツールを呼び出し、ツール実行後に再びモデルへ戻る最小構成です。
from langgraph.graph import StateGraph, START, END, MessagesState
def call_model(state: MessagesState):
response = model.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: MessagesState) -> str:
last = state["messages"][-1]
return "tools" if last.tool_calls else END
builder = StateGraph(MessagesState)
builder.add_node("model", call_model)
builder.add_node("tools", tool_node)
builder.add_edge(START, "model")
builder.add_conditional_edges("model", should_continue)
builder.add_edge("tools", "model") # ツール実行後にモデルへ戻る
graph = builder.compile()
ポイントはこの行です。
builder.add_edge("tools", "model")
ツール実行後にモデルへ戻ることで、次のようなループが作れます。
- モデルがユーザー入力を読む
- 必要ならツール呼び出しを要求する
- ツールを実行する
- 結果をモデルへ戻す
- 追加のツール呼び出しが必要か判断する
- 完了したら終了する
このループを明示的なグラフとして管理できることが、LangGraphの強みです。
永続化とヒューマン・イン・ザ・ループ
LangGraphでは、チェックポインターを使って各ステップ後の状態を保存できます。thread_idを渡すことで、同じ会話やタスクの状態を後続の呼び出しで復元できます。
from langgraph.checkpoint.memory import InMemorySaver
graph = builder.compile(checkpointer=InMemorySaver())
config = {
"configurable": {
"thread_id": "user-42"
}
}
graph.invoke(
{"messages": [user_message]},
config
)
InMemorySaverは開発用途に適しています。再起動後も状態を残したい場合は、SQLiteやPostgresをバックエンドにしたチェックポインターを検討します。
永続化があると、ヒューマン・イン・ザ・ループも実装しやすくなります。
たとえば次のような処理です。
- エージェントが外部APIを呼び出す前に停止する
- 現在の状態をUIに表示する
- 人間が承認または修正する
- 同じチェックポイントから再開する
承認ゲート、手動レビュー、危険な操作前の確認などは、この仕組みの上に構築できます。
ストリーミングを使う
エージェントの実行は時間がかかることがあります。LangGraphでは、実行中のトークンやノード更新をストリーミングできます。
UI側では、単にスピナーを表示するのではなく、次のような中間状態を表示できます。
- モデルが生成中のテキスト
- どのノードを実行しているか
- どのツールを呼び出しているか
- ツールの結果
- 次の分岐判断
長時間実行されるエージェントでは、ストリーミングはデバッグにもユーザー体験にも重要です。
LangGraphとLangChainの関係
LangChainは、モデルラッパー、プロンプトテンプレート、リトリーバー、ドキュメントローダー、各種プロバイダー連携を含む広範なツールキットです。
LangGraphは、その中でもエージェントの制御フローとランタイムに特化したオーケストレーション層です。
LangGraphを使うためにLangChainは必須ではありません。ノード内では、任意のモデルクライアントやHTTPクライアントを呼び出せます。ただし、LangChainのモデル抽象化やツール抽象化と組み合わせると、実装が楽になるケースもあります。
| LangChain | LangGraph | |
|---|---|---|
| 役割 | コンポーネントと統合 | オーケストレーションとランタイム |
| 制御フロー | 線形チェーン | サイクルと分岐を持つグラフ |
| 組み込み状態 | 限定的 | 共有、型付き、永続的 |
| 永続性 / 再開 | 主要な焦点ではない | チェックポインター + スレッドID |
| 最適用途 | モデル呼び出しとツールの構成 | ステートフルな多段階エージェント |
注意点として、LangGraph v1.0系列では、組み込みエージェントヘルパーのインポートパスが変わっている場合があります。古いコードをコピーする前に、インストール済みバージョンと公式リファレンスを確認してください。
カスタムエージェントをゼロから構築する考え方については、カスタムAIエージェントの構築も参考になります。
LangGraph PlatformとStudio
LangGraphの中核はオープンソースライブラリですが、デバッグやデプロイでは関連ツールも役立ちます。
LangGraph Studio
LangGraph Studioは、ビジュアルなエージェントIDEです。グラフ構造を表示し、実行パスや各ノードの状態を確認できます。
サイクルや条件付きルーティングがあるエージェントでは、ログだけを読むよりも、実際にどのノードを通ったかを視覚的に確認する方が効率的です。
LangGraph Platform
LangGraph Platformは、エージェントを本番環境で運用するためのデプロイメント側の製品です。
主に次のような用途があります。
- エージェント用APIエンドポイントの提供
- 長時間実行タスクの永続化
- セルフホストまたはマネージド環境での運用
- 実行状態の管理
ライブラリとしてLangGraphを使うだけならPlatformは必須ではありません。本番環境でエージェント実行基盤を管理したい場合に検討します。
LangGraphを使用すべき時
LangGraphは、すべてのLLMアプリに必要なわけではありません。次のような条件に当てはまる場合に使う価値があります。
- 処理が直線ではなくループする
- モデルの判断に応じて分岐する
- ツール呼び出しを複数回行う
- 実行が長時間になる可能性がある
- クラッシュ後に途中から再開したい
- 人間の承認や編集が必要
- 複数のサブエージェントやアクターを調整する
- 状態を明示的に管理したい
一方で、次のようなケースでは過剰です。
- 1回のLLM呼び出しで完了する
- 単純な要約や分類だけを行う
- 分岐もループもない
- 永続化が不要
単純な処理には、通常の関数や短いチェーンで十分です。LangGraphは、制御フローが複雑になった段階で導入すると効果的です。
APIテストとモックがどのように適合するか
LangGraphは、エージェントの制御フローを管理します。ただし、エージェントが呼び出す外部APIの正しさまでは保証しません。
実際のエージェントは、次のようなAPIに依存します。
- LLM API
- 検索API
- CRM API
- 社内バックエンド
- データベースAPI
- 決済や通知などの外部サービス
これらのAPIが遅い、不安定、レスポンス形式を変える、といった問題があると、LangGraph側のルーティングやノード処理も壊れます。そこでApidogのようなAPIテストとモックの仕組みが役立ちます。
LangGraphエージェントでAPIを安全に扱う手順
1. ツールAPIをモックする
開発中に毎回ライブAPIを叩くと、次の問題が起きます。
- LLMトークンを消費する
- 外部APIのレート制限に当たる
- テスト結果が外部サービスの状態に依存する
- 実行が遅くなる
- 失敗原因がグラフなのかAPIなのか切り分けにくい
APIをモックすることで、ツールノードに対して確定的なレスポンスを返せます。
たとえば、検索APIノードをテストする場合、本物の検索結果ではなく、固定のレスポンスを返すモックを用意します。
{
"results": [
{
"title": "LangGraph overview",
"url": "https://docs.langchain.com/oss/python/langgraph/overview",
"snippet": "LangGraph is a framework for stateful agents."
}
]
}
これにより、グラフの分岐ロジックだけを安定して検証できます。
2. LLMレスポンスも固定する
ルーティングのテストでは、LLMの出力も固定した方が扱いやすいです。
たとえば、モデルがツール呼び出しを要求するケースと、最終回答を返すケースを分けてテストします。
def should_continue(state: MessagesState) -> str:
last = state["messages"][-1]
return "tools" if last.tool_calls else END
この関数を確認したいだけなら、本物のLLMを呼ぶ必要はありません。tool_callsを含むダミーメッセージと、含まないダミーメッセージを用意すれば十分です。
3. レスポンス形状をアサートする
ノードは、APIレスポンスのフィールド名や型に依存します。
たとえば、ツールノードが次のようなレスポンスを前提にしているとします。
{
"status": "ok",
"data": {
"customer_id": "cus_123",
"plan": "pro"
}
}
もしAPI側がcustomer_idをcustomerIdに変更すると、ノードや条件付きエッジが壊れる可能性があります。
APIアサーションでレスポンス契約を固定しておけば、変更がエージェントに到達する前に検出できます。
4. 環境ごとの認証情報を分離する
エージェントは、開発、ステージング、本番で異なるAPIキーやエンドポイントを使うことが多いです。
ノード内にシークレットを直接書くのではなく、環境ごとに管理します。
import os
import requests
def call_crm_api(customer_id: str):
base_url = os.environ["CRM_API_BASE_URL"]
api_key = os.environ["CRM_API_KEY"]
response = requests.get(
f"{base_url}/customers/{customer_id}",
headers={"Authorization": f"Bearer {api_key}"}
)
response.raise_for_status()
return response.json()
Apidog側でも環境を分けてAPIテストを管理すれば、ステージングと本番の切り替えを安全に行えます。
エージェント向けのより具体的なテスト構成は、AIエージェント用のApidogテストハーネスで説明されています。
よくある質問
LangGraphはLangChainの代替ですか?
いいえ。LangGraphはオーケストレーションランタイムであり、LangChainはモデル、ツール、リトリーバーなどを含む広範なコンポーネント群です。
LangGraphは、通常のチェーンでは扱いにくいステートフルで循環的な制御フローを扱います。LangChainなしでも使えますし、LangChainと組み合わせても使えます。
LangGraphを始めるためにLangChainを知っている必要がありますか?
必須ではありません。StateGraphを定義し、ノードとエッジを追加し、ノード内で任意のモデルクライアントを呼び出せます。
最初は次の順で理解すると実装しやすいです。
- 状態を定義する
- ノード関数を書く
- 通常エッジを追加する
- 条件付きエッジを追加する
- チェックポインターを追加する
- 必要に応じてツールやLangChain連携を追加する
LangGraphは呼び出し間でどのように情報を記憶していますか?
チェックポインターを使います。グラフをチェックポインター付きでコンパイルし、thread_idを渡すと、LangGraphは各ステップ後に状態スナップショットを保存します。
次回同じthread_idで呼び出すと、その状態を復元できます。これにより、会話履歴、途中結果、クラッシュ後の再開を扱えます。
エージェントが呼び出すAPIをどのようにテストしますか?
グラフとは別にテストし、必要に応じてモックします。
実践的には次の流れです。
- ツールAPIの仕様を定義する
- 正常系レスポンスをモックする
- エラー系レスポンスもモックする
- ノードが期待通りに状態を更新するか確認する
- 条件付きエッジが正しく分岐するか確認する
- レスポンス形式をアサーションで固定する
ChatGPT APIのテストに関するガイドでは、認証、ストリーミング、ツール呼び出しなど、エージェントが依存しやすいインターフェースの扱いを説明しています。
まとめ
LangGraphは、ループ、分岐、永続化、人間の承認を含むエージェントを実装するための実用的なフレームワークです。
実装時は次の順で進めると整理しやすくなります。
- 状態スキーマを決める
- モデル呼び出しノードを書く
- ツール実行ノードを書く
- 条件付きエッジでルーティングする
- 必要に応じてチェックポインターを追加する
- ストリーミングと人間の承認ポイントを追加する
- エージェントが呼ぶAPIをモックし、レスポンス契約をテストする
LangGraphはオーケストレーションを担当します。一方で、エージェントが依存するAPIには別の安全ネットが必要です。開発中のコストを抑え、ツールAPIの契約を守るために、ApidogでAPIをモックし、アサーションで検証してください。エージェントが実際のエンドポイントへアクセスする前に、Apidogをダウンロードしてレスポンスを固定し、グラフの挙動を安定してテストできます。



Top comments (0)