アーカイブへ戻る

Claude Code ソースコード解説シリーズ 第12章: Agent 協調

Claude Code の複数 Agent 協調、サブ Agent、Task、権限制御を分解して説明します。

《Claude Code ソースコード解析シリーズ》第12章|エージェント連携

ここまでの章で、Claude Code の主要な流れをそれぞれ分解してきました。

  • ReAct は「モデルがどのようにラウンドごとに思考し行動するか」を説明する。
  • Prompt は「モデルが各ラウンドでどのようなルールを参照するか」を説明する。
  • Context は「履歴、ファイル、ツール結果がどのように管理されるか」を説明する。
  • Tools は「モデルがどのように実際のエンジニアリング環境にアクセスするか」を説明する。
  • MCP と Skill は「外部機能やタスク経験をどのように取り込むか」を説明する。

しかし、まだ解決されていない問題があります。

タスクが大きくなったとき、なぜ一つのメインエージェントでは足りないのか?

たとえば、ユーザーが次のように言ったとします。

この認証ロジックをリファクタリングして、ついでにテストを追加し、API 互換性を確認して、
セキュリティリスクがないかもチェックしてほしい。

メインエージェントが一つの場合、同時に多くのことをこなさなければなりません。

  • 認証コードを読む。
  • API 呼び出し元を探す。
  • テスト体系を理解する。
  • 実装を修正する。
  • テストを実行する。
  • セキュリティ境界を判断する。
  • リスクを集約する。

これらの作業を同じコンテキストに詰め込むと、現実的な問題が三つ生じます。

第一に、メインのコンテキストが汚染されることです。大量の検索結果、一時的な推測、失敗パス、ログ出力がコンテキストウィンドウを圧迫します。本当に重要な設計判断が埋もれてしまいます。

第二に、タスクは本来的に並列化可能だということです。API 呼び出し元の調査、テスト体系の確認、セキュリティ境界の監査——これらは本来、別々の実行ユニットが同時に行えます。

第三に、サブエージェントが単独で判断できない意思決定があることです。互換性を破壊してよいか、古いエントリポイントを削除してよいか、危険なコマンドを受け入れてよいか——これらはメインエージェントやユーザーに戻す必要があります。

Claude Code におけるエージェント連携は、単に「モデルインスタンスを複数立ち上げる」という単純な話ではありません。むしろ、複雑なエンジニアリングタスクを一連のランタイムシステムに分解するものに近いのです。

メインエージェント ── 目標の理解と結果の統合を担う
  └→ サブエージェント ── 局所的な調査、実装、検証を担う
  └→ タスクシステム ── バックグラウンド実行体を追跡する
  └→ SendMessage ── エージェント間の通信を担う
  └→ AskUserQuestion ── 高リスクな意思決定にユーザーを引き戻す
  └→ 権限システム ── サブエージェントがガバナンスを迂回しないように保証する

理解しやすくするために、本稿全体を通して一つの具体例を用います。

ユーザーが Claude Code にログイン失敗のバグ修正を依頼する。

メインエージェントはまず、問題がおそらく auth モジュールにあると特定する。
次に、サブエージェント A を呼び出して呼び出しチェーンを調査させ、
サブエージェント B を呼び出してテストと再現手順を確認させ、
自身は中心的な判断を維持する。
もしサブエージェントがデータベーススキーマの変更や互換性ロジックの削除を
必要とした場合、権限とユーザー確認は必ず親にエスカレーションされる。

这篇が答えるべき核心的な問いは次のとおりです。

Claude Code のソースコードにおいて、Agent 協調はどのようなオブジェクトで構成され、それぞれがどのような問題を解決しているのか?

1. まず問題チェーンをまっすぐにする

マルチエージェントの仕組みは、subagent、fork、coordinator、team、swarm、message、task といった用語の羅列で語られがちだ。

Claude Code のソースコードの進化に照らし合わせると、これはきわめて自然な問題チェーンとして現れる。

単一 Agent で単純なタスクをこなせる
-> タスクが大きくなると、検索や試行錯誤でメインコンテキストが汚染される
-> サブ Agent を導入し、局所的な探索と実行を隔離する
-> サブタスクの種類が増え、異なる役割やツール境界が必要になる
-> subagent_type / built-in subtype / カスタム Agent を導入
-> サブタスクによっては親コンテキストを継承した方がよく、ゼロから始めるのは不適切
-> fork を導入し、サブ Agent が親のコンテキストプレフィックスを共有してプロンプトキャッシュを活用できるようにする
-> サブ Agent はバックグラウンド実行される可能性があり、メインスレッドが制御を失ってはならない
-> LocalAgentTask / RemoteAgentTask と TaskOutput / TaskStop を導入
-> 複数の Agent が相互に通知・返信する必要がある
-> SendMessageTool と mailbox / bridge ルーティングを導入
-> 大規模タスクにはアドホックな仕事の割り振りではなく、組織構造が必要になる
-> Coordinator / Team / Teammate を導入
-> ユーザーの好みや高リスクな選択に直面する
-> AskUserQuestion を導入し、人間を意思決定ループに引き戻す

このチェーンが示していることはひとつだ。

Agent コラボレーションが解決しようとしているのは「脳を増やすこと」ではない。「複雑なタスクをいかに構造化し、隔離し、並列実行し、通信させ、停止させ、統治するか」である。

2. エージェント定義:「ロール」があって、次に「実行体」がある

Claude Code において、エージェントは単なるプロンプトではありません。

ソースコードの観点から見ると、エージェントには少なくとも二つの層があります。

静的定義:このエージェントは誰か、何が得意か、どのツールを使えるか、デフォルトでどのモデルを使うか
実行インスタンス:このエージェントがあるタスクによって起動された後の、現在の状態、出力、コンテキスト、ライフサイクル

静的定義とは宣言的な設定のことです。スキルと同様に、エージェントも Markdown frontmatter でメタデータを表現できます。

---
name: security-reviewer
description: 認証、権限、データ露出、シークレット処理のリスクをレビューする
tools: Read Grep Bash
model: opus
---

あなたはセキュリティ重視のレビュアーです。
スタイルの問題より、悪用可能なバグを優先してください。

この種の定義が答えるのは、次のような問いです。

  • このエージェントはどのようなときに選択されるべきか
  • その責務の境界は何か
  • ファイルを変更できるか
  • コマンドを実行できるか
  • どのモデルを使うべきか
  • トークン予算と出力方式は何か

エージェント定義の本質は「モデルに名前を付ける」ことではありません。実行ロールを、発見可能で、カスタマイズ可能で、制約可能なランタイムオブジェクトに変換することです。

新しいソースコードにおけるエージェント定義のフィールドは、この例よりもさらに充実しています。descriptiontoolspromptmodel に加えて、以下のフィールドも宣言できます。

disallowedTools
effort
permissionMode
mcpServers
hooks
maxTurns
skills
initialPrompt
memory
background
isolation

これはつまり、エージェント定義が単なる「ロールプレイのプロンプト」ではないということを示しています。ツールセット、権限モード、モデル、推論の強度、MCP サーバー、スキルの事前読み込み、バックグラウンド実行、そして分離方式にまで同時に影響を与えるランタイム設定なのです。

活跃 Agent は、単にディレクトリから読み出して終わり、というわけではない。ソースコードは built-in、plugin、user settings、project settings、flag settings、policy settings などの定義を出所に応じてマージし、同名の Agent は後から書き込まれた出所の定義が前に上書きされる。これにより、ポリシー層、プロジェクト層、ユーザー層のすべてが Agent の能力ガバナンスに関与できる。

この層がなければ、メイン Agent が仕事を割り振ろうとしても、自然言語を一行書くだけになる:

你去看一下安全问题,注意别乱改。

これではあまりに脆い。モデルが「安全问题」を誤解するかもしれないし、ついでにファイルを変更してしまうかもしれない。

Agent definition があることで、Claude Code はロールの制約をランタイムに落とし込める:

安全審査 Agent
-> 読み取り専用ツールプール
-> より強力なモデル
-> より焦点を絞った system prompt
-> 専用の出力フォーマット

これこそが built-in subtype とカスタム Agent の意義だ:

「役割分担」を単なるプロンプトの一言から、ツールプール、モデル、権限、コンテキスト戦略が共同で制約する対象へと変えること。

3. AgentTool:マルチエージェントのツールエントリポイント

Claude Code におけるマルチエージェントは、モデルが「内部で勝手に同僚を立ち上げる」仕組みではなく、ツールシステムを通じて明示的に発動する。

最も重要なエントリポイントが AgentTool だ。

その入力は、概念的には次のように抽象化できる。

const inputSchema = z.object({
  description: z.string(),
  prompt: z.string(),
  subagent_type: z.string().optional(),
  model: z.enum(["sonnet", "opus", "haiku"]).optional(),
  run_in_background: z.boolean().optional(),
})

これらのフィールドが本質をよく表している。

フィールド意味
descriptionサブタスクに短いタイトルを付け、UI やタスクシステムでの表示に使う
prompt実際にサブ Agent へ渡す作業指示
subagent_type特定の専用 Agent を選択する
model必要に応じてデフォルトモデルを上書きする
run_in_backgroundバックグラウンド実行体として走らせるかどうか

AgentTool は通常のツールではなく、制御されたディスパッチャーに近い。

メイン Agent
-> AgentTool を呼び出す
-> サブ Agent の種別を選択
-> サブ Agent のプロンプトを組み立て
-> ツールプールと権限を制限
-> ローカルまたはリモートの Agent を起動
-> 実行インスタンスをタスクシステムに登録
-> 結果またはタスク ID を返す

ここで最も重要なのは「制御されている」という点だ。

単にもう一度モデルを呼び出すだけでは、サブ Agent は制御不能なコピーになってしまう。Claude Code は多くのエンジニアリング上の細部に対処している。

  • サブ Agent の system prompt はメイン Agent と完全に混ぜてはいけない。
  • サブ Agent のツールプールは、デフォルトで無制限に開放してはいけない。
  • サブ Agent の出力はメイン Agent に戻せなければならない。
  • バックグラウンド Agent は参照・停止・通知が可能でなければならない。
  • サブ Agent が高リスクツールを発動する際、権限をバイパスできてはいけない。

AgentTool を読むときは、「高機能なプロンプトテンプレート」と捉えてはいけない。これは Tools、Prompt、Context、Task、権限システムの交点にあるものだ。

ソースコード上で AgentTool.call() が最初に分岐するのは、実は「どのサブエージェントを選ぶか」ではなく、team teammate spawn のいずれか、あるいは通常のサブエージェントなのかを判定する箇所だ。team_namename が同時に渡された場合、spawnTeammate() のパスに入る。そして teammate はさらに無限に teammate を spawn することはできず、in-process の teammate が background agent を spawn することもできない。このことは、team が単なる「名前付きのサブエージェント」ではなく、roster、mailbox、team context、そしてライフサイクル制約を備えた協調トポロジーであることを示している。

4. サブ Agent の呼び出しで何が起きるか

先ほどのログインのバグの例で追ってみよう。

メイン Agent はコードをいくつか読んだうえで、問題が auth/session 近辺にあると判断した。しかしリポジトリ全体の検索結果をすべてメインのコンテキストに詰め込みたくはない。そこでサブ Agent を送り出す。

description: "Trace auth callers"
prompt: "Find all call sites that create or validate sessions. Summarize the call chain and highlight suspicious branches. Do not edit files."
subagent_type: "explore"
run_in_background: false

実行時の流れはおおむね次のようになる。

08.1Agent協調 図 1

ここで「圧縮された要約」が極めて重要だ。

サブ Agent は大量のファイルを読み、大量のキーワードを検索し、数多くの失敗パスをたどる。しかしメイン Agent はその中間ノイズをすべて引き継ぐ必要はない。メイン Agent が必要とするのは次の情報だけだ。

どこを調べたか
どのような重要な事実が見つかったか
どのパスが除外できるか
次にどこを見るべきか

これこそが、サブ Agent がメインのコンテキストにもたらす最大の価値である。

ノイズの多い探索を、ノイズの少ない結論に変換する。