一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • LangGraphで構築するAIエージェント:状態管理とツール連携の実践

AIエージェント開発の新潮流:LangGraphとは

LLM(大規模言語モデル)を活用したアプリケーション開発において、単純なプロンプト応答を超えた「エージェント」の構築が注目を集めています。エージェントとは、与えられた目標に対して自律的に判断し、ツールを使い分けながらタスクを遂行するシステムです。LangGraphは、LangChainチームが開発したグラフベースのフレームワークで、複雑なエージェントの状態管理とワークフロー制御を実現します。

従来のLangChainのAgentExecutorでは、エージェントのフローが暗黙的で制御が難しいという課題がありました。LangGraphはこの問題を、明示的なグラフ構造でワークフローを定義することで解決しています。

LangGraphの基本概念

グラフ構造による状態遷移

LangGraphの核心は、エージェントの動作をグラフ(ノードとエッジ)で表現する点にあります。各ノードは処理ステップ、エッジは遷移条件を表し、全体のフローを視覚的かつ明示的に制御できます。

from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator

# 状態の型定義
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
current_step: str
tool_results: dict

# グラフの構築
graph = StateGraph(AgentState)

# ノードの追加
graph.add_node("analyze", analyze_task)
graph.add_node("select_tool", select_tool)
graph.add_node("execute_tool", execute_tool)
graph.add_node("evaluate", evaluate_result)

# エッジの定義
graph.set_entry_point("analyze")
graph.add_edge("analyze", "select_tool")
graph.add_edge("select_tool", "execute_tool")
graph.add_conditional_edges(
"evaluate",
should_continue,
{"continue": "analyze", "finish": END}
)

このコードでは、タスクの分析、ツールの選択、ツールの実行、結果の評価という4つのステップをノードとして定義し、条件分岐により処理を繰り返すか終了するかを制御しています。

状態管理の仕組み

LangGraphの状態管理は、TypedDictベースの状態クラスで行われます。特にAnnotatedoperator.addを使うことで、メッセージリストへの追記的な更新が可能になります。これにより、各ノードは状態の一部だけを返せばよく、状態全体を管理する負担が軽減されます。

def analyze_task(state: AgentState) -> dict:
messages = state["messages"]
# LLMにタスク分析を依頼
response = llm.invoke(messages)
return {
"messages": [response],
"current_step": "analysis_complete"
}

ツール連携の実装

エージェントの真価は、外部ツールとの連携にあります。LangGraphでは、LangChainのツール定義と組み合わせることで、LLMが状況に応じて適切なツールを選択・実行できます。

from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

@tool
def search_database(query: str) -> str:
"""データベースからクエリに一致する情報を検索する"""
# 実際のDB検索ロジック
results = db.execute(query)
return str(results)

@tool
def calculate(expression: str) -> str:
"""数式を計算して結果を返す"""
return str(eval(expression))

@tool
def send_notification(message: str, channel: str) -> str:
"""指定チャネルに通知を送信する"""
slack_client.send(channel, message)
return f"通知を{channel}に送信しました"

# ツールをLLMにバインド
tools = [search_database, calculate, send_notification]
llm_with_tools = ChatOpenAI(model="gpt-4o").bind_tools(tools)

ツール実行ノードの実装

ツールの呼び出しと結果の処理を行うノードを実装します。LLMがツール呼び出しを決定した場合、そのツールを実際に実行し結果を状態に反映します。

from langgraph.prebuilt import ToolNode

# ToolNodeは定義済みツールの実行を自動的に処理する
tool_node = ToolNode(tools)

# 条件分岐:ツール呼び出しが必要か判定
def should_use_tool(state: AgentState) -> str:
last_message = state["messages"][-1]
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "use_tool"
return "respond"

# グラフへの組み込み
graph.add_node("agent", call_agent)
graph.add_node("tools", tool_node)
graph.add_conditional_edges(
"agent",
should_use_tool,
{"use_tool": "tools", "respond": END}
)
graph.add_edge("tools", "agent")

チェックポイントによる永続化

LangGraphの強力な機能の一つが、チェックポイントによる状態の永続化です。これにより、長時間にわたるタスクの中断・再開や、会話の履歴管理が実現できます。

from langgraph.checkpoint.sqlite import SqliteSaver

# チェックポイントの設定
memory = SqliteSaver.from_conn_string(":memory:")

# グラフのコンパイル時にチェックポイントを指定
app = graph.compile(checkpointer=memory)

# スレッドIDを指定して実行
config = {"configurable": {"thread_id": "user_session_001"}}
result = app.invoke(
{"messages": [{"role": "user", "content": "売上レポートを作成して"}]},
config=config
)

チェックポイントはSQLite以外にも、PostgreSQLやRedisなど複数のバックエンドをサポートしています。本番環境ではPostgreSQLを使用し、開発環境ではSQLiteを使うといった使い分けが可能です。

Human-in-the-Loopの実装

エージェントが重要な操作(データの削除、外部への通知送信など)を行う前に、人間の承認を求める仕組みを組み込むことは、実運用において非常に重要です。

from langgraph.graph import StateGraph, END

def request_approval(state: AgentState) -> dict:
"""人間の承認を求めるノード"""
pending_action = state["tool_results"].get("pending_action")
return {
"messages": [{
"role": "assistant",
"content": f"以下の操作を実行してよろしいですか?\n{pending_action}"
}],
"current_step": "awaiting_approval"
}

# interrupt_before で特定ノードの前に中断
app = graph.compile(
checkpointer=memory,
interrupt_before=["execute_dangerous_tool"]
)

interrupt_beforeパラメータを使うことで、指定したノードの実行前にグラフの実行が中断されます。ユーザーの承認後に再開することで、安全なエージェント運用が可能になります。

実運用での設計パターン

エラーハンドリングとリトライ

エージェントがツール実行に失敗した場合のリトライロジックや、LLMの応答が不適切だった場合のフォールバック処理は、実運用で必ず必要になります。グラフの条件分岐を使って、エラー状態を明示的にハンドリングすることが重要です。

並列実行によるパフォーマンス向上

複数のツールを同時に実行できる場合、LangGraphのファンアウト・ファンイン機構を使って並列処理が可能です。例えば、複数のデータソースから同時に情報を取得し、結果を統合するパターンは頻繁に使用されます。

サブグラフによるモジュール化

複雑なエージェントは、サブグラフに分割してモジュール化することで保守性が向上します。例えば、情報収集フェーズ、分析フェーズ、レポート生成フェーズをそれぞれ独立したサブグラフとして定義し、メインのグラフから呼び出す構成が考えられます。

まとめ

LangGraphは、AIエージェント開発における状態管理とワークフロー制御の課題を、グラフベースのアプローチで解決する強力なフレームワークです。明示的なグラフ構造によりデバッグが容易になり、チェックポイント機能で長時間タスクの管理も実現できます。ツール連携とHuman-in-the-Loopの仕組みを組み合わせることで、実用的で安全なAIエージェントを構築できるでしょう。まずは小さなワークフローから始めて、徐々に複雑なエージェントへと発展させていくアプローチをお勧めします。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア