「サブエージェントに投げれば速くなるはず」と並列化を試したのに、コストが跳ね上がった/途中で止まった/返ってきた結果が噛み合わない——そんな詰まり方をしていませんか。並列サブエージェントは、条件を満たしたときにだけ効きます。本稿は Claude Agent SDK(TypeScript v0.3.219 / Python v0.2.127 以降、Claude Code v2.1.219 以降をバンドル、2026-09-03 時点)を前提に、並列が効く条件、SDK が敷く上限、そしてゼロから通せる並列レビュー・ワークフローの実装と、docs に載っていない運用の勘所までをまとめます。
並列実行が効く前提条件
Claude のサブエージェントは同時に走らせられます。
independent subtasks finish in the time of the slowest one
独立したサブタスクは、全部の合計時間ではなく最も遅い1つの時間で完了します。裏を返せば、サブタスク間に依存があると直列待ちが混ざり、この利点は薄れます。並列化の第一条件は「サブタスクが互いに独立していること」です。
もう一つの土台がコンテキストの独立性です。各サブエージェントは自分の会話の中だけで動き、親の会話履歴やツール結果は引き継ぎません(each subagent runs in its own conversation, which starts fresh)。この性質があるからこそ、投入した複数タスクが互いに干渉せず並列に走れます。
SDK が敷く4つの境界(深さ・同時実行・予算・バージョン)
並列度を上げるほど、SDK 側の上限が実運用を左右します。
まず深さ。デフォルトのネスト上限はメインエージェント配下に3層です(3 layers of subagents below your main agent)。CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH を 1 にすると、サブエージェントが自分のサブエージェントを生成すること自体を止められます(1 stops your subagents from spawning any of their own)。
次に同時実行。
20 subagents running at once, counting every subagent Claude spawns
デフォルトは同時20で、Agent ツールで生成される全サブエージェントを合算してカウントします。上限に達すると SDK は新規生成を拒否し「Concurrent subagent limit reached」を返して、実行中の数が上限を下回るまで待ちます(returning Concurrent subagent limit reached)。ドキュメントによれば ultracode が有効なセッションは拒否されません。
予算上限に達すると、SDK は新規生成を拒否し、実行中のバックグラウンドサブエージェントを停止し、クエリを error_max_budget_usd サブタイプで終了します(ends the query with the error_max_budget_usd result subtype)。
最後にバージョン。これらの上限は TypeScript SDK v0.3.219 / Python SDK v0.2.127 以降で利用可能で、それ以前では一部の上限が存在しないかデフォルトが異なります(This section describes TypeScript SDK v0.3.219 and Python SDK v0.2.127 and later)。なおサブエージェントはデフォルトでバックグラウンド実行され、run_in_background を省略した Agent ツール呼び出しはバックグラウンドサブエージェントを起動します(Subagents run in the background by default)。
ゼロから作る並列レビュー・ワークフロー(通し実装)
3つのソースファイルを別々のサブエージェントで同時にレビューし、結果を1つにまとめる最小構成を、空のディレクトリから通します。
mkdir parallel-review && cd parallel-review
mkdir src
printf 'def add(a, b):\n return a - b\n' > src/a.py
printf 'def div(a, b):\n return a / b\n' > src/b.py
printf 'def norm(xs):\n n = len(xs)\n return [x / n for x in xs]\n' > src/c.py
エージェント定義を agents.json に置きます。各エージェントは description と prompt を持ちます。tools を省略すると、サブエージェントが使えるツールをすべて継承します(the subagent gets every tool available to subagents)。
{
"reviewer": {
"description": "Reviews one source file for correctness bugs. Give it a single file path.",
"prompt": "You review one file for correctness bugs. Read the file path you are given, list each bug with its line number and a one-line fix, and return only that list as your final message. Do not edit files."
},
"aggregator": {
"description": "Merges multiple reviewer outputs into one prioritized report.",
"prompt": "You merge reviewer findings into a single prioritized report. Return only the report as your final message."
}
}
実行します。ファイルごとに1サブエージェントを割り当てて並列レビューし、その後 aggregator で統合させます。
claude --agents agents.json -p \
"Use the reviewer agent to review src/a.py, src/b.py, and src/c.py in parallel — one subagent per file — then use the aggregator agent to merge the findings into one report."
端末に出る実文言はバージョンや環境で変わります。断定できない文言を推測で書くのではなく、状態で成功を確認します。
成功時の確認ポイント:
- コマンドがゼロ終了で戻る(直後に echo $? を実行すると 0)
- reviewer が同時起動し、最も遅い1つの完了時刻で3件が出そろう
- 各サブエージェントの最終メッセージ(バグ一覧)だけが親に返り、aggregator がそれを統合する
- 完了した各サブエージェントの Agent ツール結果に agentId: を含むテキストブロックが現れる
(the Agent tool result includes a text block containing agentId)
詰まりどころ:型を間違えた定義は起動時に弾かれる。 例えば次のように description に数値を渡し、prompt を欠いた定義で起動すると、サブエージェントを1つも動かす前に検証で落ちます(Claude Code 2.1.259 で確認)。
{
"reviewer": {
"description": 1
}
}
Error: Invalid --agents configuration:
reviewer.description: Invalid input: expected string, received number
reviewer.prompt: Invalid input: expected string, received undefined
description が数値なので "expected string, received number"、prompt が無いので "received undefined" が出ます。これは失敗ではなく早期に弾いてくれる仕組みで、CI で agents.json を検証する足がかりになります。
暴走を止めたいときは深さを絞ります。
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=1 claude --agents agents.json -p "..."
1 にすると reviewer / aggregator が自分のサブエージェントを生成できなくなり、ネストは1層で止まります。なお同名ならプログラム定義がファイル定義より優先されます(Programmatically defined agents take precedence over filesystem-based agents)。
docs に無い運用設計:final-message 契約から逆算する
ここが有料の核心です。並列サブエージェントの設計は、次の1点から逆算すると崩れません。
only its final message returns to the parent
親に返るのは最終メッセージだけで、途中のツール呼び出しや結果は届きません。ここから実務上の指針が3つ導けます(以下は事実の帰結にもとづく筆者の設計意見です)。
第一に、サブエージェントの prompt は「最終メッセージに何を入れるか」を最初に定義するべきです。上の reviewer が「return only that list as your final message」と明示しているのはこのためです。親が拾えるのは最後の1発だけなので、途中の探索ログに頼る設計は成立しません。
第二に、本番の同時実行はデフォルトの20より低く固定することを勧めます。20は上限であって推奨値ではありません。APIのレート制限とコストは同時数に比例して跳ねるため、まず4〜8程度から始め、上限到達時の「Concurrent subagent limit reached」を観測しながら上げるのが安全です。特に Claude Opus 5 は委譲しやすいため、上限を明示する価値が高いとドキュメントも述べています(Claude Opus 5 delegates to subagents more readily than earlier models)。
第三に、深さは目的が明確でない限り1に絞る。サブエージェントがさらにサブエージェントを産む多段委譲は、コストと所要時間の予測を一気に難しくします。多段が本当に要るときだけ2以上に緩めます。
再開が要る設計なら、返ってくる agentId を保存しておきます。再開したサブエージェントは以前のツール呼び出し・結果・推論を含む完全な会話履歴を保持します(A resumed subagent retains its full conversation history)。ただし組み込みの Explore と Plan はワンショットで agentId を返しません(The built-in Explore and Plan agents are one-shot and don't return an agentId)。再開したいならカスタムエージェントか汎用エージェントを使います。
既知の限界と注意
親はサブエージェントの最終メッセージをそのまま信用しません。
Claude Code scans the final message for instruction-shaped patterns
v2.1.210 以降、親が読む前に最終メッセージがインストラクション形状のパターンでスキャンされ、制御タグの模倣や権限設定への言及、会話ターンの境界マーカーが無害化されます。サブエージェントから親へ「指示」を渡す設計は通らない前提で組んでください。データを返すのは可、命令を返すのは不可、と割り切るのが安全です。
そのほか運用で効いてくる点を挙げます。
- サブエージェントが maxTurns 上限に達すると Agent ツール結果の出力が partial とマークされます。この partial マークには Claude Code v2.1.246 以降が必要です(
The partial marking requires Claude Code v2.1.246 or later)。 - 環境変数の扱いが SDK で異なります。TypeScript は env でサブプロセス環境を置換するため、PATH などを残すには process.env をスプレッドします(
spread process.env into it to keep variables like PATH)。Python は継承環境にマージします。 -
CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1を設定すると組み込みの汎用サブエージェントが消え、subagent_type なしの呼び出しは失敗します(such a call fails with subagent_type is required)。 - 1回の会話に収まらない大規模な多数エージェント調整には Workflow ツールを使います(
The Workflow tool is available in the TypeScript Agent SDK v0.3.149)。 - サブエージェントのトランスクリプトは別ファイルに保存され、メイン会話から独立して永続します(
Subagent transcripts are stored in separate files)。事後の監査やデバッグはここを見ます。 - サブエージェントはメインセッションの extended thinking 設定を継承します(
A subagent also inherits the main session's extended thinking configuration)。 - Agent ツールは tool_use ブロックでは「Agent」、system:init のツール一覧では「Task」として現れます(
in tool_use blocks but as)。ログを追うときの見分けに注意してください。 - 補足として、Claude Code v2.1.198 より前はバックグラウンド既定が段階展開中で、run_in_background を省いた呼び出しが同期実行になり得ました(
Before Claude Code v2.1.198, the background default was rolling out gradually)。