CodeCraft Lab

マルチエージェントパターン

並列・直列・階層という複数エージェントの協調パターンと、エージェント間のデータ受け渡し設計を、調査→実装→レビューの3エージェント構成で実践的に学びます

応用17分で読了
マルチエージェントパイプライン設計Agent SDK実践

このレッスンで学ぶこと

  • 複数エージェントの協調パターン(並列・直列・階層)を区別し、状況に応じて選べるようになる
  • エージェント間でデータをどう受け渡すか、設計レベルで判断できるようになる
  • 「調査 → 実装 → レビュー」という3エージェント構成のパイプラインを、CLI・Agent SDKの両方の視点で構築する

前提条件

  • 応用コース Lesson 7(サブエージェントの設計と活用)を修了し、タスク分解・結果統合の基本パターンを理解していること
  • 応用コース Lesson 9(Agent SDKでカスタムエージェントを構築する)で、セッション管理・カスタムツールの実装方法を理解していること
このレッスンの立ち位置

Lesson 7では「1つのメインエージェントが複数のサブエージェントに仕事を振る」という構図を扱いました。このレッスンでは視点を一段上げ、複数のエージェントが役割を持って連携する仕組み全体をパターンとして整理し、実際にパイプラインとして組み立てます。

複数エージェントの協調パターン

複数のエージェントを組み合わせる方法は、エージェント同士の依存関係によって大きく3つに分類できます。

graph TD
    subgraph "並列パターン"
        P1["メイン"] --> PA["エージェントA"]
        P1 --> PB["エージェントB"]
        PA --> PR["結果統合"]
        PB --> PR
    end
graph LR
    subgraph "直列パターン"
        S1["エージェントA"] --> S2["エージェントB"] --> S3["エージェントC"]
    end
graph TD
    subgraph "階層パターン"
        H1["統括エージェント"]
        H1 --> H2["中間エージェント1"]
        H1 --> H3["中間エージェント2"]
        H2 --> H4["末端エージェントA"]
        H2 --> H5["末端エージェントB"]
    end
パターン特徴向いている場面
並列複数のエージェントが同時に独立して動く互いに依存しない複数領域の調査・レビュー
直列前段の出力が後段の入力になる調査→実装→レビューのように、工程に順序性がある作業
階層統括エージェントが中間エージェントに仕事を振り、中間エージェントがさらに末端に振る大規模な調査やタスクを、複数レベルの委譲で分割したい場合

Lesson 7で扱った並列実行・逐次実行は、この並列パターン・直列パターンの実装そのものです。このレッスンでは、それらを組み合わせた直列パイプラインを中心に、実際に手を動かして構築します。

階層パターンは慎重に使う

サブエージェントは自身がさらにサブエージェントを生成できますが、階層が深くなるほど「今どのレベルで何が起きているか」の把握が難しくなり、エラー時の原因切り分けも複雑になります。深さには上限があり、無制限に階層化できるわけでもありません。まずは並列・直列の組み合わせで設計し、本当に必要な場合にのみ階層化を検討しましょう。

エージェント間のデータ受け渡し設計

複数エージェントを連携させるとき、最も設計判断が問われるのが「前段のエージェントの出力を、どう後段に渡すか」です。大きく2つのアプローチがあります。

アプローチ1: メインエージェントが仲介する(CLI向き)

CLIでサブエージェントを使う場合、各サブエージェントの出力はいったんメインエージェント(あなたと対話しているClaude)に返ります。メインエージェントが内容を理解し、次のサブエージェントへの指示として再構成してから渡します。

graph LR
    A["調査サブエージェント"] -->|"調査結果(生データ)"| M["メインエージェント"]
    M -->|"要点を整形した指示"| B["実装サブエージェント"]
    B -->|"diff"| M
    M -->|"diff + レビュー観点"| C["レビューサブエージェント"]
 
    style M fill:#fff3e0,stroke:#e65100

このアプローチの利点は、メインエージェントが「次に何を渡すべきか」を都度判断できる柔軟性です。一方で、すべてのデータがメインのコンテキストを経由するため、各段階の出力が大きいとコンテキスト消費がかさみます。

アプローチ2: 構造化データで直接受け渡す(Agent SDK向き)

Agent SDKでパイプラインをプログラムとして組む場合は、各エージェントのquery()呼び出しの結果(テキストやstructuredContent)を変数に保持し、次のquery()のプロンプトに直接埋め込めます。メインの「対話エージェント」が介在しないため、各段階の入出力を明示的にコードで制御できます。

graph LR
    A["query() #1<br/>調査"] -->|"result文字列"| Code["呼び出し側のコード"]
    Code -->|"プロンプトに埋め込み"| B["query() #2<br/>実装"]
    B -->|"result文字列"| Code2["呼び出し側のコード"]
    Code2 -->|"プロンプトに埋め込み"| C["query() #3<br/>レビュー"]
 
    style Code fill:#e8f4fd,stroke:#1a73e8
    style Code2 fill:#e8f4fd,stroke:#1a73e8

この方式では、各段階の出力をログに残す、必要な部分だけ抽出する、フォーマットを検証してから次に渡す、といった処理をコードとして明示的に書けます。CI/CDのような自動化パイプラインでは、こちらのアプローチがより堅牢です。

実践: 調査 → 実装 → レビューの3エージェント構成

ここからは、「バグ修正」を題材に、調査・実装・レビューという役割の異なる3つのエージェントを連携させるパイプラインを、CLIとAgent SDKの両方で構築します。

CLIでの構築: サブエージェント定義 + 明示的な指示

まず、3つの役割に対応するサブエージェントを定義します。

---
name: bug-investigator
description: バグの原因を調査する。再現条件・関連コード・根本原因を特定する。バグ報告を受けたときに使用する。
tools: Read, Grep, Glob, Bash
---
 
報告されたバグについて、関連するコードを調査し、以下を報告してください。
 
1. 再現条件(どんな入力・操作で発生するか)
2. 根本原因と考えられる箇所(ファイルパス・行番号付き)
3. 修正方針の提案(複数ある場合は選択肢として)
 
実装はしないでください。調査と原因特定に専念してください。
---
name: bug-fixer
description: 調査済みのバグを修正実装する。bug-investigatorの調査結果を受け取って実装する。
tools: Read, Edit, Write, Bash
---
 
渡された調査結果(根本原因・修正方針)に基づいて、修正を実装してください。
実装後、関連するテストを実行して結果を報告してください。
テストが存在しない場合は、再発防止のためのテストも追加してください。
---
name: fix-reviewer
description: バグ修正の実装をレビューする。実装の妥当性とエッジケースの考慮漏れを確認する。
tools: Read, Bash
---
 
実装されたバグ修正の diff をレビューしてください。
 
- 報告されたバグが実際に解消されているか
- 修正によって新たな問題が生まれていないか(副作用・エッジケース)
- テストが修正内容を適切にカバーしているか
 
指摘は「ブロッカー」「推奨」「問題なし」の3区分で報告してください。

これら3つを.claude/agents/に配置したうえで、メインエージェントにパイプラインとして実行するよう依頼します。

次の手順でログイン画面のバグを修正して。
 
1. bug-investigatorサブエージェントで「ログイン後にリダイレクトが2回発生する」
   バグの原因を調査する
2. 調査結果をもとに、bug-fixerサブエージェントで修正を実装する
3. 実装結果をfix-reviewerサブエージェントでレビューする
4. レビューでブロッカーがあれば、bug-fixerに差し戻して再修正する

この指示の重要な点は、各サブエージェントへの「何を渡すか」をメインエージェントの判断に委ねつつ、全体の手順と「ブロッカーがあれば差し戻す」という条件分岐を明示していることです。直列パイプラインでは、こうした全体設計をメインエージェントへの指示として明文化することが、安定した実行の鍵になります。

Agent SDKでの構築: query()を3回つなぐ

同じパイプラインをAgent SDKでプログラム化すると、各段階の入出力をコードで明示的に制御できます。

import { query } from "@anthropic-ai/claude-agent-sdk";
 
async function runResultText(prompt: string, allowedTools: string[]): Promise<string> {
  let result = "";
  for await (const message of query({ prompt, options: { allowedTools } })) {
    if (message.type === "result" && message.subtype === "success") {
      result = message.result;
    }
  }
  return result;
}
 
async function fixBugPipeline(bugReport: string) {
  // ステージ1: 調査(読み取り専用ツールのみ許可)
  const investigation = await runResultText(
    `次のバグについて、原因調査だけを行い、実装はしないでください。\n\nバグ報告: ${bugReport}`,
    ["Read", "Grep", "Glob", "Bash"]
  );
  console.log("=== 調査結果 ===\n", investigation);
 
  // ステージ2: 実装(調査結果を明示的にプロンプトへ埋め込む)
  const implementation = await runResultText(
    `以下の調査結果に基づいて修正を実装し、テストを実行してください。\n\n調査結果:\n${investigation}`,
    ["Read", "Edit", "Write", "Bash"]
  );
  console.log("=== 実装結果 ===\n", implementation);
 
  // ステージ3: レビュー(実装結果を渡す。読み取り専用)
  const review = await runResultText(
    `次の実装内容をレビューし、「ブロッカー」「推奨」「問題なし」の3区分で報告してください。\n\n実装結果:\n${implementation}`,
    ["Read", "Bash"]
  );
  console.log("=== レビュー結果 ===\n", review);
 
  return { investigation, implementation, review };
}
 
fixBugPipeline("ログイン後にリダイレクトが2回発生する");

CLIのサブエージェント版との違いに注目してください。Agent SDK版では、各ステージがそれぞれ独立したquery()呼び出しであり、allowedToolsを段階ごとに絞り込むことで「調査段階ではファイルを書き換えられない」「レビュー段階では読み取りしかできない」という制約をコードレベルで強制できます。これはCLIでのtools制限を、より明示的にプログラムへ落とし込んだ形です。

セッションのresumeで会話を引き継ぐ選択肢もある

上記の例では、各ステージの結果をプロンプトに文字列として埋め込み直しています。代わりに、Lesson 9で扱ったresumeを使って同一セッションを引き継ぐことも可能です。ただしその場合、後段のエージェントが前段の会話全体(ツール呼び出しの詳細含む)を引き継ぐため、コンテキスト消費は増えます。「結果の要点だけを渡したい」ならプロンプト埋め込み、「文脈をまるごと引き継ぎたい」ならresume、という使い分けが目安になります。

条件分岐を組み込む

CLIの例で触れた「ブロッカーがあれば差し戻す」という条件分岐も、Agent SDK版ではコードのif文として素直に表現できます。

async function fixBugPipelineWithRetry(bugReport: string, maxRetries = 2) {
  const investigation = await runResultText(
    `次のバグについて原因調査だけを行ってください。\n\nバグ報告: ${bugReport}`,
    ["Read", "Grep", "Glob", "Bash"]
  );
 
  let implementation = await runResultText(
    `以下の調査結果に基づいて修正を実装してください。\n\n調査結果:\n${investigation}`,
    ["Read", "Edit", "Write", "Bash"]
  );
 
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const review = await runResultText(
      `次の実装をレビューし、ブロッカーがあれば具体的に指摘してください。先頭に「ブロッカーあり」か「問題なし」と明記すること。\n\n実装結果:\n${implementation}`,
      ["Read", "Bash"]
    );
 
    if (review.startsWith("問題なし")) {
      console.log("レビュー完了:", review);
      return { investigation, implementation, review };
    }
 
    // ブロッカーが残っている場合は、レビュー指摘を踏まえて再実装する
    console.log(`再修正が必要 (試行 ${attempt + 1}/${maxRetries}):`, review);
    implementation = await runResultText(
      `以下のレビュー指摘を踏まえて修正してください。\n\nレビュー指摘:\n${review}\n\n元の実装:\n${implementation}`,
      ["Read", "Edit", "Write", "Bash"]
    );
  }
 
  throw new Error("最大リトライ回数に達しましたが、レビューを通過できませんでした");
}

この形にしておくと、「何回まで自動でリトライするか」「リトライしても通らない場合にどう失敗させるか」といった運用上の判断を、コードとして明確に定義できます。CI/CDパイプラインに組み込む場合は、このような失敗時の挙動の明文化が特に重要になります。

まとめ

このレッスンでは、複数エージェントを協調させる3つのパターン(並列・直列・階層)を整理し、エージェント間のデータ受け渡しを「メインエージェントが仲介する方式」と「構造化データで直接受け渡す方式」の2つの観点で比較しました。そのうえで、調査・実装・レビューという3エージェント構成のパイプラインを、CLIのサブエージェント定義とAgent SDKのquery()連鎖の両方で実際に構築しました。

  • 並列・直列・階層は、エージェント間の依存関係に応じて選ぶ。階層パターンは複雑さが増すため必要な場合に限定する
  • CLIでは「メインエージェントへの明示的な手順指示」、Agent SDKでは「query()の結果を次のプロンプトに埋め込む」という形でパイプラインを組む
  • Agent SDKでは各ステージのallowedToolsを絞り込むことで、「調査段階では書き込み禁止」のような制約をコードレベルで強制できる
  • 「ブロッカーがあれば差し戻す」といった条件分岐は、CLIでは指示文として、Agent SDKではコードのif文として表現する

これで応用コースは折り返し地点です。Skills・サブエージェント・Agent SDK・マルチエージェントパターンという拡張の仕組みを一通り学んだので、次のLesson 11からはIDE連携やRemote Control APIなど、Claude Codeをより広い環境で活用する方法に進んでいきます。