第8章 エージェントデザインパターン

更新日:2025年12月16日

本章では、AIエージェント開発における主要なデザインパターンについて解説する。シングルエージェントパターン(ReAct、Plan-and-Execute、Reflection)からマルチエージェントパターン(Supervisor、Hierarchical)まで、各パターンの特徴と適用場面、LangGraphによる実装例を学ぶ。

1. パターン概要

エージェントデザインパターンは、LLMエージェントの構造と動作を定義する設計テンプレートである。適切なパターンを選択することで、タスクに最適なエージェントを効率的に構築できる。

Table 1. 主要なエージェントデザインパターン

カテゴリ パターン 特徴 適用場面
シングル ReAct 思考→行動→観察のループ 汎用タスク
Plan-and-Execute 計画立案後に実行 複雑なタスク
Reflection 自己評価と改善 品質重視タスク
マルチ Supervisor 監督者が作業者を管理 専門性の分離
Hierarchical 階層的な管理構造 大規模・複雑なタスク

これらのパターンは排他的ではなく、組み合わせて使用することも多い。例えば、Supervisor内の各作業者がReActパターンで動作する構成などが考えられる。

2. シングルエージェント

シングルエージェントパターンは、1つのLLMがタスクを遂行する構成である。シンプルな構造で、多くのユースケースに対応できる。

2.1 ReAct

ReAct(Reasoning and Acting)は、第6章で解説した基本パターンである。LLMが「思考」「行動」「観察」を繰り返してタスクを遂行する。

Fig 1. ReActパターンのグラフ構造

     ┌──────────┐
     │  START   │
     └────┬─────┘
          │
     ┌────▼─────┐
┌───▶│  agent   │◀──────┐
│    └────┬─────┘       │
│         │             │
│    ┌────▼─────┐       │
│    │ 条件分岐  │       │
│    └────┬─────┘       │
│         │             │
│   ┌─────┴─────┐       │
│   │           │       │
│ ┌─▼──┐    ┌──▼──┐    │
│ │END │    │tools│────┘
│ └────┘    └─────┘
└─────────────────────────

Table 2. ReActパターンの特徴

項目 内容
利点 シンプル、汎用性が高い、推論過程が追跡可能
欠点 長いタスクでループする可能性、計画性に欠ける
適用場面 情報検索、簡単な計算、Q&A

2.2 Plan-and-Execute

Plan-and-Executeパターンは、タスクを最初に計画(分解)してから、各サブタスクを順次実行する構成である。複雑なタスクに対して、ReActよりも構造化されたアプローチを提供する。

Fig 2. Plan-and-Executeのグラフ構造

     ┌──────────┐
     │  START   │
     └────┬─────┘
          │
     ┌────▼─────┐
     │ planner  │ ←── タスクを分解
     └────┬─────┘
          │
     ┌────▼─────┐
┌───▶│ executor │ ←── サブタスクを実行
│    └────┬─────┘
│         │
│    ┌────▼─────┐
│    │ 条件分岐  │
│    └────┬─────┘
│         │
│   ┌─────┴─────┐
│   │           │
│ ┌─▼──┐    ┌──▼───┐
│ │END │    │次タスク│
│ └────┘    └───────┘
└─────────────────────

Fig 3. Plan-and-Executeの実装例

from langgraph.graph import StateGraph, START, END
from typing import TypedDict, List

class PlanExecuteState(TypedDict):
    task: str
    plan: List[str]
    current_step: int
    results: List[str]

def planner(state: PlanExecuteState) -> dict:
    """タスクを分解して計画を立てる"""
    task = state["task"]
    # LLMでタスクを分解
    plan = llm_plan(task)  # ["ステップ1", "ステップ2", ...]
    return {"plan": plan, "current_step": 0}

def executor(state: PlanExecuteState) -> dict:
    """現在のステップを実行"""
    step = state["plan"][state["current_step"]]
    result = llm_execute(step)
    return {
        "results": [result],
        "current_step": state["current_step"] + 1
    }

def should_continue(state: PlanExecuteState) -> str:
    if state["current_step"] >= len(state["plan"]):
        return END
    return "executor"

graph = StateGraph(PlanExecuteState)
graph.add_node("planner", planner)
graph.add_node("executor", executor)
graph.add_edge(START, "planner")
graph.add_edge("planner", "executor")
graph.add_conditional_edges("executor", should_continue)

2.3 Reflection

Reflectionパターンは、エージェントが自身の出力を評価し、改善するサイクルを持つ構成である。コード生成、文章作成など、品質が重要なタスクに有効である。

Fig 4. Reflectionのグラフ構造

     ┌──────────┐
     │  START   │
     └────┬─────┘
          │
     ┌────▼─────┐
┌───▶│ generate │ ←── 初期出力を生成
│    └────┬─────┘
│         │
│    ┌────▼─────┐
│    │ reflect  │ ←── 出力を評価
│    └────┬─────┘
│         │
│    ┌────▼─────┐
│    │ 条件分岐  │
│    └────┬─────┘
│         │
│   ┌─────┴─────┐
│   │           │
│ ┌─▼──┐    ┌──▼───┐
│ │END │    │revise │───┐
│ └────┘    └───────┘   │
└───────────────────────┘

Fig 5. Reflectionの実装例

class ReflectionState(TypedDict):
    task: str
    draft: str
    critique: str
    revision_count: int

def generate(state: ReflectionState) -> dict:
    """初期ドラフトを生成"""
    draft = llm_generate(state["task"])
    return {"draft": draft}

def reflect(state: ReflectionState) -> dict:
    """ドラフトを批評"""
    critique = llm_critique(state["draft"])
    return {"critique": critique}

def revise(state: ReflectionState) -> dict:
    """批評に基づいて修正"""
    revised = llm_revise(state["draft"], state["critique"])
    return {
        "draft": revised,
        "revision_count": state["revision_count"] + 1
    }

def should_continue(state: ReflectionState) -> str:
    # 最大3回の修正、または批評が良好なら終了
    if state["revision_count"] >= 3:
        return END
    if "excellent" in state["critique"].lower():
        return END
    return "revise"

3. マルチエージェント

マルチエージェントパターンは、複数のLLMエージェントが協調してタスクを遂行する構成である。専門性の分離、並列処理、複雑なワークフローの実現に適している。

3.1 Supervisor

Supervisorパターンは、監督者(Supervisor)エージェントが複数の作業者(Worker)エージェントを管理する構成である。監督者がタスクを適切な作業者に振り分け、結果を統合する。

Fig 6. Supervisorパターンの構造

          ┌────────────┐
          │ Supervisor │
          └──────┬─────┘
                 │
        ┌────────┼────────┐
        │        │        │
   ┌────▼───┐┌───▼───┐┌───▼────┐
   │Researcher││Coder  ││Writer  │
   └─────────┘└───────┘└────────┘
   検索担当    コード担当  文章担当

Fig 7. Supervisorの実装例

from langgraph.graph import StateGraph, START, END
from typing import Literal

class SupervisorState(TypedDict):
    messages: Annotated[list, add]
    next_worker: str

# 作業者の定義
workers = ["researcher", "coder", "writer"]

def supervisor(state: SupervisorState) -> dict:
    """次に作業する作業者を決定"""
    response = llm_with_tools.invoke([
        SystemMessage(content=f"作業者: {workers}. 次に誰が作業すべきか決定してください。"),
        *state["messages"]
    ])
    return {"next_worker": response.next_worker}

def researcher(state: SupervisorState) -> dict:
    """検索・調査を担当"""
    result = research_agent.invoke(state["messages"])
    return {"messages": [AIMessage(content=result, name="researcher")]}

def coder(state: SupervisorState) -> dict:
    """コード作成を担当"""
    result = coding_agent.invoke(state["messages"])
    return {"messages": [AIMessage(content=result, name="coder")]}

def writer(state: SupervisorState) -> dict:
    """文章作成を担当"""
    result = writing_agent.invoke(state["messages"])
    return {"messages": [AIMessage(content=result, name="writer")]}

def route_to_worker(state: SupervisorState) -> str:
    return state["next_worker"]

# グラフ構築
graph = StateGraph(SupervisorState)
graph.add_node("supervisor", supervisor)
graph.add_node("researcher", researcher)
graph.add_node("coder", coder)
graph.add_node("writer", writer)

graph.add_edge(START, "supervisor")
graph.add_conditional_edges("supervisor", route_to_worker, 
    {w: w for w in workers} | {END: END})

for worker in workers:
    graph.add_edge(worker, "supervisor")

3.2 Hierarchical

Hierarchicalパターンは、Supervisorパターンを階層的に拡張したものである。大規模なタスクを複数のチームに分割し、各チームに監督者を配置する。

Fig 8. Hierarchicalパターンの構造

              ┌─────────────┐
              │ Top Manager │
              └──────┬──────┘
                     │
         ┌───────────┼───────────┐
         │           │           │
    ┌────▼────┐ ┌────▼────┐ ┌────▼────┐
    │Research │ │Dev Team │ │QA Team  │
    │  Lead   │ │  Lead   │ │  Lead   │
    └────┬────┘ └────┬────┘ └────┬────┘
         │           │           │
    ┌────┴────┐ ┌────┴────┐ ┌────┴────┐
    │Workers  │ │Workers  │ │Workers  │
    └─────────┘ └─────────┘ └─────────┘

Table 3. マルチエージェントパターンの比較

パターン 構造 利点 適用場面
Supervisor フラット シンプル、柔軟 3〜5人の専門家
Hierarchical 階層的 スケーラブル 大規模チーム

4. 実装例

実際のプロジェクトでの適用例を示す。

Table 4. ユースケース別の推奨パターン

ユースケース 推奨パターン 理由
カスタマーサポート ReAct + RAG シンプル、FAQ検索との連携
レポート作成 Plan-and-Execute 構造化された出力が必要
コード生成 Reflection 品質向上のための反復
研究アシスタント Supervisor 検索・分析・執筆の分担
ソフトウェア開発 Hierarchical 設計・実装・テストの階層

Fig 9. パターン選択のフローチャート

タスクの複雑さは?
├── シンプル → ReAct
└── 複雑
    ├── 計画が必要?
    │   └── Yes → Plan-and-Execute
    └── 品質向上が必要?
        └── Yes → Reflection
            
専門性の分離が必要?
├── No → シングルエージェント
└── Yes → マルチエージェント
    ├── チーム数 ≤ 5 → Supervisor
    └── チーム数 > 5 → Hierarchical

エージェントデザインパターンは、タスクの性質、求められる品質、スケーラビリティを考慮して選択する。最初はシンプルなReActから始め、必要に応じてより複雑なパターンに移行することを推奨する。

参考・免責事項
本コンテンツは2025年12月時点の情報に基づいて作成されています。エージェントデザインパターンは研究が活発な分野であり、新しいパターンが提案される可能性があります。参考文献: Liu et al. (2024) "Agent Design Pattern Catalogue"