はじめに
本記事では、Mastra(TypeScript製のエージェントフレームワーク)で組んでいた 役割ごとに6個の専門エージェントを並列実行する構成 が運用面で詰まり、po3rin氏のスライド「検索設計から推論設計への重心移動とRecall-First Retrieval」から着想を得て 「情報収集 + 推論」の2層モデル に作り直した経験を書きます。マルチエージェントを役割ごとに増やしたら辛くなった人向けに、設計の重心をどこに置くか を整理する内容です。
1. 6個のエージェントで作っていた頃の話
最初に組んだ構成はとてもシンプルな発想でした。
様々な情報を必要としているエージェントを作成している中で、領域や情報ごとに専門エージェントを作り、それぞれが独立に情報を集めて、最後に統合エージェントが束ねればいい。
これは当然のように思いつくアイデアで、Anthropicの"Building Effective Agents"にもRoutingやOrchestrator-Workersといったパターンが紹介されています。なので私も6個のエージェントを用意し、Mastra Workflowの.parallel()で同時に走らせ、最後に統合エージェントで1つの回答にまとめるパイプラインを組みました。
import { createWorkflow, createStep } from "@mastra/core/workflow";
export const oldWorkflow = createWorkflow({
id: "old-multi-agent",
inputSchema: z.object({ question: z.string() }),
outputSchema: z.object({ answer: z.string() }),
})
.parallel([AgentA, AgentB, AgentC, AgentD, AgentE, AgentF])
.then(IntegratorAgent)
.commit();
.parallel()で6個を同時に走らせ、.then()で統合エージェントに渡す、という素直な書き方です。コード上は綺麗に見えるのですが、運用に入っていくつもの課題が出てきました。
- LLM呼び出し回数:質問1件につき並列6個 + 統合1個で 合計7回。レイテンシ・コスト・レート制限の三方向から効いてきます
- 統合ロジックの認知負荷:統合エージェントは6個の出力に部分矛盾があると重み付けが難しく、ぼやけた回答を返しがちでした
- コード量が線形に増える:新しい領域を追加するたびにAgent作成・統合との繋ぎ込み・Workflow編集… 変更箇所が線形に増えます
- デバッグがきつい:並列実行は「どこで失敗したか」は掴めても、「どこを改良すべきか」を読み解くのが面倒でした
つまり「役割ごとに専門Agentを並べる」設計は、綺麗に見えて運用に入った際に線形にコスト増加する構造 だったわけです。
2. Recall-First Retrievalから着想を得る
このマルチエージェント増殖問題に対して、po3rinさんのスライド『検索設計から推論設計への重心移動とRecall-First Retrieval』が、考え方の突破口を示してくれました。
スライドの中心命題はこうです。
RAGの品質改善の重心は、「検索設計」から「推論設計」へ移りつつある。
従来のRAGは「いかに正確なドキュメントだけを検索段階で拾うか」に労力を割いてきましたが、LLMが十分強くなった現在、検索段階ではむしろ「漏れなく取る(高recall)」ほうが効き、LLMが後段で不正確な情報を濾別・補完する というパラダイムシフトです。Amazon Researchの「Keyword Search Is All You Need」やRich Suttonの The Bitter Lesson にも接続される考え方で、人間が手で組み込んだ知識よりも、汎用的な計算のスケール(強いLLM)のほうが長期的には勝つ、という構図です。
これを読んだとき、自分のマルチエージェント構成が抱えていた問題はまさに RAGが乗り越えようとしている問題と同じ構造 だと気付きました。マルチエージェントも、子エージェントごとに「いかに適切な情報を統合エージェントに渡すか」の精査に労力をかけていたからです。
| RAG検索の文脈 | マルチエージェント設計への転用 |
|---|---|
| 検索の高recall化 | 情報収集Agentの責務 |
| LLMによる濾別・補完 | 推論Agentの責務 |
| 検索チューニングが終わらない | 役割ごとのAgent増殖が終わらない |
| The Bitter Lesson | 強いLLMにまとめて推論させる |
つまり、6個のエージェントに「自分の領域内で結論を出す」と重い責務を課す代わりに、情報を漏らさず一箇所に集めて、強いLLMに一気に推論させればいい、というのが新しい設計方針です。
3. 情報収集 + 推論の2層モデル
新しい構成は、責務を2層に分けます。
- 情報収集層:関連しうるデータを 漏らさず 取得する役割
- 推論層:集まったデータを読み込み、結論を出す 役割。ここに高性能LLMを充てる
ここで重要なのは、情報収集側はもう"結論を出さなくていい" ということです。彼らの仕事は「自分の領域に関係するデータを正確に拾って渡す」だけ。判断は推論層に集約します。これにより、独立した統合エージェントは要らなくなり、統合の責務は推論層が兼ねる 形になります。
なお「6個から2個」というのは概念モデルとしての話で、実装上は情報収集層に複数のAgentが並んでいて構いません。重要なのは 意思決定の責務が推論層に集中していること、情報収集層のAgentが独立して結論を出さないこと、統合エージェントが要らなくなること の3点です。
4. Mastraでの実装
実装はAnthropic "Building Effective Agents"の Routingパターン に近い形になります。
推論層は1個のCoordinator Agentで実装し、toolsプロパティに情報収集側を渡します。Coordinatorのinstructionsには「質問の意図に応じて、関連するToolを1つだけ呼び出して情報を取り、自分でユーザー向けの回答を作る」と書きます。
import { Agent } from "@mastra/core/agent";
import { specialistTools } from "../tools/specialist-tools";
export const coordinatorAgent = new Agent({
name: "coordinator",
instructions: `
あなたは質問を受け取り、以下の手順で回答する Coordinator です。
1. 質問の意図を特定する
2. 関連する情報収集 Tool を 1つだけ 呼び出す
3. 取得した情報をもとに、自分で最終回答を作成する
`,
model: highCapableModel,
tools: specialistTools,
});
情報収集側は Sub-agent as Toolパターン を使います。各エージェントをcreateTool()でラップして、外からは関数として見える形にします。descriptionに「情報を漏らさず取得する」と書くのは、Recall-Firstの哲学をToolの責務にそのまま落とし込んだものです。Toolは結論を出さず情報を返すだけ に徹します。
import { createTool } from "@mastra/core/tools";
import { z } from "zod";
const wrapAsTool = (id: string, description: string, agent: Agent) =>
createTool({
id,
description,
inputSchema: z.object({ question: z.string() }),
outputSchema: z.object({ retrieved_context: z.string() }),
execute: async ({ context }) => {
const result = await agent.generate(context.question);
return { retrieved_context: result.text };
},
});
export const specialistTools = {
domain_a: wrapAsTool("domain_a", "領域Aの情報を漏らさず取得する", AgentA),
domain_b: wrapAsTool("domain_b", "領域Bの情報を漏らさず取得する", AgentB),
// 必要な領域だけ追加していく
};
そしてWorkflow側で面白いのが、.parallel()の用途が完全に変わる ことです。旧構成では.parallel()は「6個のAgentに同時に応答を作らせる」ために使っていましたが、新構成では応答生成はCoordinator 1個で完結します。代わりに .parallel() は 応答品質を評価する複数軸のEvalを並列に走らせる のに使い、.branch() は HITL(Human-in-the-loop)エスカレーション に使います。
export const recallFirstWorkflow = createWorkflow({
id: "recall-first",
inputSchema: z.object({ question: z.string() }),
outputSchema: z.object({ answer: z.string(), composite_score: z.number() }),
})
.then(stepCoordinatorRoute) // ← 推論層: 1回のLLM呼び出しで完結
.parallel([ // ← .parallel() は Eval 複数軸の並列に
stepFaithfulness,
stepAnswerRelevancy,
stepHallucination,
stepActionability,
])
.then(stepMergeEvals)
.branch([ // ← .branch() は HITL エスカレーションに
[({ inputData }) => inputData.composite_score < 0.65, stepHitlSuspend],
[({ inputData }) => inputData.composite_score >= 0.65, stepAutoApprove],
])
.then(stepFinalize)
.commit();
.parallel()を 応答ではなく評価に、.branch()を HITLエスカレーションに 使う。これがMastraの制御フローAPIの 本来の用途に戻った 形だと感じています。
5. 何が楽になったか、そして限界
2層モデルに作り直して、運用面では3つの大きな変化がありました。
楽になった3点
① LLM呼び出し回数の激減
| 旧構成 | 新構成 | |
|---|---|---|
| 並列実行のAgent | 6回 | — |
| Coordinator / 統合 | 1回 | 1回 |
| 選ばれたTool | — | 1回 |
| 合計 | 7回 | 2回(約 1/3.5) |
質問が特定領域に閉じている場合、関係ない領域のToolは呼ばれません。「どれが当たりかを事前に決めず、LLMにまとめて判断させる」というRecall-First的アプローチの直接効果です。
② ドメイン追加コストの低下
新しい領域を1つ追加するときの変更箇所を比較すると、こうなります。
| 変更箇所 | 旧構成 | 新構成 |
|---|---|---|
| Agentファイル作成 | ✅ | ✅ |
Workflowに createStep 追加 |
✅ | — |
.parallel([...]) の配列に追加 |
✅ | — |
| 統合役プロンプト改修 | ✅ | — |
| 統合役の出力スキーマ調整 | ✅ | — |
tools への登録 |
— | ✅ |
Workflowを一切触らなくていい のが本質で、Workflowが領域追加に対して閉じている(Open/Closed Principle)状態になります。
③ デバッグの直線化
並列分岐の段数が 1段 → 0段 になり、追うべきトレースが直線になりました。「最終回答がぼやけている」というフィードバックを受けたときも、Coordinator の Tool call ログを見れば呼ばれた Tool が1つだけ分かる ので、原因特定が早くなります。「6個の主張をどう融合するか」という根本的に難しい問題そのものが消滅したのが大きいです。
限界と注意点
ただしこのモデルは万能ではありません。次の3点は前提として押さえておく必要があります。
- 強いLLMが前提 — 推論層に判断責務を集約するため、推論力が弱いモデルでは品質が崩れます
- 領域分離が曖昧な質問 — Toolを1つだけ呼ぶ設計だと情報が片寄ります。複数Tool呼び出しや動的反復探索を許す設計で補う必要があります
- Coordinatorのinstructionsが品質を決める — 統合ロジックは消えたのではなく 推論層に折り畳まれた だけです。フォールバックやトーン指定が薄いと、場当たり的な判断になります
まとめ
役割ごとに専門Agentを並列に走らせて統合する構成は、運用に入ると線形にコストが増えていく 構造でした。これに対して 情報収集 + 推論 の2層モデルは管理コードを薄く保て、ドメイン追加に閉じていて、デバッグも直線で済みます。po3rin氏のRecall-First Retrievalは検索設計の話でしたが、エージェント設計にもそのまま転用できる発想 でした。
これはAnthropicの "find the simplest solution possible" にも、Suttonの The Bitter Lesson にも整合する方向です。エージェント設計でも、役割ごとに増やす方向に手を伸ばす前に、情報収集と推論を分離する方向 を試してみる価値はあるはずです。
参考文献
- po3rin「検索設計から推論設計への重心移動 と Recall-First Retrieval」 — Speaker Deck
- Anthropic「Building Effective Agents」 — https://www.anthropic.com/engineering/building-effective-agents
- Rich Sutton「The Bitter Lesson」(2019) — http://www.incompleteideas.net/IncIdeas/BitterLesson.html
- Mastra Workflows Control Flow — https://mastra.ai/docs/workflows/control-flow