Agent SDKのサブエージェント上限 — 深さ・並列数・支出をキャップする
Agent SDKにはサブエージェントの深さ・同時実行数・支出をコードから制限する3つの仕組みがあります。既定値と上限到達時の挙動を解説します。
サブエージェントはなぜ上限が必要か
allowedToolsにAgentを含めた瞬間から、いつ・何個のサブエージェントを立てるかはClaude自身の判断に委ねられます。サブエージェントはそれぞれ独立してAPIリクエストを送るため、その費用はクエリ全体のtotal_cost_usdにすべて合算されます。サブエージェントは自分自身もサブエージェントを起動できます。1つのプロンプトから木構造にエージェントが枝分かれし、放っておくと際限なく広がります。
Agent SDKはこの広がり方を3つの軸——ネストの深さ、同時実行数、クエリ全体の支出——でキャップする仕組みを持ちます。深さと同時実行数は環境変数で、支出はクエリのオプションで設定します。以下はTypeScript SDK v0.3.219 / Python SDK v0.2.127以降(Claude Code v2.1.219以降を同梱するリリース)の挙動です。それより前のSDKでは一部の上限が欠けているか既定値が異なります。
深さ・同時実行数・支出はどう設定するか
| 上限 | 設定方法 | 既定値 | 上限に達したときの挙動 |
|---|---|---|---|
| 深さ | 設定方法CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH | 既定値3層(メイン下)。1で子エージェントの起動自体を止める | 上限に達したときの挙動最下層のサブエージェントは自分では起動できず、委任された作業を自力でこなす |
| 同時実行数 | 設定方法CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS | 既定値20(Agentツールで起動した全サブエージェントを数える) | 上限に達したときの挙動新規起動を拒否しConcurrent subagent limit reachedを返す |
| 支出 | 設定方法TypeScriptはmaxBudgetUsd、Pythonはmax_budget_usd | 既定値上限なし(呼び出し自身の支出を数え、サブエージェントの分も含む) | 上限に達したときの挙動新規起動を拒否しBudget limit reachedを返し、バックグラウンドの実行中エージェントも停止 |
深さと同時実行数は環境変数なのでenvオプション経由で渡し、どちらもClaude Code v2.1.217以降が必要です。値は正の整数だけが有効で、それ以外の文字列は無視されます。つまり上限を調整はできても、取り払うことはできません。CLIで使うときは、settings.jsonのenvに同じキーを書けば同じ効果になります。
envの渡し方はSDKで挙動が違う
同じenvオプションでも意味が逆
TypeScript SDK
サブプロセスの環境変数をenvの中身で丸ごと置き換えます。PATHを残すにはprocess.envを展開してから上書きします。展開を忘れると、PATHなどの変数が失われます。
Python SDK
渡した内容を、継承済みの環境変数へマージします。上限に使うキーだけを書けば足ります。
次の例は、ネストを完全に止め、同時実行を最大5に絞り、推定支出が5ドルに達したらクエリを止める設定です。
const result = query({
prompt: "Audit every service in this repo for unhandled promise rejections",
options: {
allowedTools: ["Read", "Grep", "Glob", "Agent"],
env: {
...process.env,
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH: "1",
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS: "5",
},
maxBudgetUsd: 5,
},
});options = ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"],
env={
"CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "1",
"CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "5",
},
max_budget_usd=5.0,
)支出上限の下で見える結果は3通りです。上限未満で終わればsuccessと実際のコストが返ります。上限に達するとerror_max_budget_usdというサブタイプとともに上限以上のコストが返り、エラーハンドラーに処理が移ります。同時実行数の上限に達した場合は、クエリのメッセージストリームにConcurrent subagent limit reachedというtool_resultブロックが現れます。Claudeもこれと同じ内容をAgentツールの結果として受け取ります。
上限の数え方には抜け道がある
表の既定値だけを見ると、上限は厳密な壁に見えます。実際には、数え方と例外を知らないと想定を超えて走ります。
同時実行数: 枠を使うのに止められない実行がある
同時実行数の上限が止めるのは、ClaudeがAgentツールで起動するサブエージェントだけです。ただし、ほかの実行も同じ枠を占有します。
/subtaskで始めたセッション内のフォークは、実行中は1枠を使います。上限に達していても止められません- 終了済みサブエージェントの再開は、新しい枠を使いながら上限を確認しません。再開が重なると、実行中の数が上限を超えることがあります
- ワークフローのエージェントや、エージェントチームのチームメイトは、この上限ではなく別々の上限に従います
上限に当たったときのエラーには、Claudeに再試行しないよう促す文言が含まれます。実行中の数が上限を下回れば、次の起動は再び成功します。ultracodeが有効なセッションは、同時実行数の上限による拒否の対象外です。セッション全体で起動できる総数に上限はありません。
深さ: 最下層でもフォークだけは扱いが違う
深さの上限に達すると、Claude Codeは最下層のサブエージェントからAgentツールを取り上げます。フォークだけは例外で、Agentツールが継承されたまま残りますが、呼ぶとエラーが返り、起動はされません。最下層のサブエージェントが作業を自力でこなして要約を1つ返す、という動きは通常のサブエージェントの場合です。
入れ子のバックグラウンド実行にも癖があります。対話セッションでは、バックグラウンドのサブエージェントを起動したサブエージェントは、その結果を待ってから終わります。非対話モードとAgent SDKでは起動元が待たないので、起動元の終了後に完了した入れ子のサブエージェントは、メイン会話に直接報告します。SDKで木構造を組むときは、結果が想定外の場所から届く前提で受け口を作っておく必要があります。
特定のサブエージェントだけ入れ子を止めたい場合は、そのtoolsリストからAgentを外すか、disallowedToolsにAgentを加えます。入れ子を許可した対話セッションでは、ターミナルのサブエージェントパネルが各行に子孫の数を(+N)で示し、木構造として表示します。
支出: 数えるものと数えないものがある
支出上限は、その呼び出し自身の支出だけを数えます。再開したセッションから復元した累計は含まれず、/clearで予算は最初から数え直されます。error_max_budget_usdで終わった結果では、usageに上限を超えた応答が含まれません。一方、total_cost_usdとmodelUsageにはその応答が入ります。集計はusageではなくtotal_cost_usdかmodelUsageで行います。
ストリーミング入力で複数のメッセージを流す構成では、予算の合計がメッセージをまたいで積み上がります。支出がmaxBudgetUsdに達したあとは、同じ会話の後続メッセージもerror_max_budget_usdで終わります。長寿命のセッションでは、1回のクエリ単位ではなく会話全体の累計で上限を見積もる必要があります。
結果メッセージの3つの項目は、サブエージェントの扱いが違います。usageはトップレベルのループだけを数え、サブエージェント内で使ったトークンを含みません。total_cost_usdは、サブエージェントの分も合わせた金額です。modelUsageも同じく含み、モデル別の内訳で返ります。入れ子が生じた時点でusageは過少になるため、木構造全体のトークンはmodelUsageで見ます。
単一メッセージ入力で最終ターンの終了時にバックグラウンドのサブエージェントが動いていると、Claude Codeは結果を出す前にそれらの完了を待ちます。待っている間の作業も、total_cost_usdとmodelUsageに入ります。
これらのコスト値は開発中の目安で、請求の根拠にはなりません。公式は、課金の確認にはUsage and Cost APIかConsoleのUsageページを使うよう案内しています。支出上限が効くのも、この概算値に対してです。
Opus 5のサブエージェント上限はなぜ従来以上に重要か
Claude Opus 5は従来のモデルよりもサブエージェントへの委任を積極的に行います。そのため深さ・同時実行数・支出の上限は、Opus 5を使うクエリでこそ効いてきます。Claude Codeのclaude_codeシステムプロンプトプリセットを使っている場合、モデルがOpus 5であればシステムプロンプトに「求められない限りAgentツールを呼ばない」という一文が自動的に足されます。ただしAgentツール自体は使える状態のままです。カスタムのシステムプロンプトを使う、あるいはsystemPromptを指定しない構成では、Claude Code側はシステムプロンプトを組み立てないため、この一文は入りません。その場合はOpus 5のプロンプトガイドが挙げる委任抑制の指示を自分のプロンプトに追加する必要があります。
どちらの指示も、あくまでClaudeの振る舞いを誘導するだけです。実際にどれだけ委任するかはClaudeの判断に委ねられているため、指示を入れたかどうかにかかわらず、深さ・同時実行数・支出の上限は別途設定しておく必要があります。
数十から数百規模のサブエージェントを毎ターン束ねたい場合、上限を緩めて力業で対応するより、オーケストレーションをスクリプト側に移すWorkflowツールに切り替えるほうが実質的です。WorkflowツールはTypeScript Agent SDK v0.3.149以降で使え、allowedToolsに含めるとワークフローの実行が自動承認されます。ターンごとの委任ではなくスクリプトが会話の外でエージェントを束ねる仕組みはダイナミックワークフローとはで扱っています。個々のサブエージェントを並列に使う実践パターンはClaude Codeのサブエージェント完全活用にまとめています。
3つの上限はいつ今の形になったか
サブエージェント上限の変遷
- v2.1.212セッション単位の起動総数に上限(既定200)
暴走する委任ループを止めるための上限です。
CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONで変更できました。 - v2.1.217同時実行数(既定20)と深さ(既定1)を導入
入れ子はいったん既定で禁止されました。支出上限がバックグラウンドのサブエージェントを止めない不具合も、この版で直っています。
- v2.1.219深さの既定が3に
サブエージェントが自分の下に、さらに2層まで子を持てるようになりました。
- v2.1.224セッション単位の上限を削除
CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONはno-opになりました。以後は深さと同時実行数の2つだけが数の歯止めです。
深さ1を前提に組んだ構成が、v2.1.219以降では3層まで広がり得る点には注意が要ります。
用途に応じてどう組み合わせるか
無人で動かすCI/CDパイプラインと、対話的に使うローカル開発では、適切な上限の組み合わせが異なります。
| 用途 | 深さ | 同時実行数 | 支出上限 | 狙い |
|---|---|---|---|---|
| CI/CDでの自動レビュー | 深さ1(既定の3から下げる) | 同時実行数既定20のまま、または絞る | 支出上限明示的に設定 | 狙い1回の実行コストを予測可能にする |
| ローカルでの探索的な調査 | 深さ既定3のまま | 同時実行数既定20のまま | 支出上限設定しない、または高めに | 狙い委任の自由度を確保しつつ暴走だけ防ぐ |
| マルチテナントのSDKアプリ | 深さテナントの信頼度で分岐 | 同時実行数テナントごとに調整 | 支出上限テナントの契約枠に合わせて必須設定 | 狙いテナント間でコストを分離する |
無人で動かす構成ほど、3つの上限をすべて明示しておくと実行コストを読めます。ローカルの探索では逆に、絞りすぎるとサブエージェントが委任のたびに壁に当たります。既定値で走らせ、実際のコストや実行時間を見てから絞るほうが手戻りが少なくなります。
まとめ
支出だけは既定で無制限なので、無人で回すならmaxBudgetUsdを明示し、total_cost_usdで結果を確認するのが基本線です。同時実行数は再開とフォークで超えうる前提で、深さは1か2に絞るかどうかをワークロードで決めます。個々のサブエージェントの設計はAgent SDKのサブエージェント定義、権限モードとの組み合わせはAgent SDKのパーミッション制御にまとめています。