LangGraphでAIワークフローを構築する実践ガイド2026 - ステートマシン型エージェント開発
はじめに
LangGraphは、LangChainチームが開発したグラフベースのAIワークフローフレームワークです。複雑なAIエージェントを「状態機械(ステートマシン)」として定義し、条件分岐・ループ・並列処理を直感的に実装できます。
2026年、エンタープライズレベルのAIアプリ開発でLangGraphはデファクトスタンダードになりつつあります。
LangGraphとは
従来のLLMチェーンの限界
入力 → LLM1 → LLM2 → LLM3 → 出力
(直線的なパイプライン)
複雑なタスクには不十分でした:ツール呼び出しの失敗時のリトライ、複数のエージェントが協調作業、人間の確認を挟む(Human-in-the-loop)。
LangGraphのアプローチ
[検索]
/ \
開始 → [計画] → [コード生成] → [実行] → [終了]
\ /
[エラー処理] ←---
(グラフ構造で複雑なフローを表現)
主な特徴:
- ステート管理: ワークフロー全体の状態を型安全に管理
- 条件分岐: 状態に基づいて次のノードを動的に決定
- サイクル(ループ): LLMが「続ける」か「終了」かを判断
- ストリーミング: 各ステップの進捗をリアルタイムに取得
- Human-in-the-loop: 人間の承認を挟む処理
- 永続化: チェックポイントで状態を保存・再開
インストール
pip install langgraph langchain-openai langchain-community
# またはAnthropicと一緒に
pip install langgraph langchain-anthropic
基本: シンプルなエージェント
ステート定義
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
import operator
# グラフ全体で共有される状態の型定義
class AgentState(TypedDict):
messages: Annotated[list, operator.add] # メッセージ履歴を追記
current_step: str
result: str
ノード(処理単位)の定義
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode
# ツールの定義
@tool
def search_web(query: str) -> str:
"""Webを検索します。"""
# 実際にはAPIを呼ぶ
return f"'{query}'の検索結果: 最新情報が見つかりました..."
@tool
def calculate(expression: str) -> str:
"""数式を計算します。"""
try:
result = eval(expression)
return str(result)
except:
return "計算エラー"
tools = [search_web, calculate]
# LLMにツールを紐付け
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools)
# エージェントノード
def agent_node(state: AgentState):
messages = state["messages"]
response = llm_with_tools.invoke(messages)
return {"messages": [response]}
# ツール実行ノード
tool_node = ToolNode(tools)
グラフの構築
from langgraph.graph import StateGraph, END
# グラフの作成
graph = StateGraph(AgentState)
# ノードを追加
graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)
# 条件分岐: ツール呼び出しがあればtools、なければENDへ
def should_use_tools(state: AgentState):
messages = state["messages"]
last_message = messages[-1]
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
return END
# エッジの追加
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_use_tools)
graph.add_edge("tools", "agent") # ツール実行後はエージェントへ戻る
# コンパイル
app = graph.compile()
実行
# 同期実行
result = app.invoke({
"messages": [HumanMessage(content="東京の明日の天気を調べて、摂氏と華氏で教えて")]
})
for msg in result["messages"]:
print(f"{msg.__class__.__name__}: {msg.content}")
# ストリーミング実行
for event in app.stream({
"messages": [HumanMessage(content="1234 * 5678を計算してください")]
}):
for key, value in event.items():
print(f"[{key}]", value)
中級: ReActエージェント
ReAct(Reasoning + Acting)パターンをLangGraphで実装します:
from typing import TypedDict, Annotated, Literal
from langgraph.graph import StateGraph, END
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
import operator
class ReActState(TypedDict):
messages: Annotated[list[BaseMessage], operator.add]
iterations: int
# Claudeをベースにしたエージェント
llm = ChatAnthropic(model="claude-sonnet-4-6")
SYSTEM_PROMPT = """あなたは役立つAIアシスタントです。
タスクを達成するために、利用可能なツールを使ってください。
思考 → 行動 → 観察のサイクルで問題を解決します。"""
def react_agent(state: ReActState):
messages = state["messages"]
system = SystemMessage(content=SYSTEM_PROMPT)
response = llm.bind_tools(tools).invoke([system] + messages)
return {
"messages": [response],
"iterations": state["iterations"] + 1
}
def should_continue(state: ReActState) -> Literal["tools", "end"]:
messages = state["messages"]
last_message = messages[-1]
# 最大イテレーション数を超えたら終了
if state["iterations"] >= 10:
return "end"
# ツール呼び出しがあれば続行
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
return "end"
# グラフ構築
react_graph = StateGraph(ReActState)
react_graph.add_node("agent", react_agent)
react_graph.add_node("tools", ToolNode(tools))
react_graph.set_entry_point("agent")
react_graph.add_conditional_edges(
"agent",
should_continue,
{"tools": "tools", "end": END}
)
react_graph.add_edge("tools", "agent")
react_app = react_graph.compile()
高度: マルチエージェントシステム
複数の専門エージェントが協調するシステム:
from langgraph.graph import StateGraph, END, MessagesState
from langchain_openai import ChatOpenAI
# 各エージェントの役割
PLANNER_PROMPT = """あなたはプランナーエージェントです。
タスクを分析し、具体的な実行計画を立ててください。
出力形式: {"plan": ["step1", "step2", ...]}"""
RESEARCHER_PROMPT = """あなたはリサーチャーエージェントです。
与えられた情報を調査・分析してください。"""
WRITER_PROMPT = """あなたはライターエージェントです。
調査結果を基に、明確で読みやすいレポートを書いてください。"""
class TeamState(TypedDict):
task: str
plan: list[str]
research: str
report: str
next_agent: str
llm = ChatOpenAI(model="gpt-4o")
def planner_agent(state: TeamState):
"""タスクを分解してプランを立てる"""
import json
messages = [
{"role": "system", "content": PLANNER_PROMPT},
{"role": "user", "content": f"タスク: {state['task']}"}
]
response = llm.invoke(messages)
try:
plan_data = json.loads(response.content)
plan = plan_data.get("plan", [])
except:
plan = [state["task"]]
return {"plan": plan, "next_agent": "researcher"}
def researcher_agent(state: TeamState):
"""情報を調査・収集する"""
plan_text = "\n".join([f"- {step}" for step in state["plan"]])
messages = [
{"role": "system", "content": RESEARCHER_PROMPT},
{"role": "user", "content": f"以下の計画を基に調査してください:\n{plan_text}"}
]
response = llm.invoke(messages)
return {"research": response.content, "next_agent": "writer"}
def writer_agent(state: TeamState):
"""レポートを作成する"""
messages = [
{"role": "system", "content": WRITER_PROMPT},
{"role": "user", "content": f"調査結果:\n{state['research']}"}
]
response = llm.invoke(messages)
return {"report": response.content, "next_agent": "end"}
def route_agent(state: TeamState):
return state["next_agent"]
# マルチエージェントグラフ
team_graph = StateGraph(TeamState)
team_graph.add_node("planner", planner_agent)
team_graph.add_node("researcher", researcher_agent)
team_graph.add_node("writer", writer_agent)
team_graph.set_entry_point("planner")
team_graph.add_conditional_edges(
"planner", route_agent,
{"researcher": "researcher", "end": END}
)
team_graph.add_conditional_edges(
"researcher", route_agent,
{"writer": "writer", "end": END}
)
team_graph.add_conditional_edges(
"writer", route_agent,
{"end": END}
)
team_app = team_graph.compile()
# 実行
result = team_app.invoke({"task": "2026年のAI開発トレンドについてレポートを作成してください"})
print("最終レポート:")
print(result["report"])
Human-in-the-loop(人間の承認)
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, END, interrupt
class ApprovalState(TypedDict):
task: str
draft: str
approved: bool
final: str
def draft_node(state: ApprovalState):
"""下書きを生成"""
llm = ChatOpenAI(model="gpt-4o")
response = llm.invoke([
{"role": "user", "content": f"以下についてドラフトを作成: {state['task']}"}
])
return {"draft": response.content}
def human_review(state: ApprovalState):
"""人間の確認を待つ(ここで処理が中断される)"""
print("\n=== 人間のレビューが必要です ===")
print(f"ドラフト:\n{state['draft']}")
print("================================")
# interrupt()で処理を中断し、人間の入力を待つ
user_input = interrupt("このドラフトを承認しますか? (yes/no)")
return {"approved": user_input.lower() == "yes"}
def finalize(state: ApprovalState):
if state["approved"]:
return {"final": state["draft"]}
else:
return {"final": "ドラフトは却下されました。"}
def route_after_review(state: ApprovalState):
return "finalize"
# チェックポイント(状態保存)
memory = MemorySaver()
approval_graph = StateGraph(ApprovalState)
approval_graph.add_node("draft", draft_node)
approval_graph.add_node("review", human_review)
approval_graph.add_node("finalize", finalize)
approval_graph.set_entry_point("draft")
approval_graph.add_edge("draft", "review")
approval_graph.add_edge("review", "finalize")
approval_graph.add_edge("finalize", END)
approval_app = approval_graph.compile(
checkpointer=memory,
interrupt_before=["review"] # レビュー前に中断
)
状態の永続化
from langgraph.checkpoint.postgres import PostgresSaver
# Postgresで状態を永続化
with PostgresSaver.from_conn_string(
"postgresql://user:pass@localhost/db"
) as checkpointer:
app = graph.compile(checkpointer=checkpointer)
# 設定(スレッドIDで会話を識別)
config = {"configurable": {"thread_id": "user_123"}}
# 実行
result = app.invoke(
{"messages": [HumanMessage(content="こんにちは")]},
config=config
)
# 続きから再開(同じthread_id)
result2 = app.invoke(
{"messages": [HumanMessage(content="先ほどの続きで...")]},
config=config
)
ストリーミングと可視化
# イベントのストリーミング
async def stream_agent(query: str):
async for event in app.astream_events(
{"messages": [HumanMessage(content=query)]},
version="v2"
):
kind = event["event"]
if kind == "on_chat_model_stream":
content = event["data"]["chunk"].content
if content:
print(content, end="", flush=True)
elif kind == "on_tool_start":
print(f"\n[ツール実行: {event['name']}]")
elif kind == "on_tool_end":
print(f"[ツール完了]")
import asyncio
asyncio.run(stream_agent("最新のAI技術を調べてください"))
LangGraph Studio(視覚的なデバッグ)
# LangGraph Studioをローカルで起動
pip install langgraph-cli
langgraph dev
langgraph.jsonの設定:
{
"dependencies": ["."],
"graphs": {
"my_agent": "./agent.py:app"
}
}
まとめ
LangGraphは複雑なAIワークフローを構造化して管理するための強力なフレームワークです。
LangGraphが特に向いているシナリオ:
- 複数ステップにわたる複雑なタスク
- ツール呼び出しを含むエージェント
- 人間の承認が必要なワークフロー
- 状態を保持した長期的な会話
- マルチエージェント協調システム
次のステップ:
- LangSmithでエージェントの動作を可視化・デバッグ
- PostgresSaverで本番環境の状態管理
- LangGraph Studioでリアルタイムデバッグ