Managed Agentsでマルチエージェントを編成する方法 — コーディネーターとスレッド設計
Managed Agentsのコーディネーターが複数エージェントへ作業を委譲する流れを、委譲先の一覧の宣言・スレッド分離・MCP接続の3点から解説します。
Managed Agentsのマルチエージェント編成は、1つのコーディネーターが複数のエージェントへ作業を分担させる仕組みです。各エージェントは独立したコンテキストで動き、並列に処理を進められます。連携の要は multiagent フィールドとスレッド分離の2つです。roster(委譲先の一覧)を宣言し、コーディネーターが実行時にスレッドを生成して作業を渡します。費用面で得かどうかは、オーケストレーターでコストが下がる2条件で扱います。
Managed Agentsのマルチエージェントとは何か
複数のエージェントが同じサンドボックスを共有しつつ、それぞれ独自のセッションスレッド(コンテキストが分離された独立の会話履歴)で動く仕組みです。何が共有され何が独立するかの内訳は、記事の後半で比べます。
コーディネーターの活動はプライマリスレッド(セッションレベルのイベントストリームと同一)に集約されます。委譲が発生すると、そのつどスレッドが実行時に生成される仕組みです。スレッドは永続します。コーディネーターは以前呼び出したエージェントへ追加の指示を送れて、相手はそれまでの会話をすべて覚えています。
向いているパターンは3つあります。
| パターン | 内容 |
|---|---|
| 並列化 | 内容独立した複数のサブタスクを同時に走らせ、結果をコーディネーターが統合する |
| 専門化 | 内容セキュリティ担当・ドキュメント担当のように、領域特化のシステムプロンプトとツールを持つエージェントへ振り分ける |
| エスカレーション | 内容一部の複雑なサブタスクだけ、より高性能なモデルのエージェントに相談する |
コーディネーターを設定する
multiagent に type: "coordinator" と委譲先の agents 配列を指定します。
curl -fsS https://api.anthropic.com/v1/agents \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{
"name": "Engineering Lead",
"model": "claude-opus-5-5",
"system": "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
"tools": [{"type": "agent_toolset_20260401"}],
"multiagent": {
"type": "coordinator",
"agents": [
{"type": "agent", "id": "'"$REVIEWER_AGENT_ID"'"},
{"type": "agent", "id": "'"$TEST_WRITER_AGENT_ID"'"}
]
}
}'CLIの ant apply を使う場合、コーディネーターとroster内の各エージェントを別々のMarkdownファイルとして定義し、まとめて適用できます。
ant apply engineering-lead.md reviewer.md test-writer.mdengineering-lead.md のfrontmatterで multiagent.agents に ./reviewer.md のようなファイルパスを書くと、applyがそのエージェントを先に作成します。パスは、バージョンを固定した {"type": "agent", "id": ..., "version": ...} 参照へ自動的に置き換えられます。作成されたリソースのIDは、実行したディレクトリの claude-lock.json に記録されます。このファイルをコミットしておくと、次回の実行は新規作成ではなく同じリソースの更新になります。--dry-run を付けると変更内容の詳細な計画だけを表示して終了します。ant apply にはCLI 1.30.0以降が必要です。
multiagent.agents の各エントリには4つの形式があります。
| 形式 | 意味 |
|---|---|
{"type": "agent", "id": ...} | 意味既存エージェントをIDで参照(version省略時は作成時点の最新版に固定) |
{"type": "agent", "id": ..., "version": ...} | 意味特定バージョンに固定して参照 |
{"type": "self"} | 意味コーディネーター自身のコピーを生成できるようにする |
{"type": "advisor", "model": ...} | 意味セッションのプライマリスレッドにアドバイザーを付ける(rosterに置けるのは最大1件) |
roster内の参照はコーディネーター作成・更新時点でスナップショットされます。参照先エージェントを後から更新しても、rosterのバージョン固定は自動追随しません。新しいバージョンへ委譲したい場合は、コーディネーター側を更新してrosterの参照を差し替えます。
委譲は1階層までです。roster内のエージェントがさらに自分の multiagent.agents を持っていると、コーディネーターの作成・更新自体が検証エラーで失敗します。rosterに列挙できるエージェントは最大20体ですが、同じエージェントの複数コピーを呼び出すことは可能です。
エージェント間はどう連携するか — スレッドとイベント
セッションの status は全スレッドの活動を集約したものです。1つでも running のスレッドがあれば、セッション全体のstatusも running になります。同時に持てるスレッド数は最大25で、これはアドバイザーの相談スレッドを除いた上限です。
セッションのbudgetは、セッション内の全スレッドで共有する単一の上限です。上限に達すると各スレッドは個別に一時停止し、それぞれのコストはそのスレッドが実際に使ったモデルの単価で計算されます。
プライマリスレッドで観測できる主なイベントは次のとおりです。
| イベント | 内容 |
|---|---|
session.thread_created | 内容スレッドが生成された(session_thread_id と agent_name を含む) |
session.thread_status_running | 内容スレッドが活動を開始した |
session.thread_status_idle | 内容エージェントが入力待ちになった(stop_reason を含む) |
session.thread_status_terminated | 内容スレッドがアーカイブされた、または終端エラーで終わった |
agent.thread_message_received | 内容あるエージェントがコーディネーターへ報告や質問を送った |
agent.thread_message_sent | 内容コーディネーターが別のエージェントへタスクや追加指示を送った |
イベント名の向きは、そのイベントが流れるスレッドから見た方向です。コーディネーターが委譲したタスクは、子スレッド側のストリームでは agent.thread_message_received として届きます。
プライマリスレッドには全スレッドの要約(開始・終了・ツール権限リクエストなどのブロッキングイベント)しか流れません。特定エージェントの詳細な思考やツール呼び出しを追いたい場合は、そのエージェント専用のスレッドイベントストリーム(/v1/sessions/{session_id}/threads/{thread_id}/stream)を個別に購読します。event_deltas[] パラメーターはセッションレベルのストリームと同じように使えますが、子スレッドのプレビューはセッションレベルのストリームには出てきません。サブエージェントの生成過程をリアルタイムで見たいなら、そのスレッド自体のストリームを開きます。
特定のスレッドだけを止めたいときは user.interrupt に session_thread_id を指定します。省略すると、プライマリを含む全ての非アーカイブスレッドが対象になります。権限確認(requires_action)で止まっている子スレッドを中断すると、保留中のツール呼び出しがエラーの結果で閉じられます。結果の文面は "Tool execution was interrupted before completion. Please retry." です。モデルは呼ばれず、stop_reason: end_turn の session.thread_status_idle が再び出ます。すでに idle のスレッドへの中断は何も起こしません。
MCPサーバーとvaultはエージェントごとに接続する
MCPサーバーはエージェント定義ごとにスコープされ、vault認証情報はセッション単位でスコープされます(セッション作成時に渡す vault_ids は全スレッドに適用)。この非対称から、実装上の注意点が2つ出てきます。
- MCPサーバーを認証するには、全エージェントが使うMCPサーバーの数だけvault認証情報をセッションに含める
- エージェントのアクセス範囲を絞るには、そのエージェント定義に必要なサーバーだけを宣言する
環境のネットワークを limited にしている場合は、もう一段の条件があります。エージェントのMCPサーバーは、allow_mcp_servers: true か、各サーバーのホストを並べた allowed_hosts がない限りブロックされます。どちらも無いとセッション作成が400エラーで失敗し、この検査はコーディネーターが委譲できる全エージェントに及びます。
vault側の資格情報も、mcp_server_url がエージェントの mcp_servers[].url と同じサーバーを指していないと認証に使われません。比較の前にスキームとホストは小文字化され、既定ポートと末尾スラッシュは取り除かれるので、その程度の違いは影響しません。パスやサブドメイン、既定以外のポートが違うと別のサーバー扱いになります。
セッション作成時のエージェント設定オーバーライドは、コーディネーターとその self コピーのMCPサーバーを置き換えます。IDで参照したroster内の他エージェントには適用されません。
委譲に向くタスクと向かないタスク
冒頭の3パターンに当てはまるかに加えて、共有されるものとの相性も判断材料になります。
| タスクの性質 | 委譲の向き | 理由 |
|---|---|---|
| 複数の子が同じファイルを読み書きする | 委譲の向き要設計 | 理由ファイルシステムは共有で、書いたファイルは別のエージェントも同じパスで読める |
| 費用の上限をエージェントごとに分けたい | 委譲の向き不可 | 理由budgetはセッション共通で、スレッドごとの上限はない |
| 子が権限確認つきのツールを使う | 委譲の向き要設計 | 理由requires_action がプライマリへ転送され、誰かが応答する必要がある |
| 単一の直線的な作業 | 委譲の向き△ | 理由スレッド分離のオーバーヘッドに見合わない |
| 深い委譲階層(委譲先がさらに委譲) | 委譲の向き不可 | 理由roster内エージェントにmultiagent.agentsがあると検証エラー |
inference_geo(推論地域)をピン留めしているエージェントが混在する場合も制約があります。コーディネーターとroster内の全メンバーは、ピンを全員同じ値に揃えるか、全員未設定にする必要があります。混在は400エラーで拒否されます。
サブエージェントが権限確認を求めたらどうなるか
委譲先のエージェントが応答待ちになるイベントは、発生元の session_thread_id を添えてプライマリスレッドにも転送されます。対象は、always_ask 設定のツールを実行しようとしたときと、カスタムツールの結果をクライアントから受け取る必要があるときです。
{
"type": "session.thread_status_idle",
"id": "sevt_01ABC...",
"session_thread_id": "sth_01DEF...",
"agent_name": "code-reviewer",
"stop_reason": {
"type": "requires_action",
"event_ids": ["sevt_01XYZ..."]
}
}権限確認が要るのは always_ask のときと、auto でサーバーが判定を出せなかったときです。
auto でサーバーが拒否した場合はプライマリスレッドへ何も転送されず、イベントとエラーのツール結果は子スレッド自身のストリームにだけ現れ、サブエージェントは動き続けます。auto では user.message が許可の判断材料になりえますが、サブエージェントのスレッド内のやり取りは利用者の意図には数えられません。コーディネーターからサブエージェントへのメッセージも同じ扱いです。
転送された権限確認に応答する流れ
- 1
プライマリスレッドで止まりを見つける
session.thread_status_idleのstop_reason.typeがrequires_actionなら、session_thread_idとagent_nameが発生元を指します。 - 2
応答を送る
user.tool_confirmation(tool_use_idを指定)またはuser.custom_tool_result(custom_tool_use_idを指定)を送り返します。 - 3
ルーティングはサーバーに任せる
サーバーが該当スレッドへ応答を振り分けるため、クライアントは発行元のスレッドを気にせず単一のハンドラーで書けます。
スレッドを片づけたいときは、archive エンドポイントを使います。アーカイブは、スレッドが idle のときだけ成功します。requires_action で止まっているスレッドも idle に数えられ、そのままアーカイブできます。実行中のスレッドだけは、先に user.interrupt で止めます。アーカイブすると25スレッドの上限の枠が空きます。
運用の単位はスレッドとAPIリソース
Agent SDKのサブエージェントやClaude Codeのサブエージェントと、Managed Agentsのマルチエージェントは発想が近く見えます。運用の単位は違い、Managed Agentsのコーディネーターは、APIリソースとして永続化したエージェント定義同士をrosterで結びます。イベントと権限はスレッドごとに分かれ、費用はセッション共通のbudgetの下でスレッドごとに実際のモデル単価で計算されます。1階層までという委譲の制約は、エージェント定義を独立したAPIリソースとして参照し合う設計と表裏の関係にあるようです。
観測の単位もスレッドです。プライマリスレッドが受け取るのは要約だけで、詳細を見るには子スレッド自身のイベントストリームを個別に開きます。
予算停止と権限確認が重なると何が起きるか
budgetの上限に達したスレッドと、権限確認で待つスレッドが混在することがあります。このときセッション全体のstatusは requires_action になります。保留中の確認にはまだ応答が要るため、上限に達していても確認への応答はbudgetに阻まれません。
全スレッドが上限で止まっているセッションに user.interrupt を送っても、受け付けられたうえで無視され、イベント一覧にも現れません。再開するにはbudgetを変更するか外します。
費用の突き合わせにも癖があります。スレッドごとの usage は個別に丸められ、セッションの実行時間にかかる費用も含まないため、合計はセッションの list_cost と一致しません。上限の判定に使われるのはセッション側の値です。
サンドボックス・ファイルシステム・vaultは共有される
アクセス範囲を役割ごとに絞るなら、共有されるものではなく、独立しているものの側で制御します。あるエージェントが書いたファイルは、別のエージェントも同じパスで読めるからです。
共有されるものと独立しているもの
共有される
サンドボックスとファイルシステム、vault認証情報です。認証が必要な外部サービスへ、複数のエージェントが同じ資格情報でアクセスする構成が組めます。
独立している
ツール、MCPサーバー、会話コンテキストです。モデル、システムプロンプト、skillsもエージェントごとの設定で、アクセス範囲を絞る手段になるのはツール定義とMCPサーバーの宣言です。
まとめ
コーディネーターが効くのは、並列化・専門化・エスカレーションのどれかに当てはまる作業です。単一の直線的な作業なら、挟まないほうが構成は単純です。環境を limited にしている場合は、MCPサーバーの許可設定が全エージェント分そろっているかを最初に見ます。
rosterにアドバイザーを加えてコーディネーターの判断そのものを補助する構成は、Managed Agentsのマルチエージェントにアドバイザーをつけるで扱います。Managed Agents全体の設計思想はAgent SDKのManaged Agentsの設計思想を参照してください。呼べる相手を名簿のエージェントだけに絞る設定は、Managed Agentsのpredefined_agentsとinlineにあります。動的ワークフローのrunの追い方はworkflow runを追う方法にあります。