マルチエージェントAI開発入門 -- 2026年に押さえるべき設計パターンと実装手法
2026年、AI開発の現場では「マルチエージェントシステム」が急速に普及している。単一のLLMに全てを任せるアプローチから、複数の専門エージェントが協調して問題を解決するアーキテクチャへの移行が進んでいる。
本記事では、マルチエージェントシステムの基本概念から実装パターン、TypeScript/Pythonでの具体的なコード例、そして本番運用時の設計指針までを解説する。
マルチエージェントシステムとは何か
マルチエージェントシステム(MAS)とは、それぞれが独立した判断能力を持つ複数のAIエージェントが、共通の目標に向かって協調動作するアーキテクチャである。
従来の「1つのプロンプトに全てを詰め込む」アプローチと比較して、以下の優位性がある。
- 専門性の分離: 各エージェントが特定のドメインに集中できる
- スケーラビリティ: エージェントの追加・削除が容易
- 信頼性: 1つのエージェントの障害がシステム全体を停止させない
- テスタビリティ: 各エージェントを個別にテスト可能
単一エージェントとの違い
| 観点 | 単一エージェント | マルチエージェント |
|---|---|---|
| コンテキスト管理 | 1つの長大なプロンプト | エージェントごとに最適化 |
| エラー耐性 | 全体が失敗 | 部分的な再試行が可能 |
| トークンコスト | コンテキスト肥大化で高騰 | 必要な情報のみ渡すため効率的 |
| 開発速度 | 変更が全体に影響 | 独立して開発・デプロイ可能 |
| デバッグ | ブラックボックス化しやすい | エージェント間通信をトレース可能 |
代表的な設計パターン
マルチエージェントシステムには、いくつかの確立された設計パターンがある。プロジェクトの要件に応じて選択する。
パターン1: オーケストレーター(Orchestrator)
中央の司令塔エージェントがタスクを分解し、専門エージェントに委任する。最も一般的なパターン。
[ユーザー入力]
|
[Orchestrator]
/ | \
[検索] [分析] [生成]
\ | /
[Orchestrator]
|
[最終出力]
適用場面: タスクの分解が明確で、各サブタスクが独立して実行できる場合。
パターン2: パイプライン(Pipeline)
エージェントが直列に接続され、前段の出力が後段の入力になる。データ処理や段階的な品質改善に向く。
[入力] -> [収集] -> [分析] -> [要約] -> [校正] -> [出力]
適用場面: 処理に明確な順序がある場合。コンテンツ生成パイプラインや、ETL処理など。
パターン3: ディベート(Debate / Adversarial)
複数のエージェントが異なる立場から議論し、より質の高い結論を導き出す。
[テーマ]
|
[賛成Agent] <-> [反対Agent]
| |
+-------+-------+
|
[Judge Agent]
|
[結論]
適用場面: 意思決定の質を高めたい場合。リスク評価やコードレビューなど。
パターン4: 階層型チーム(Hierarchical Team)
マネージャーエージェントが複数のチームを管理し、各チームがさらに専門エージェントを持つ。大規模プロジェクト向け。
適用場面: 複雑なプロジェクトで、異なる専門領域のチームが並行作業する場合。
TypeScriptによる実装例
以下に、オーケストレーターパターンの基本実装を示す。
エージェントの基本インターフェース
// types.ts
interface AgentMessage {
role: 'user' | 'assistant' | 'system';
content: string;
metadata?: Record<string, unknown>;
}
interface AgentResult {
success: boolean;
output: string;
tokenUsage: number;
agentId: string;
}
interface Agent {
id: string;
name: string;
systemPrompt: string;
execute(messages: AgentMessage[]): Promise<AgentResult>;
}
専門エージェントの実装
// agents/research-agent.ts
class ResearchAgent implements Agent {
id = 'research';
name = 'Research Agent';
systemPrompt = `あなたはリサーチ専門のAIアシスタントです。
与えられたトピックについて、正確で最新の情報を収集してください。
出力は構造化されたJSON形式で返してください。`;
async execute(messages: AgentMessage[]): Promise<AgentResult> {
const response = await callLLM({
model: 'claude-sonnet-4-5-20250929',
system: this.systemPrompt,
messages,
});
return {
success: true,
output: response.content,
tokenUsage: response.usage.totalTokens,
agentId: this.id,
};
}
}
オーケストレーターの実装
// orchestrator.ts
class Orchestrator {
private agents: Map<string, Agent> = new Map();
private executionLog: AgentResult[] = [];
registerAgent(agent: Agent): void {
this.agents.set(agent.id, agent);
}
async execute(userInput: string): Promise<string> {
// Step 1: タスク分解
const plan = await this.decompose(userInput);
// Step 2: 各エージェントに委任
const results = await Promise.all(
plan.tasks.map(async (task) => {
const agent = this.agents.get(task.agentId);
if (!agent) throw new Error(`Agent not found: ${task.agentId}`);
const result = await agent.execute([
{ role: 'user', content: task.instruction },
]);
this.executionLog.push(result);
return result;
})
);
// Step 3: 結果を統合
return this.synthesize(userInput, results);
}
private async decompose(input: string): Promise<ExecutionPlan> {
const response = await callLLM({
model: 'claude-sonnet-4-5-20250929',
system: `ユーザーの要求をサブタスクに分解してください。
利用可能なエージェント: ${[...this.agents.keys()].join(', ')}
JSON形式で返してください。`,
messages: [{ role: 'user', content: input }],
});
return JSON.parse(response.content);
}
private async synthesize(
originalInput: string,
results: AgentResult[]
): Promise<string> {
const context = results
.map((r) => `[${r.agentId}]: ${r.output}`)
.join('\n\n');
const response = await callLLM({
model: 'claude-sonnet-4-5-20250929',
system: '各エージェントの出力を統合し、一貫性のある最終回答を生成してください。',
messages: [
{ role: 'user', content: `元の質問: ${originalInput}\n\n各エージェントの出力:\n${context}` },
],
});
return response.content;
}
}
Pythonによる実装例
Pythonではasyncioを活用した並行処理が自然に書ける。
パイプラインパターンの実装
# pipeline.py
from dataclasses import dataclass
from typing import Callable, Awaitable
@dataclass
class PipelineStage:
name: str
system_prompt: str
process: Callable[[str], Awaitable[str]]
class AgentPipeline:
def __init__(self):
self.stages: list[PipelineStage] = []
self.trace: list[dict] = []
def add_stage(self, stage: PipelineStage) -> "AgentPipeline":
self.stages.append(stage)
return self
async def run(self, initial_input: str) -> str:
current = initial_input
for stage in self.stages:
result = await stage.process(current)
self.trace.append({
"stage": stage.name,
"input_length": len(current),
"output_length": len(result),
})
current = result
return current
パイプラインの使用例: コンテンツ生成
# content_pipeline.py
import asyncio
from pipeline import AgentPipeline, PipelineStage
async def create_content_pipeline() -> AgentPipeline:
pipeline = AgentPipeline()
pipeline.add_stage(PipelineStage(
name="research",
system_prompt="与えられたテーマについて調査し、要点を箇条書きで返す",
process=research_step,
))
pipeline.add_stage(PipelineStage(
name="draft",
system_prompt="調査結果を元に記事のドラフトを作成する",
process=draft_step,
))
pipeline.add_stage(PipelineStage(
name="review",
system_prompt="ドラフトの事実確認と品質チェックを行う",
process=review_step,
))
pipeline.add_stage(PipelineStage(
name="polish",
system_prompt="最終的な校正と文体統一を行う",
process=polish_step,
))
return pipeline
async def main():
pipeline = await create_content_pipeline()
result = await pipeline.run("Next.jsのServer Actionsのベストプラクティス")
print(result)
if __name__ == "__main__":
asyncio.run(main())
本番運用で考慮すべき設計指針
マルチエージェントシステムを本番環境で運用する際に、特に注意すべきポイントを整理する。
1. トークンコストの制御
マルチエージェントシステムでは、エージェント間の通信が増えるほどトークン消費が増大する。以下の対策が有効。
- コンテキストウィンドウの最適化: 各エージェントに渡す情報を必要最小限に絞る
- 要約エージェントの挿入: 長い中間出力は要約してから次のエージェントに渡す
- モデルの使い分け: 簡単なタスクには軽量モデル、判断が必要なタスクには高性能モデルを割り当てる
// モデル選択の例
function selectModel(taskComplexity: 'low' | 'medium' | 'high'): string {
switch (taskComplexity) {
case 'low':
return 'claude-haiku-4-5-20251001';
case 'medium':
return 'claude-sonnet-4-5-20250929';
case 'high':
return 'claude-opus-4-6';
}
}
2. エラーハンドリングとリトライ
エージェントの応答は非決定的であるため、堅牢なエラーハンドリングが必要。
async function executeWithRetry(
agent: Agent,
messages: AgentMessage[],
maxRetries: number = 3
): Promise<AgentResult> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await agent.execute(messages);
if (!result.success) {
throw new Error(`Agent ${agent.id} returned failure`);
}
return result;
} catch (error) {
if (attempt === maxRetries) throw error;
// 指数バックオフ
await new Promise((r) =>
setTimeout(r, Math.pow(2, attempt) * 1000)
);
}
}
throw new Error('Unreachable');
}
3. 可観測性(Observability)
本番環境では、各エージェントの振る舞いを追跡できることが不可欠。
- 構造化ログ: エージェントID、入出力トークン数、実行時間を記録する
- トレーシング: リクエストIDで一連のエージェント呼び出しを紐付ける
- メトリクス: エージェントごとの成功率、平均応答時間、トークンコストを監視する
interface AgentTrace {
requestId: string;
agentId: string;
startTime: number;
endTime: number;
inputTokens: number;
outputTokens: number;
success: boolean;
error?: string;
}
4. テスト戦略
マルチエージェントシステムのテストは、以下の3層で行う。
- ユニットテスト: 各エージェントを単独でテスト。モックされたLLM応答を使用
- インテグレーションテスト: エージェント間の連携をテスト。実際のLLM呼び出しを含む
- エンドツーエンドテスト: ユーザー入力から最終出力までの全フローをテスト
// ユニットテスト例(Vitest)
import { describe, it, expect, vi } from 'vitest';
describe('ResearchAgent', () => {
it('構造化されたJSON形式で結果を返す', async () => {
const mockLLM = vi.fn().mockResolvedValue({
content: JSON.stringify({
findings: ['finding1', 'finding2'],
sources: ['source1'],
}),
usage: { totalTokens: 150 },
});
const agent = new ResearchAgent(mockLLM);
const result = await agent.execute([
{ role: 'user', content: 'Next.jsの最新機能を調査' },
]);
expect(result.success).toBe(true);
expect(JSON.parse(result.output)).toHaveProperty('findings');
});
});
まとめ
マルチエージェントシステムは、2026年のAI開発において標準的なアーキテクチャになりつつある。本記事で解説した設計パターンと実装手法を押さえておけば、プロダクション品質のマルチエージェントシステムを構築するための基盤が得られる。
特に重要なポイントを再掲する。
- 適切なパターン選択: オーケストレーター、パイプライン、ディベート、階層型から要件に合ったものを選ぶ
- コスト制御: モデルの使い分けとコンテキスト最適化でトークンコストを抑える
- 可観測性: ログ、トレーシング、メトリクスで運用中の問題を早期に検知する
- テスト: ユニット、インテグレーション、E2Eの3層テストで品質を担保する
マルチエージェントシステムの設計は、マイクロサービスアーキテクチャの設計と多くの共通点がある。ソフトウェアエンジニアリングの知見を活かしつつ、LLM特有の非決定性を考慮した設計を心がけてほしい。