LabHub

ブログ

本番エージェント設計パターン 2026 — Supervisor・CodeAct・Plan-Execute・Self-RAG・Handoff・Subagent を実際に組み合わせる方法

한국어English日本語

プロローグ — 入門書のパターン vs 本番のパターン

2023〜2024 年に出回った「LLM エージェント設計パターン」記事のカタログはどれも似ている。ReAct、Chain-of-Thought、Reflection、Plan-and-Solve、Tree of Thoughts、そして最後の章に「マルチエージェント」。きれいに 5 つの箱。デモはちゃんと動く。

問題は、それらの多くが おもちゃのベンチマーク環境で検証された ことだ。HotpotQA の 2〜3 ホップ、GSM8K の算数、ALFWorld のテキストゲーム。そこで意味があったものは、本番 — 5,000 行のコードベースを修正し、社内データを 30 のシステムから引き、決済 API を呼び、30 分のタスクの途中で OOM が起きる環境 — では、別の壊れ方をする。

2026 年の本番エージェントの真実、一行にまとめるなら:単一のパターンで動いているシステムはほとんどない。 Supervisor + CodeAct + Subagent isolation + Hooks を一緒に出荷するか、Plan-Execute + Self-RAG + Reflexion + Handoff を一緒に出荷する。責任は分担されており、各持ち場で別のパターンが仕事をする。

本稿では 2024〜2026 年の本番ワークロードで生き残った 9 つのパターン を整理する。各パターンについて、(1) 何か、(2) いつ光るか、(3) どう崩れるか、(4) 30〜50 行の最小スケッチ。そして最後に — 実際にどう組み合わせるか。


1. Supervisor / Orchestrator

一行定義: 上位(supervisor)エージェントが 1 つ、タスクを受け、複数の専門(specialist)エージェントに分配し、結果を集めて次のステップを決める。

LangGraph の公式「supervisor」ガイドが推奨する構成で、実際に最もよく見るマルチエージェントトポロジ。自由気ままな swarm と違い、誰が次に働くか を 1 つの supervisor が決めるため、全体を制御可能な状態に保てる。

トポロジ

                    ┌────────────┐
   user task ────►  │ Supervisor │
                    └─────┬──────┘
                          │ route by intent / state
              ┌───────────┼───────────┐
              ▼           ▼           ▼
       ┌──────────┐ ┌──────────┐ ┌──────────┐
       │ Research │ │ Coder    │ │ Reviewer │
       │ agent    │ │ agent    │ │ agent    │
       └────┬─────┘ └────┬─────┘ └────┬─────┘
            └──────┬─────┴──────┬─────┘
                   ▼            ▼
              return to Supervisor (loop)

いつ光るか

どう崩れるか

最小スケッチ

# supervisor.py — LangGraph スタイルの最小 supervisor
from typing import Literal, TypedDict
from langgraph.graph import StateGraph, END

class State(TypedDict):
    task: str
    messages: list
    next: Literal["research", "code", "review", "done"]
    hop: int

def supervisor(state: State) -> dict:
    if state["hop"] >= 6:
        return {"next": "done"}
    prompt = f"Task: {state['task']}\nHistory: {state['messages'][-3:]}\nWho works next? research/code/review/done"
    decision = llm(prompt)  # 強制的に enum のひとつだけを返す
    return {"next": decision, "hop": state["hop"] + 1}

def specialist(name):
    def run(state: State) -> dict:
        result = run_agent(name, state["task"], state["messages"])
        # 重要:raw output を丸ごと入れず、要約だけ
        return {"messages": state["messages"] + [{"agent": name, "summary": summarize(result)}]}
    return run

g = StateGraph(State)
g.add_node("supervisor", supervisor)
g.add_node("research", specialist("research"))
g.add_node("code", specialist("code"))
g.add_node("review", specialist("review"))
g.set_entry_point("supervisor")
g.add_conditional_edges("supervisor", lambda s: s["next"],
    {"research": "research", "code": "code", "review": "review", "done": END})
for s in ["research", "code", "review"]:
    g.add_edge(s, "supervisor")
graph = g.compile()

ポイントは supervisor ノードが 決定関数 であること — 仕事自体はせず、誰が働くかだけを決める。


2. CodeAct — アクション言語として Python を使う

一行定義: エージェントの「Action」出力を tool_name(arg) の JSON オブジェクトではなく、実行可能な Python ブロック にする。1 ステップで変数を割り当て、分岐し、ループし、複数ツールを一度に呼べる。

出発点は Yang ら "Executable Code Actions Elicit Better LLM Agents"(CodeAct、2024 年 6 月)。2025 年に Manus チームが自社エージェントのアクション層として採用していることを公開記事で明かした。HuggingFace の smolagents.CodeAgent も同じ思想で作られている。

比較

[古典 ReAct + JSON tool calls]
Action: {"tool": "search", "args": {"q": "kubernetes pod oom"}}
Observation: [50 件]
Action: {"tool": "search", "args": {"q": "kubernetes pod oom limits"}}
Observation: [30 件]
Action: {"tool": "summarize", "args": {"text": "..."}}
→ LLM 呼び出し 3 回、ラウンドトリップ 3 回

[CodeAct]
Action:
```python
results_a = search("kubernetes pod oom")
results_b = search("kubernetes pod oom limits")
merged = list(set(results_a + results_b))[:20]
print(summarize("\n".join(r.snippet for r in merged)))

Observation: [要約 1 つ] → LLM 呼び出し 1 回、ラウンドトリップ 1 回


### いつ光るか

- **複数ツールを組み合わせる** タスク。fetch → 変換 → 集計 → グラフ化が 1 ブロックに収まる。
- **反復が自然な** タスク。100 ファイルの処理を JSON tool call でやると 100 回のラウンドトリップが必要。CodeAct なら `for f in files:` 一行。
- **算術が混ざる** タスク。LLM 自身が計算せず、`numpy` 一行で終わる。
- **状態が積み上がる** タスク。変数を介して前ステップの結果が次ステップに自然に流れる。

### どう崩れるか

- **サンドボックスが漏れた瞬間、セキュリティは終わる。** Python を実行するということは任意コード実行ということ。対策:Docker、Firecracker、E2B、gVisor、最低でも Python`restricted exec` + syscall フィルタ。
- **`import os; os.system('rm -rf /')` の一行で全てが終わる。** LLM がそれを書かない保証はない。対策:import の allowlist + ネットワーク隔離 + ephemeral filesystem。
- **無限ループ。** `while True: search("x")` 一発でトークン請求書が爆発する。対策:実行タイムアウト + 出力トークン上限。
- **デバッグが難しい。** 1 ブロックが 7 ツールを呼び、5 番目で失敗すると、モデルは「何が起きたか」を再構築する必要がある。対策:実行トレースをそのままモデルに返す(行ごとの stdout)。

### 最小スケッチ

```python
# codeact_executor.py — 隔離環境でモデル生成コードを実行
import subprocess, json, textwrap

ALLOWED_IMPORTS = {"json", "re", "math", "datetime", "tools"}

def execute_codeact(code: str, tools: dict, timeout_s: int = 30) -> str:
    # tools はモデルが呼べる関数の dict — コンテナに事前注入
    wrapper = textwrap.dedent(f"""
        import json, sys
        from tools import {", ".join(tools.keys())}
        try:
        {textwrap.indent(code, "            ")}
        except Exception as e:
            print(f"__ERROR__: {{type(e).__name__}}: {{e}}", file=sys.stderr)
    """)
    result = subprocess.run(
        ["docker", "run", "--rm", "-i", "--network=none",
         "--memory=512m", "--cpus=1.0", "codeact-sandbox:latest", "python", "-"],
        input=wrapper.encode(),
        capture_output=True,
        timeout=timeout_s,
    )
    stdout = result.stdout.decode()[-4000:]  # 出力上限
    stderr = result.stderr.decode()[-1000:]
    return f"STDOUT:\n{stdout}\n\nSTDERR:\n{stderr}"

# モデルループ
while not done:
    code = llm_extract_codeact(messages)  # ```python ... ``` ブロックを抽出
    obs = execute_codeact(code, tools)
    messages.append({"role": "user", "content": f"Execution result:\n{obs}"})

肝心なディテール:出力上限(最後の 4 KB のみ)、ネットワーク隔離--network=none の上で必要なドメインのみ追加)、メモリ/CPU 制限タイムアウト


3. Plan-Execute — 計画を先に、その後実行

一行定義: 大きな LLM 呼び出し 1 回で N ステップの plan を作り、その plan に沿って決定論的(または小型モデル)でステップごとに実行する。ReAct が「1 思考、1 行動」を交互に行うのに対し、Plan-Execute は「N ステップを一度に思考、N 行動を実行」。

LangGraph の公式チュートリアルに "plan-and-execute" の例があり、Wang ら "Plan-and-Solve Prompting"(2023)以来、最も安定的な構造として定着している。

トポロジ

[Plan phase]                       [Execute phase]
┌──────────────┐                   ┌──────────────┐
│  Planner LLM │  →  step list  →  │ Executor LLM │  →  results
│  (one shot)  │                   │  (per step)  │
└──────────────┘                   └──────┬───────┘
                  optional re-plan  ◄─────┘
                  (if step fails)

いつ光るか

どう崩れるか

最小スケッチ

# plan_execute.py
def plan(task: str, tools_schema: dict) -> list[dict]:
    prompt = f"""You will be given a task. Decompose it into 3-7 ordered steps.
Each step: {{"id": int, "goal": str, "tool": str | null, "depends_on": [int]}}
Available tools: {list(tools_schema.keys())}
Task: {task}
Return a JSON array."""
    return json.loads(llm(prompt, temperature=0))

def execute_step(step: dict, prior_results: dict) -> dict:
    context = "\n".join(f"step{i}: {prior_results[i]}" for i in step["depends_on"])
    return run_executor_agent(step["goal"], context, tool=step["tool"])

def plan_execute(task: str, tools_schema: dict, max_replans: int = 2):
    steps = plan(task, tools_schema)
    results, replans = {}, 0
    i = 0
    while i < len(steps):
        try:
            results[steps[i]["id"]] = execute_step(steps[i], results)
            i += 1
        except UnexpectedObservation as e:
            if replans >= max_replans: raise
            steps = plan(f"{task}\nSo far: {results}\nProblem: {e}", tools_schema)
            replans += 1
            i = 0  # 新 plan で最初から
    return results

ReAct と比べて LLM 呼び出し回数が 1/3 〜 1/5 になるのが普通。だからコストが最大の利点になる。


4. Self-RAG / Corrective RAG — そもそも検索するかを判断する

一行定義: 全クエリに検索を付ける素の RAG と違い、エージェント自身が 「検索すべきか」「結果は十分か」「もう一度検索すべきか」 を判断する。

源流は Asai ら "Self-RAG"(2023)。その後 Yan ら "Corrective Retrieval Augmented Generation"(CRAG、2024)が「検索結果が悪ければウェブ検索で補完」する形に拡張した。2025〜2026 年には LangGraph チュートリアルの "Adaptive RAG" が事実上の標準リファレンスになった。

グラフ

                        ┌─────────────┐
   query  ───────────►  │ Route LLM   │
                        └──────┬──────┘
                  retrieve?    │     direct?
              ┌────────────────┴────────────────┐
              ▼                                 ▼
        ┌──────────┐                      ┌──────────┐
        │ Retrieve │                      │ Answer   │
        └────┬─────┘                      │ directly │
             ▼                            └──────────┘
        ┌──────────┐
        │ Grade    │  ──→  irrelevant  ──→  rewrite query, retry
        │ docs LLM │  ──→  partial     ──→  web search supplement
        └────┬─────┘  ──→  relevant    ──→  answer
        ┌──────────┐
        │ Answer   │  ──→  hallucinated?  ──→  retry
        │ + check  │  ──→  grounded      ──→  return
        └──────────┘

いつ光るか

どう崩れるか

最小スケッチ

# self_rag.py
def adaptive_rag(query: str, max_hops: int = 3):
    route = llm_classify(query, choices=["direct", "retrieve"])
    if route == "direct":
        return llm_answer(query)

    for hop in range(max_hops):
        docs = retrieve(query, k=5)
        grade = llm_grade(query, docs)  # relevant / partial / irrelevant のいずれか
        if grade == "relevant":
            answer = llm_answer_with_context(query, docs)
            if llm_check_grounding(answer, docs) == "grounded":
                return answer
            # 幻覚なら再試行
            continue
        if grade == "partial":
            web_docs = web_search(query, k=3)
            return llm_answer_with_context(query, docs + web_docs)
        # irrelevant → rewrite
        query = llm_rewrite(query, hint=f"prior docs were off-topic: {summarize(docs)}")
    return llm_answer_with_context(query, docs)  # 最後の試行

5. Reflexion / Self-Critique — verbal-RL の再試行ループ

一行定義: タスクを試み、失敗したら 「なぜ失敗したか」の自然言語 reflection を生成してメモリに保存し、次の試行のシステムプロンプトに注入する。重みは変わらない — "verbal reinforcement learning" だ。

源流は Shinn ら "Reflexion: Language Agents with Verbal Reinforcement Learning"(NeurIPS 2023)。HumanEval で GPT-4 のコーディング成功率を 80% → 91% に押し上げた。

サイクル

     ┌──────────────────────────────────────┐
     │                                      │
     ▼                                      │
  Actor  ──► Action ──► Env ──► Reward      │
  (LLM)                                     │
     ▲                                      │
     │                                      │
  Evaluator (success/fail)                  │
     │                                      │
     ▼                                      │
  Self-Reflection LLM ──► "why I failed" ───┘
                          (memory buffer)

いつ光るか

どう崩れるか

最小スケッチ

# reflexion.py
def reflexion_loop(task: str, max_trials: int = 5):
    reflections = []
    for trial in range(max_trials):
        sys_prompt = base_prompt + "\n\nPast reflections:\n" + "\n".join(reflections[-3:])
        result = actor_llm(task, system=sys_prompt)
        evaluation = evaluator(result, task)  # status: success|fail, plus reason
        if evaluation["status"] == "success":
            return result
        reflection = reflect_llm(task=task, attempt=result, why_failed=evaluation["reason"])
        # 重複除去
        if reflection not in reflections:
            reflections.append(reflection)
        else:
            # 同じ気づきの繰り返し → 角度を変える強制
            reflection = reflect_llm(task=task, attempt=result, hint="try a completely different approach")
            reflections.append(reflection)
    return result  # 最後の試行を返す

6. Handoff — swarm スタイルのマルチエージェント

一行定義: supervisor はいない。各エージェントが自分の仕事が終わったら、次のエージェントに直接「ハンドオフ」する。 2024 年 10 月に公開された OpenAI Swarm ライブラリの核となるパターンで、Anthropic の computer-use デモも結果として同じ形を取る。

Supervisor vs Handoff

[Supervisor]                       [Handoff]
   ┌─────────┐                        A
   │   Sup   │                        │ handoff_to(B)
   └──┬──┬──┬┘                        ▼
      ▼  ▼  ▼                         B
      A  B  C                         │ handoff_to(C)
中央集権の決定                         C
"次は誰?"                              分散決定
                                       "次に渡すのは私が知っている"

OpenAI Swarm では、handoff は「関数のように見えるツール呼び出し」だ。transfer_to_billing() を呼ぶと会話コンテキストが billing エージェントに移る。

いつ光るか

どう崩れるか

最小スケッチ

# swarm.py — OpenAI Swarm 風
def make_agent(name, instructions, tools, handoffs):
    return {
        "name": name,
        "instructions": instructions,
        "tools": tools + [{"name": f"transfer_to_{h.name}", "fn": lambda h=h: h} for h in handoffs],
    }

triage = make_agent("triage",
    "Route user to billing or tech_support based on intent.",
    tools=[], handoffs=[billing, tech])

def run(agent, user_msg, history):
    while True:
        history.append({"role": "user", "content": user_msg})
        out = llm(agent["instructions"], history)
        if out.tool_call and out.tool_call.name.startswith("transfer_to_"):
            agent = out.tool_call.fn()  # handoff!
            continue
        return out, history

7. Specialist routing — intent 分類器でディスパッチ

一行定義: 入力が来ると、まず 安く速い分類器 が intent を捉え、対応する specialist エージェントへルーティングする。Supervisor に似ているが、違いは 分類器が LLM ではなく、小型の分類モデルか決定論的ルール であること。

ほとんどの本番チャットボットがすでにこの形だ。Anthropic の顧客対応エージェント、OpenAI ChatGPT の GPTs ルーティング、Slack/Discord ボットのコマンドルーティング。

トポロジ

user msg
┌─────────────┐
│ Intent      │   ← 小型 BERT 分類器、正規表現、もしくは Haiku のような安い LLM
│ classifier  │
└──┬─────┬────┘
   │     │
   ▼     ▼
agent_a  agent_b  ... agent_n
(各自が自分のシステムプロンプトとツールを持つ)

いつ光るか

どう崩れるか

最小スケッチ

# routing.py
INTENT_AGENTS = {
    "billing": billing_agent,
    "shipping": shipping_agent,
    "account": account_agent,
    "general": general_agent,
}

def classify(text: str) -> tuple[str, float]:
    # 小型分類器 — bert-base finetune か Haiku zero-shot
    logits = small_classifier(text)
    intent, conf = top1(logits)
    return intent, conf

def route(user_msg: str):
    intent, conf = classify(user_msg)
    if conf < 0.7 or intent == "uncertain":
        return general_agent(user_msg)  # フォールバック
    return INTENT_AGENTS[intent](user_msg)

8. Subagent isolation — Claude Code のコンテキスト隔離パターン

一行定義: メインエージェントが大きなタスクの途中で、完全に新しいコンテキストウィンドウ を持つ子エージェント(subagent)を立ち上げ、狭いサブタスクを任せ、結果の要約だけを受け取る。子のコンテキストは親に蓄積されない。

Claude Code を象徴するパターンのひとつで、Anthropic の公式ドキュメントが「subagent / Task tool」として扱っている。OpenAI Agents SDK の subagent や Cursor の「バックグラウンドエージェント」にも同じ概念がある。

[main agent context]
  ┌────────────────────────────────────────────────┐
  │ user task, system prompt, history (240k tok)   │
  │                                                │
  │   ── spawn(subagent, "summarize 18 PRs") ──►   │
  │                                                │
  │            [subagent context]                  │
  │            ┌───────────────────┐               │
  │            │ fresh window      │               │
  │            │ subtask + tools   │               │
  │            │ (returns: 400 tok)│               │
  │            └─────────┬─────────┘               │
  │                      │                         │
  │   ◄────── result summary (400 tok) ──          │
  │                                                │
  │ continues main task (context grew by 400 tok)  │
  └────────────────────────────────────────────────┘

いつ光るか

どう崩れるか

最小スケッチ

# subagent.py
def spawn_subagent(parent_state, subtask: str, tools: list,
                   max_tokens: int = 8000, depth: int = 0):
    if depth >= 2:
        raise RuntimeError("subagent depth limit reached")
    sub_context = {
        "system": SUB_AGENT_SYSTEM,  # 親とは別のシステムプロンプト
        "messages": [{"role": "user", "content": subtask}],
    }
    while not done(sub_context) and tokens(sub_context) < max_tokens:
        sub_context = step(sub_context, tools, depth=depth + 1)
    summary = final_summary(sub_context)  # 子が自分の結果を短く要約する
    return summary  # 親はこれだけを受ける

# 親ループ内
if parent_decides_to_delegate():
    summary = spawn_subagent(state, "summarize the 18 open PRs in the repo", tools=[git_tool, gh_tool])
    state["messages"].append({"role": "tool", "name": "subagent", "content": summary})

肝は 子の raw chain が親の messages に入らないこと。8,000 トークンの子作業が 400 トークンの要約に畳まれる。


9. Hooks + 権限ゲート — LLM ステップ間の決定論的ガードレール

一行定義: エージェントループの各ステップの前/後に 決定論的なコード(hook) が入り、検査/変換/ブロックを行う。LLM は速く柔軟だが信用ならない。hook は遅いが 100% 決定論的。両者が交互に走る。

Claude Code の hook system(PreToolUse, PostToolUse, UserPromptSubmit)が代表例で、LangGraph の interrupt_before / interrupt_after、OpenAI Agents SDK の guardrails、Anthropic Agent Skills の permission gate も同じファミリ。

どこに挟まるか

       ┌──────────────────────────────────────┐
       │              Agent loop              │
       │                                      │
 user  │  ┌──── UserPromptSubmit hook ───┐    │
 msg ──┼─►│ check policy, strip secrets   │   │
       │  └──┬────────────────────────────┘   │
       │     ▼                                │
       │  ┌──── LLM step ──────────┐          │
       │  │ propose tool call      │          │
       │  └──┬─────────────────────┘          │
       │     ▼                                │
       │  ┌── PreToolUse hook ────┐           │
       │  │ allow? deny? mutate?  │           │
       │  └──┬────────────────────┘           │
       │     ▼                                │
       │  ┌── Tool execution ─────┐           │
       │  └──┬────────────────────┘           │
       │     ▼                                │
       │  ┌── PostToolUse hook ───┐           │
       │  │ redact, audit         │           │
       │  └──┬────────────────────┘           │
       │     ▼                                │
       │  loop                                │
       └──────────────────────────────────────┘

いつ光るか

どう崩れるか

最小スケッチ

# hooks.py
def pre_tool_hook(tool: str, args: dict, state) -> tuple[bool, str | None]:
    if tool == "bash":
        cmd = args.get("command", "")
        if "rm -rf" in cmd or "curl http" in cmd:
            return False, "blocked: dangerous command pattern"
        if "$SECRET" in cmd or re.search(r"sk-[a-zA-Z0-9]{20,}", cmd):
            return False, "blocked: secret leak detected"
    if tool == "write_file":
        if not args["path"].startswith(state["allowed_root"]):
            return False, f"blocked: outside allowed root {state['allowed_root']}"
    return True, None

def agent_step(state):
    proposal = llm_propose_action(state)
    allowed, reason = pre_tool_hook(proposal.tool, proposal.args, state)
    if not allowed:
        state["messages"].append({"role": "tool", "content": f"BLOCKED: {reason}"})
        return state
    result = execute(proposal)
    audit_log(proposal, result)  # post hook
    state["messages"].append({"role": "tool", "content": result})
    return state

10. 実システムはこれらをどう組み合わせるか

ここからが本題だ。単一パターンで動く本番システムはほぼない。2026 年のよく出来たエージェントは 3〜4 のパターンを合成 する。

ケース 1 · コーディングエージェント(Claude Code、Cursor、Devin 系)

[user task]
┌────────────────────────────────────────────────┐
│  Main agent (loop) — Plan-Execute              │
│   - Reads task, builds a 3-7 step plan         │
│   - Per step:                                  │
│      ┌─── PreToolUse hook ────┐                │
│      │ permission + policy    │                │
│      └─────────┬──────────────┘                │
│                ▼                               │
│      ┌─── Tool / CodeAct exec ────┐            │
│      └─────────┬──────────────────┘            │
│                ▼                               │
│      ┌─── PostToolUse hook ───┐                │
│      │ audit, redact          │                │
│      └─────────┬──────────────┘                │
│                ▼                               │
│      need deep exploration? ──► spawn subagent │
│                                  (isolated ctx)│
│                ▼                               │
│   - On step failure: Reflexion-style retry     │
└────────────────────────────────────────────────┘

組み合わせ:Plan-Execute + Subagent isolation + Hooks + Reflexion(4 個)。

ケース 2 · 顧客サポートエージェント(Anthropic、Intercom Fin、Linear Asks)

[user msg]
┌──── Specialist routing (intent classifier) ────┐
│       ▼          ▼         ▼          ▼        │
│   billing    shipping   account    general     │
│      │          │         │           │        │
└──────┼──────────┼─────────┼───────────┼────────┘
       ▼          ▼         ▼           ▼
     [each specialist: Self-RAG over internal KB]
     can resolve? ──── yes ──► reply
       no
     Handoff to human OR another specialist

組み合わせ:Specialist routing + Self-RAG + Handoff(3 個)。

ケース 3 · データ分析エージェント(Manus、OpenAI Code Interpreter)

[user question over CSV / DB]
Supervisor (Manus-style)
    ├─► Researcher subagent  (subagent isolation)
    │     └─ Self-RAG over schemas/docs
    ├─► Analyst subagent
    │     └─ CodeAct (writes pandas / SQL in Python blocks)
    │     └─ executes in sandbox with Hooks (network=none, fs scoped)
    └─► Reporter subagent
          └─ writes markdown summary

組み合わせ:Supervisor + Subagent isolation + CodeAct + Self-RAG + Hooks(5 個)。

ケース 4 · OS 自動化エージェント(Anthropic computer use)

[user goal: "book a flight"]
Plan phase: high-level plan (find site → search → fill form → confirm)
Execute phase: action loop
   - screenshot
   - decide click/type/key
   - PreActionHook: dangerous zone? (banking, settings) → halt + ask user
   - execute
   - PostActionHook: screenshot diff
   - on unexpected screen → Reflexion-style replan

組み合わせ:Plan-Execute + Hooks + Reflexion

組み合わせマトリックス

                        単純   | 中程度        | 非常に複雑
入門書                  ReAct  | ReAct+Refl    | (ベンチマークここで止まる)
実本番推奨              Routing| Plan-Exec+    | Supervisor+Subagent+
                       +Self-  | Hooks+        | CodeAct+Hooks+Reflexion
                        RAG    | Subagent      |

11. アンチパターン

  1. 全パターンの寄せ集め。 「supervisor の中にまた supervisor、subagent の中に plan-execute...」デバッグ不可能になる。追加する前に削れ が黄金律。
  2. Hooks なしの CodeAct。 数日のうちに誰かが rm -rf を書くか、秘密情報を漏らす。CodeAct はサンドボックス + hooks が前提条件。
  3. 全 specialist の raw 出力を supervisor に累積。 5 ラウンドで 240k トークン使い切る。
  4. ハードキャップなしの Reflexion。 12 回の再試行が 3,000 ドルの請求書として返ってくる。
  5. Plan-Execute の plan を絶対視。 plan は仮説に過ぎない。Execute で plan と異なる観測が出たら replan を許可せねばならない。
  6. Handoff 時にコンテキストをそのまま渡す。 毎回新エージェントが同じコンテキストを再読するため、コストが N 倍になる。要約 + 変数パケット のみ渡す。
  7. Self-RAG の grader を本体と同じモデルにする。 Grader は安い別モデルの方がよい。
  8. routing の confidence を無視。 confidence 0.51 の入力をそのまま specialist に流す。fallback トラックは必須。

12. パターン選択チェックリスト

新しいエージェントを作るとき、自問すべき質問:

自分の組み合わせマトリックスを描け。パターンは道具に過ぎない。

エピローグ — パターンカタログではなくシステム思考

2024 年の記事が「ReAct を使え」と言ったなら、2026 年の答えはもっと静かだ — 「あなたのワークロードではどの失敗が多いか?」 から始まる。コンテキストが暴走するなら Subagent。検索がまばらなら Self-RAG。段階が散らかっているなら Plan-Execute。取り返しのつかないアクションがあるなら Hooks。責任が広すぎるなら Specialist routing か Supervisor。

パターンはカードデッキで、あなたが手札を組む。手札はシステム次第。そして一度に一枚ずつ足せ — それが最大の教訓。

次回は、これらの組み合わせの 評価 を扱う。Inspect AI、Phoenix、LangSmith でマルチパターンエージェントの回帰をどう捕まえるか、そして swarm / handoff のような非決定的トポロジで「正解」をどう定義するか。

参考 / References

コメント

まだコメントはありません。

ログインするとコメントできます