マルチエージェント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層で行う。

  1. ユニットテスト: 各エージェントを単独でテスト。モックされたLLM応答を使用
  2. インテグレーションテスト: エージェント間の連携をテスト。実際のLLM呼び出しを含む
  3. エンドツーエンドテスト: ユーザー入力から最終出力までの全フローをテスト
// ユニットテスト例(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特有の非決定性を考慮した設計を心がけてほしい。


本記事はEzark Consultingの技術ブログTechBoostに掲載されています。Web制作・AI開発に関するご相談はお問い合わせページからどうぞ。

関連記事