はじめに
RAG(Retrieval-Augmented Generation)を実運用に載せていくと、単一のエージェント(「検索して→生成する」の1本道パイプライン)ではすぐに限界が見えてきます。
- 検索対象のドメインが増えるほど、1つのプロンプト・1つのツールセットに詰め込みすぎて精度が落ちる
- 「手順を丸ごと返したい」「症状から逆引きしたい」のように、同じ知識ベースでも検索の意図が複数ある
- 出典なしの回答がハルシネーションのリスクになる(検証ステップが要る)
- 検索対象のデータソースが増えるたびに、1つの巨大なコンテキストウィンドウが埋まっていく
この記事では、RAGをどういう軸でマルチエージェントに分割するかと、Claude APIでどう実装するか(Tool Use / MCP / Managed Agentsのマルチエージェント機能)を、実務で使える粒度でまとめます。
1. なぜマルチエージェント化するのか
先に釘を刺しておくと、マルチエージェント化は常に正解ではありません。Anthropic自身のエージェント設計ガイドでも「最もシンプルな構成から始めよ」が原則です。単一のAPI呼び出しやワークフロー(コード側で制御するツール呼び出しの連鎖)で足りるなら、それで十分です。
マルチエージェント化を検討すべきサインは以下のようなケースです。
| サイン | 内容 |
|---|---|
| 並列化できる独立したタスクがある | 複数のデータソースを横断検索する、複数の観点でレビューする、など |
| 1つのエージェントのコンテキストが「読み込み」で埋まる | 大量の文書を読んでから要約する、といった処理を1本のスレッドでやると本流の思考トークンを圧迫する |
| 専門性の異なるタスクが混在している | 検索クエリの意図解釈、ドメイン別の検索、事実検証、生成、で求められる能力・コストが違う |
| 失敗時の影響が大きく、検証ステップが要る | 医療・法務・安全手順など、もっともらしい創作が事故に直結する領域 |
逆に言うと、「検索して埋め込んで生成するだけ」のQ&A程度なら、無理にマルチエージェントにする必要はありません。複雑さはコストとレイテンシに直結します。
2. どんな軸で分けるか
マルチエージェントRAGの設計で最初に決めるべきは「何を基準にエージェントを分割するか」です。実務でよく使う4つの軸を整理します。
2-1. 機能軸(パイプライン分割)
検索〜生成のパイプラインを、工程ごとに専門エージェントへ分割する方式です。
クエリ書き換え → 検索(Retriever) → 再ランキング(Reranker) → 生成(Generator) → 検証(Verifier)
- クエリ書き換えエージェント: ユーザーの曖昧な自然文を、検索に強いクエリへ変換する(同義語展開、意図分解)
- 検索エージェント: ベクトル検索・キーワード検索・SQL検索など、複数の検索手段を使い分ける
- 再ランキングエージェント: 取得候補を目的(網羅性 / 精度)に応じて絞り込む
- 生成エージェント: 最終回答を書く
- 検証エージェント: 生成結果が検索結果と矛盾していないか、出典が正しいかをチェックする
一番オーソドックスな分け方ですが、工程間の受け渡しが多くなるとレイテンシが積み上がるので、後述のOrchestrator-Workerパターンで「本当に別エージェントにすべき工程」を絞り込むのがコツです。
2-2. データソース軸
検索対象のデータソース(コーパス)ごとにエージェント(=検索ツール)を分ける方式です。
- 社内Wiki検索エージェント
- 製品マニュアル(ベクトルDB)検索エージェント
- 構造化マスタ(DB)検索エージェント
- Web検索エージェント
これは実装上、各データソースを1つのツール(できればMCPサーバー)として切り出すのと相性が良く、後述の「4-2 MCPで検索エージェントを分離する」で詳しく扱います。データソースが増えても、オーケストレーター側のプロンプトを変えずにツールを追加していけるのが利点です。
筆者が実際に設計したLINE向けRAGボットでも、「レシピ(手順)」「道具・資機材」「失敗パターン」という3種類のマスタを、検索用チャンクとしては同じインデックスに投影しつつ、metadata.type で種別を分けて集約する設計にしています。これをエージェントレベルまで分離すると、まさにこのデータソース軸の分割になります。
2-3. ドメイン・業務軸
同じ知識ベースでも、業務カテゴリ(ドメイン)ごとに専門エージェントを立てる方式です。例えば「造園」「調理」「接客」のように業務が分かれている場合、それぞれに特化したシステムプロンプト・ツールセット・温度パラメータを持つエージェントに振り分けます。
ドメインをまたぐルーティングは、ナレッジ量が少ないうちは絞り込みすぎて精度を下げるリスクがあるため、データが十分に溜まってから有効化するのが安全です(最初は「全ドメイン共通の1エージェント + agent_type をデータとして保持だけしておく」形にしておき、後からルーティングを有効化できるようにしておく設計がおすすめです)。
2-4. ロール軸(Orchestrator-Worker + Critic)
上記3つの分割軸を、実行時にどう組み合わせるかの骨格になるのがこのロール軸です。次章で詳しく扱います。
| 分割軸 | 分けるもの | 向いているケース |
|---|---|---|
| 機能軸 | パイプラインの工程 | 各工程の複雑さ・失敗率が異なる |
| データソース軸 | 検索対象のコーパス | データソースが増減する、権限が異なる |
| ドメイン軸 | 業務カテゴリ | 専門用語・トーンが業務ごとに大きく違う |
| ロール軸 | 実行時の役割(指揮 / 実働 / 検証) | 並列実行・コンテキスト分離をしたい |
実際の設計では、これらを組み合わせて使います。「ロール軸でOrchestrator-Workerの骨格を作り、Workerをデータソース軸で複数用意する」というのが最もよくあるパターンです。
3. Orchestrator-Workerパターンの基本形
マルチエージェントRAGの中核になる構成が Orchestrator-Worker(指揮者-作業者)パターン です。
役割分担は次のとおりです。
- Orchestrator: ユーザーの質問を受け取り、どのWorkerに何を検索させるかを計画し、複数Workerの結果を統合する。自分では検索の詳細を持たない。
- Worker: 1つのデータソース・1つの検索手段に特化する。独立したコンテキストウィンドウを持つため、大量の文書を読んでもOrchestratorの思考トークンを圧迫しない。並列実行できる。
- Critic(任意): 生成結果が検索結果と矛盾していないか、出典が正しいかを別視点でチェックする。ハルシネーション対策として、生成担当と検証担当を分離するのは効果が大きいポイントです。
ポイントは「Workerは互いの会話履歴を共有しない」ことです。Orchestratorが渡すタスク文の中に、必要な文脈(検索意図・制約・回答フォーマット)を全部書く必要があります。Workerは自分の会話履歴しか見えないので、「さっきの続きをやって」は通じません。
4. 実装方法
Claude APIでこのパターンを組む方法は、成熟度に応じて3段階あります。どれも「Orchestrator-Worker」という骨格は同じで、Workerをどう呼び出すかが違います。
4-1. Tool Useでオーケストレーターを組む(自前でループを書く場合)
最もシンプルな実装は、各Workerを「ツール」としてOrchestratorに公開する方法です。Python SDKの tool_runner(ベータ)を使うと、ツール呼び出しのループを自前で書かずに済みます。
import anthropic
from anthropic import beta_tool
client = anthropic.Anthropic()
@beta_tool
def search_manual(query: str) -> str:
"""製品マニュアルのベクトルDBを検索し、関連する手順を返す。
Args:
query: 検索クエリ(自然文でよい)
"""
# 実際にはベクトルDB(例: pgvector, Pinecone等)への問い合わせ
hits = manual_vector_store.search(query, top_k=5)
return "\n\n".join(f"[出典: {h.title}]\n{h.text}" for h in hits)
@beta_tool
def search_faq(query: str) -> str:
"""よくある質問(FAQ)ナレッジベースを検索する。"""
hits = faq_vector_store.search(query, top_k=5)
return "\n\n".join(f"[出典: FAQ-{h.id}]\n{h.text}" for h in hits)
# Orchestrator: どのWorkerを呼ぶか自分で判断させる
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=16000,
thinking={"type": "adaptive"},
output_config={"effort": "high"},
system=(
"あなたは検索オーケストレーターです。ユーザーの質問に答えるために、"
"必要なツールを呼び出して根拠を集めてから回答してください。"
"回答には必ず出典を明記してください。"
),
tools=[search_manual, search_faq],
messages=[{"role": "user", "content": "芝刈り機のエンジンがかからないときの対処法は?"}],
)
for message in runner:
for block in message.content:
if block.type == "text":
print(block.text)
この構成では、search_manual と search_faq が「データソース軸で分割されたWorker」に相当します。Orchestratorのシステムプロンプトには検索の詳細を書かず、「必要なツールを呼び出せ」とだけ指示するのがコツです。個別Workerの検索ロジック(どのDBを使うか、どうスコアリングするか)はツール関数の中に隠蔽されるため、Orchestrator側のプロンプトを変えずにデータソースを増減できます。
Worker側を重い処理(大量文書の要約など)にする場合は、そのツール関数の中でさらに別モデル(例:
claude-haiku-4-5)を呼び出す「入れ子構成」にすると、Orchestratorは軽い呼び出しコストで済みます。
4-2. 検索エージェントをMCPサーバーとして切り出す
上記の search_manual / search_faq をローカル関数として実装する代わりに、それぞれを独立したMCPサーバーとして公開すると、Worker(検索エージェント)を実装レベルでOrchestratorから完全に切り離せます。
MCPサーバー化のメリットは次のとおりです。
- 検索ロジックのデプロイ・バージョン管理をOrchestratorのコードと独立させられる
- 同じ検索エージェントを複数のOrchestrator(複数プロダクト)から再利用できる
- 認証情報(DB接続情報など)をOrchestrator側のプロセスに持たせずに済む
Claude APIの MCP connector を使うと、Anthropic側がMCPサーバーへの接続を代行してくれます。
response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=16000,
betas=["mcp-client-2025-11-20"],
mcp_servers=[
{"type": "url", "name": "manual-search", "url": "https://internal.example.com/mcp/manual"},
{"type": "url", "name": "faq-search", "url": "https://internal.example.com/mcp/faq"},
],
tools=[
{"type": "mcp_toolset", "mcp_server_name": "manual-search"},
{"type": "mcp_toolset", "mcp_server_name": "faq-search"},
],
system="あなたは検索オーケストレーターです。必要なツールで根拠を集めてから回答し、出典を明記してください。",
messages=[{"role": "user", "content": "芝刈り機のエンジンがかからないときの対処法は?"}],
)
mcp_servers に検索エージェントのエンドポイントを列挙し、tools に対応する mcp_toolset を紐付けるだけです。データソースが増えたら、この配列に1行追加するだけでOrchestratorが新しい検索エージェントを使えるようになります。データソース軸の分割をそのままインフラ構成に落とし込めるのがMCP化の強みです。
4-3. 本番運用ではManaged Agentsのマルチエージェント機能を使う
さらに一歩進めて、「Workerを完全に独立したエージェント(別モデル・別コンテキスト・並列実行)として動かしたい」場合は、Claude の Managed Agents(β) が提供するマルチエージェントセッションが選択肢になります。
# Worker: 読み込み中心の検索エージェント(安いモデルでよい)
worker = client.beta.agents.create(
name="Web researcher",
description="読み取り専用の検索エージェント。1つの検索タスクを渡すと、根拠付きで簡潔に報告する。",
model="claude-haiku-4-5",
system="与えられた質問に絞って検索・調査し、出典(URLまたはファイルパス)付きで簡潔に報告してください。",
tools=[{
"type": "agent_toolset_20260401",
"default_config": {"enabled": False},
"configs": [{"name": n, "enabled": True} for n in ("read", "glob", "grep", "web_fetch", "web_search")],
}],
)
# Orchestrator: Workerを配下に持つ司令塔エージェント
orchestrator = client.beta.agents.create(
name="RAG orchestrator",
model="claude-opus-5",
system=(
"独立した検索が必要な問い合わせは Web researcher に委譲してください。"
"複数の観点が必要な場合は並列で複数タスクを渡し、結果を統合・検証してから回答してください。"
),
tools=[{"type": "agent_toolset_20260401"}],
multiagent={"type": "coordinator", "agents": [worker.id, {"type": "self"}]},
)
session = client.beta.sessions.create(agent=orchestrator.id, environment_id=env.id)
multiagent にWorkerのロースターを渡すだけで、OrchestratorはWorkerへタスクを委譲する専用ツールを自動的に持ちます。Workerは別スレッド(別コンテキストウィンドウ)で並列実行され、結果だけがOrchestratorに戻ってくるため、読み込みの多い検索処理でOrchestratorのコンテキストが汚染されないのが最大の利点です。安いモデル(Haiku)をWorkerに、判断力が要るOrchestratorに高性能モデル(Opus)を割り当てるコスト設計もしやすくなります。
5. 実践Tips
出典検証はCriticエージェントに分離する
生成担当のエージェントに「出典を明記して」と指示するだけでは、もっともらしい捏造(ハルシネーション)を完全には防げません。生成結果と検索結果を突き合わせて検証する専用のCriticエージェント(または前述の advisor 相当の仕組み)を挟むと、検証の抜け漏れを構造的に減らせます。特に、誤った回答が実害につながる領域(安全手順・医療・法務など)では、生成用エージェントと検証用エージェントで温度パラメータを分ける(生成は低め、検証はさらに厳密に)のも有効です。
冗長なWorkerを増やしすぎない
「似たような検索エージェントを、微妙な条件違いだけで複数用意してしまう」のはよくある失敗です。ツールセットも指示もほぼ同じ2つのWorkerがあるなら、それは1つのWorkerにして、違いを引数(フィルタ条件)として渡すべきサインです。エージェントの数はそのままオーケストレーション側の複雑さとコストに直結します。
いきなり全部を分割しない
最初から「機能軸×データソース軸×ドメイン軸」を全部かけ合わせて設計すると、デバッグも運用も破綻します。おすすめの順番は次のとおりです。
- まず単一エージェント + Tool Useで動くものを作る
- データソースが増えてきたら、データソース軸でWorkerを切り出す(4-1 or 4-2)
- コンテキスト圧迫やレイテンシが問題になったら、Managed Agentsの並列Worker化を検討する(4-3)
- 誤答の実害が大きい領域だけ、Criticエージェントを追加する
まとめ
- マルチエージェントRAGは「機能軸」「データソース軸」「ドメイン軸」「ロール軸」の4つの切り口で設計を考えると整理しやすい
- 実行時の骨格は Orchestrator(指揮)→ Worker(並列実働)→ Critic(検証)という Orchestrator-Worker パターンが基本形
- 実装は Tool Use(自前ループ)→ MCPによる検索エージェントの切り出し → Managed Agentsのマルチエージェントセッション、と段階的に成熟させていける
- 複雑さは常にコストなので、「本当にマルチエージェントが必要か」を都度問い直す