Claude Codeサブエージェントのネスト—3階層の設定と累計200の上限

Claude Codeサブエージェントのネスト—3階層の設定と累計200の上限 | mohablog

Subagent spawn limit reached が出てサブエージェントの起動が止まる。原因になりうる上限は、深さ・同時実行数・セッション累計の3か所です。Claude Code v2.1.221 時点で、それぞれ独立した環境変数を持っています。

目次

depth・同時実行・累計は別々の変数で効く

公式ドキュメント “Session subagent limit” セクションの書き出しがそのまま答えになっています。

Three separate limits control subagent use, each with its own variable

上限既定値環境変数到達時の挙動必要バージョン
ネストの深さ3層CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTHエラーなし。Agent ツールが黙って消えるv2.1.219
同時実行数20CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTSConcurrent subagent limit reachedv2.1.217
セッション累計200CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONSubagent spawn limit reachedv2.1.212

深さだけエラーが出ない

同時実行と累計は、上限に当たった瞬間に Agent ツールがエラーを返します。深さは違う。上限の層に到達したサブエージェントからは Agent ツール自体が取り除かれ、そのサブエージェントは委譲できることを知らないまま自力で作業を終えます。ログを追っても何も残りません。

設定は settings.json の env に置く

{
  "env": {
    "CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "2",
    "CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "8"
  }
}

値は文字列で書きます。~/.claude/settings.json に置けば全プロジェクト、.claude/settings.json ならリポジトリ単位。シェルの環境変数として渡しても同じように効きます。

既定値が3層に落ち着くまで

ネストの既定値は2026年に入ってから2回変わっています。公式ドキュメントの Note が経緯を残しています。

v2.1.172 through v2.1.216: subagents could nest by default, up to five layers deep, and the limit couldn’t be changed.

5層固定だった時期

v2.1.172 でネストが入ったとき、深さは5層固定で変更不可でした。日本語の解説記事の多くはこの時期に書かれたもので、いまも「最大5階層」と書いてあります。v2.1.217 以降は成立しません。

1に落ちて3で止まった

v2.1.217 で既定値が 1 に下がり、ネストは明示的に上げない限り無効になりました。この状態が2バージョン続いたあと、v2.1.219 で 3 に引き上げられ、同時に環境変数での変更が入っています。5層固定 → 1(実質オフ)→ 3(可変)という動き。6週間で既定値が2回動いています。

depth の上限に当たると Agent ツールが消える

ドキュメントの記述を実測で確かめます。検証用に、自分が持っているツール名だけを返すサブエージェントを .claude/agents/ に置きました。

---
name: tool-lister
description: 自分が使えるツール名を列挙して返す検証用エージェント
tools: Read, Agent
model: haiku
---
あなたが実際に呼び出せるツールの名前を、カンマ区切りで1行だけ出力してください。説明は不要です。

tools には ReadAgent を明示しています。定義上は委譲できるエージェントです。

depth=1 では Agent が定義から剥がされる

CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=1 claude -p \
  "Agentツールで subagent_type='tool-lister' を run_in_background=false で起動し、返ってきたテキストをそのまま出力して" \
  --dangerously-skip-permissions
Read

tools: Read, Agent と書いたのに、実際に渡っているのは Read だけ。フロントマターの Agent は無視されます。上限の層では定義より優先されるということ。

既定のまま走らせると Agent が残る

claude -p \
  "Agentツールで subagent_type='tool-lister' を run_in_background=false で起動し、返ってきたテキストをそのまま出力して" \
  --dangerously-skip-permissions
Read, Agent

環境変数を外すと既定の3層。1層目のサブエージェントはまだ2層分の余裕があるので、Agent を持ったまま起動します。

depth=2 なら1層目だけが委譲できる

境界がどこに引かれるかを見るため、検証エージェントを書き換えます。自分のツールを self=[...] で返し、Agent が使えるなら同じエージェントをもう1段呼んで child={...} に入れる、という定義。

---
name: tool-lister
description: 自分の階層で使えるツールを列挙し、可能なら1つ下の階層も調べる検証用エージェント
tools: Read, Agent
model: haiku
---
次の手順で1行だけ出力してください。

1. 自分が実際に呼び出せるツール名を列挙し `self=[...]` の形にする
2. Agent ツールが使えるなら subagent_type='tool-lister' を1つ起動し、
   その戻り値を `child={...}` として付ける。使えないなら `child=none` と書く

出力例: `self=[Read,Agent] child={self=[Read] child=none}`
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=2 claude -p \
  "Agentツールで subagent_type='tool-lister' を run_in_background=false で起動し、返ってきたテキストをそのまま出力して" \
  --dangerously-skip-permissions
self=[Read,Agent] child={self=[Read] child=none}

1層目は Agent あり、2層目は Read のみ。2 という値はメイン会話の下に何層のサブエージェントを許すかを指していて、2層目が最深になります。ネストを完全に切るなら 1

fork だけツールが残ってエラーを返す

会話を分岐する fork は上限の層でも Agent がツール一覧から消えません。代わりに呼び出すとエラーが返ります。fork はメイン会話の tool pool をそのまま引き継ぐ仕様で、他のサブエージェントに掛かる2段のフィルタを通らないためです。

同時実行が20で頭打ちになる条件

“Concurrent subagent limit” セクションが定めているのは、走っている数の上限です。20個が同時に動いている状態で Agent ツールを呼ぶと Concurrent subagent limit reached で失敗し、エラー文にはリトライしないよう書かれています。どれかが終われば再び通る、という単純な仕組み。

枠は取るがブロックされない経路がある

  • /subtask で自分が始めたセッション内 fork – 走っている間ずっと枠を1つ占有するが、上限では止まらない
  • 終了済みサブエージェントの再開 – 上限チェックを通らずに新しい枠を取るので、実行数が20を超えることがある

「20で必ず止まる」ではなく「Claude が Agent ツールで起動する分だけが20で止まる」。Workflow のエージェントや Agent Teams のチームメイトは、この上限ではなくそれぞれの上限に従います。

ultracode では enforce されない

effort レベルを ultracode にしたセッションは同時実行の上限が適用外です。20個で頭打ちにならない代わりに、走らせすぎたときのブレーキも無くなります。

累計200に何がカウントされるか

「1セッションで200個」の数え方は経路によって違います。

数えるものと数えないもの

実行経路累計200に入るか
ネストしたサブエージェント入る(層に関係なく1つずつ)
バックグラウンドのサブエージェント入る
/subtask のセッション内 fork入る(ただし上限到達後も起動はできる)
Workflow のエージェントが Agent ツールで起動した分入る
Workflow スクリプトの agent() 呼び出し入らない(Workflow 自身の上限で管理)
/fork で作った別セッション入らない(独立した予算を持つ)

終わったサブエージェントも数えたまま。200は同時に走る数ではなく、セッションを通じた累計です。

/clear で戻るが、戻らない条件がある

/clear を打つとカウントがリセットされ、200の予算が丸ごと戻ります。ただし clear をまたいで生き残る仕事、たとえば実行中の Workflow がある場合はカウントが引き継がれます。長時間の Workflow を走らせたまま /clear しても、累計はゼロには戻りません。

ネストする reviewer を組む

---
name: review-lead
description: 変更差分をレビューし、指摘ごとに検証エージェントを立てて裏を取る
tools: Read, Grep, Glob, Agent
model: sonnet
---
差分から問題を洗い出したあと、指摘1件につき verifier サブエージェントを1つ起動し、
再現するかどうかを判定させてください。確認が取れた指摘だけを返します。
---
name: verifier
description: 指摘が実際に再現するかを1件だけ検証する
tools: Read, Grep, Bash
disallowedTools: Agent
model: haiku
---
渡された指摘が実際のコードで再現するかを確認し、再現する/しないと根拠を1行で返してください。

公式ドキュメント “Let subagents spawn their own subagents” が挙げている例がまさにこの形です。

a reviewer subagent that dispatches a verifier per finding, so the intermediate output never reaches your main conversation

親に Agent、子に disallowedTools

verifier 側で disallowedTools: Agent を明示している点。既定が3層である以上、放っておくと verifier も孫を起動できます。tools から外すか disallowedTools に入れるか、どちらかで止める必要があります。深さの上限はセッション全体に掛かる制限であって、個別のエージェントを読み取り専用に保つ手段ではありません。

深い層ほどモデルを軽くする

上の例では review-lead が sonnet、verifier が haiku。指摘が10件出れば haiku のセッションが10本走ります。ここを触らずにおくと、10本すべてがメイン会話と同じモデルで動く。model を省略したときの既定は inherit なので、深い層こそ明示が要ります。カスタムサブエージェントの作り方で整理した toolsmodel の継承ルールが土台になります。

ネストを切ったほうがいい場面

中間結果を自分の目で見たいときは 1 に落とします。ネストの利点は中間出力がメイン会話に届かないことで、それは裏返せば2層目以降が何をしたか手元に残らないということ。返ってくるのは最上位サブエージェントの要約だけです。

権限を絞ったサブエージェントを運用しているリポジトリでも切っておく価値があります。tools: Read, Grep で読み取り専用にしたつもりのエージェントでも、Agent を明示的に外していなければ孫を起動でき、その孫は自分の tools 定義で動きます。

まとめ

  • サブエージェントを止める上限は深さ・同時実行数・セッション累計の3か所にあり、環境変数も既定値も別々
  • 深さの既定は 3層(v2.1.219以降)。CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH で変更し、1 でネスト無効
  • 上限の層では tools に書いた Agent が黙って剥がされる。実測では depth=2self=[Read,Agent] child={self=[Read] child=none}
  • 同時実行の既定は20、セッション累計は200。/fork の別セッションと Workflow の agent() は累計に入らない
  • 個別のエージェントを孫を持たない状態に保つには disallowedTools: Agent を書く。深さの上限では代用できない
  • 「最大5階層・変更不可」は v2.1.216 までの仕様
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次