Claude Media
Claude Codeのサブエージェント並列設計 — フォーク既定化で何が変わるか

Claude Codeのサブエージェント並列設計 — フォーク既定化で何が変わるか

Claude Codeのフォーク(fork)は会話を丸ごと引き継ぐサブエージェント。v2.1.232で対話セッションの既定になりました。深さ制限・並列数上限との相互作用も扱います。

Claude Code v2.1.232で、対話セッションのsubagent forkが既定onになりました。subagent_type: "fork"のサブエージェントは会話全体とプロンプトキャッシュを引き継ぎ、fork・非forkを問わずClaudeが起動したサブエージェントはバックグラウンドで動きます。並列タスクの組み方は「毎回フレッシュなコンテキストで投げる」から「同じ文脈を安く複製して投げる」へ重心が動いています。

フォーク(fork)とは、それまでの会話・システムプロンプト・ツールをそのまま引き継ぐサブエージェントのことです。オンとオフは環境変数CLAUDE_CODE_FORK_SUBAGENTで切り替わります。

この記事では、forkと非forkで何が届き何が届かないか、上限と課金に響く条件、そして/fork・/subtask・--fork-sessionの取り違えを扱います。手元のv2.1.287でclaude --helpを確認した出力も載せています。

forkと非forkサブエージェントは何が違うか

forkは、それまでの会話をまるごと引き継ぐサブエージェントです。システムプロンプト・ツール・モデル・メッセージ履歴のすべてがメインセッションと同一になるため、状況を説明し直さずに横道のタスクを任せられます。fork自身のツール呼び出しはメイン会話には出ず、最終結果だけが返るので、メインのコンテキストウィンドウは汚れません。

くらべる

forkと非forkの出発点

会話を複製

fork

会話全体の履歴・システムプロンプト・ツール・モデルがメインと同一です。権限確認はターミナルにそのまま出て、プロンプトキャッシュもメインと共有します。

委任メッセージから開始

非forkサブエージェント

Claudeが書いた委任メッセージだけで始まり、ツールは定義ファイル由来です。モデルは定義のmodelフィールドで決まり、バックグラウンド実行ではツールがさらに絞られます。権限確認はメインセッションに出て、キャッシュは別です。

ClaudeはAgentツールでforkというsubagent_typeを指定してforkを起動します。タイプを指定しなければ、セッションにそのタイプが残っている限りgeneral-purposeサブエージェントが起動します。

並列に投げたタスクの出力がメインと揃わないとき

非forkのサブエージェントも、空っぽのコンテキストから始まるわけではありません。エージェント自身のシステムプロンプトと委任メッセージに加えて、次の4つが初期コンテキストに乗ります。

  • CLAUDE.mdとAGENTS.mdの全階層(~/.claude/CLAUDE.md・プロジェクトルール・CLAUDE.local.md・管理ポリシー)
  • セッション開始時点のGitステータスのスナップショット
  • エージェント定義のskillsフィールドで指定したSkillの全文
  • SendMessageで名指しできる相手一覧(sibling roster)

ビルトインのExploreとPlanは、CLAUDE.mdとGitステータスを省略します。自作エージェントでも、omitClaudeMd: trueでCLAUDE.mdを省けます。

一方、メイン会話にあってもforkでなければ届かない状態が3つあります。

  • 出力スタイル: 非forkはサブエージェント自身のシステムプロンプトで動くため反映されません
  • 自動メモリー: 読み込まれません。永続メモリーを持たせたいならmemoryフィールドを使います
  • コンテキストウィンドウのサイズ: サブエージェント自身のモデルで決まり、親より小さいモデルに委任すればウィンドウも小さくなります

出力フォーマットや文体がメインと揃わないと感じたら、まずこの3点を疑うのが早道です。forkはメインセッションの状態をそのまま引き継ぐので、3つとも揃います。

/fork・/subtask・--fork-sessionを取り違えない

名前が似たものが3つあり、起きることはまるで違います。

入力起きること
/subtask <タスク>起きること会話を引き継ぐforkサブエージェントを開始する(v2.1.212以降)
/fork起きることagent viewが有効なら、セッション全体をコピーした新しいバックグラウンドセッションを作る
claude --resume --fork-session起きること再開時に元のセッションIDを使い回さず、新しいセッションIDで始める

/forkの意味はバージョンと設定で変わります。v2.1.161〜v2.1.211ではforkサブエージェントを起動するコマンドでした。agent viewを無効にしている場合は今でも/forkでforkサブエージェントが始まりますが、そうでなければ/subtaskのほうがその役割です。

手元のv2.1.287でclaude --helpを開くと、--fork-sessionは次のように説明されていました。

--fork-session  When resuming, create a new session ID
                instead of reusing the original (use
                with --resume or --continue)

このフラグはサブエージェントのforkとは別物で、--resumeか--continueと組み合わせて過去のセッションを枝分かれさせるためのものです。

/subtaskの使い方は次のとおりです。タスクの最初の数語でforkに名前が付きます。

/subtask パーサーの変更に対する単体テストを書いて

forkはプロンプト入力欄の下のパネルに表示され、バックグラウンドで走りながら作業を続けられます。成功して終わった行は消え、失敗した行や自分で止めた行は30秒残ります。

走っているforkを覗く・止める・追いかけて指示する

パネルでは、主セッションと各forkが1行ずつ並びます。操作は次のとおりです。

キー動作
↑ / ↓動作行の移動
Enter動作forkのトランスクリプトを開き、追加メッセージを送る
x動作実行中なら停止、終了済みなら行を消す
Esc動作プロンプト入力欄に戻る

トランスクリプトを開いている間は、/compact・/clear・/rewindが主会話に作用するため、実行前に確認が入ります。/modelと/fastは主会話の設定を変えるので、この画面からは実行できません。

作業待ちのforkに今すぐ読ませたいときは、Ctrl+EnterかCtrl+X Ctrl+Sで送ります。シェルコマンドなどバックグラウンドへ移せる待ち状態は移され、forkが書き込み中なら、それが済んでから読まれます。送信キーはv2.1.275以降で、待ち状態をバックグラウンドへ移す挙動はv2.1.286以降です。

完了済みのサブエージェントに続きを任せるときは、Claudeがエージェントidか名前をtoに指定してSendMessageを送ります。完了済みのサブエージェントは、新たなAgent呼び出しなしにバックグラウンドで自動的に再開します。TaskStopで止めたサブエージェントも、実行が終わってから同じ扱いです。ExploreとPlanは1回限りでエージェントidを返さないため再開できません。

自分で止めたサブエージェントは別です。/tasksでxを押す、またはSDKのstop_taskで止めたものは、Claudeがメッセージを送っても再開されず、キャンセル済みだと伝えられます。ただし、パネルに行が残っている間に自分でトランスクリプトへ入力して再開すると、その後はClaudeからのメッセージでも再開されます。

v2.1.199以降は、同じ名前が別のエージェントに使い回されていないかもSendMessage側でチェックされます。誤送信は拒否され、名前が今指しているエージェントがエラーに示されます。

サブエージェントのトランスクリプトは主会話と別ファイルに保存されるため、主会話が/compactで圧縮されても影響しません。サブエージェント自身も同じ条件で自動コンパクションの対象になり、CLAUDE_AUTOCOMPACT_PCT_OVERRIDEも効きます。トランスクリプトはcleanupPeriodDaysの保持期間(既定30日)を過ぎると削除されます。

バックグラウンド実行が標準になると何が変わるか

v2.1.232より前は、Claudeが結果をすぐ必要とするかどうかでフォアグラウンド・バックグラウンドを選んでいました。fork既定onの対話セッションでは、この判断そのものが変わります。ClaudeがAgentツールで起動するサブエージェントは、fork・非forkを問わずバックグラウンドで動き、Claude側からフォアグラウンド実行を要求することはできません。Agentツールのrun_in_backgroundパラメーター自体が取り除かれます。

バックグラウンドのサブエージェントは、権限確認が必要なツール呼び出しに達するとメインセッション側にプロンプトが表示され、どのサブエージェントからの要求かが名前で示されます。承認すればサブエージェントは続行し、Escで個別のツール呼び出しだけを拒否できます。セッション全体に及ぶ許可を答えた場合は、その判断がメイン会話を含むセッション全体に適用されます。

プロンプトキャッシュはどこまで効くのか

forkのシステムプロンプトとツール定義は親と完全に同一なので、最初のリクエストが親のプロンプトキャッシュを読みます。同じ文脈を必要とするタスクを並列に投げるなら、毎回新規サブエージェントを立てるより安く済みます。

非forkは事情が違います。親とプレフィックスが異なるため、最初のリクエストは親のキャッシュを読めず、自分用のキャッシュを温め直します。しかもサブエージェントはメイン会話のTTL区分の外にあり、サブスクリプションでも保持は5分です。v2.1.242以降は、長いTTLも選べます。subagentPromptCacheTtl設定かCLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL環境変数に1hを指定します。

forkも同じ扱いです。Claude Codeはリクエストごとに、メイン会話か「それ以外」かでTTLの区分を決めており、forkはサブエージェントや圧縮と並んで「それ以外」に入ります。サブスクリプションの範囲内でも既定は5分です。forkの最初のリクエストは親のキャッシュを読みますが、その後fork自身が書くキャッシュは5分で切れます。

再開したサブエージェントの最初のリクエストは、元の実行が温めたキャッシュを読めます。ただしサブエージェントのキャッシュは既定で5分の非アクティブで切れるため、元の実行から5分以上空けて再開すると、キャッシュは作り直しになります。再開したforkのキャッシュが効かなくなる不具合は、v2.1.281で修正されています。

上限に当たるのはどんなときか

深さ制限・同時実行数・worktree分離は、それぞれ別の設定で動きます。

制約

forkに関わる3つの制約

  • 入れ子の深さ

    既定で主会話の下に3階層までです。CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTHで変更でき、1にすると入れ子が無効になります。forkがさらにforkを起動することはできません。

  • 同時実行数

    既定は20です(v2.1.217以降)。上限に達した状態でAgentツールが起動しようとすると、Concurrent subagent limit reachedで失敗し、Claudeは再試行しないよう伝えられます。ultracodeが有効なセッションでは適用されません。

  • 作業ツリー

    isolation: "worktree"付きのforkは、編集を別のgit worktreeに書き込みます。同じリポジトリで複数のforkに実装を試させるなら、分離しなければ、どのforkも同じチェックアウトに書き込みます。

深さ上限での挙動には非対称があります。深さ上限に達すると、非forkのサブエージェントからはAgentツールが取り上げられます。forkだけはツール一覧にAgentが残りますが、呼び出すとエラーが返り、起動はしません。

入れ子の結果の戻り方にも違いがあります。対話セッションでは、孫のバックグラウンドサブエージェントを起動したサブエージェントは、その結果が返るまで終わりません。非対話モードとAgent SDKでは待たず、起動元が終わったあとに孫が完了すると、結果はメイン会話に直接届きます。

同時実行数の上限(CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTSで変更可能)は、数え方に非対称があります。ClaudeがAgentツール経由で起動するものだけが上限でブロックされます。ユーザーが/subtaskで開始したforkは実行中ずっと枠を使いますが、上限に止められることはありません。

完了済みサブエージェントの再開も同じ型で、新しい枠を確保しますが上限は確認されません。再開が重なると、実行中の数が上限を超えることがあります。

fork既定を切り替える・特定のforkだけ止める

fork既定onは対話セッションのみが対象です。非対話モード(-p)とAgent SDKでは既定offのままで、CLAUDE_CODE_FORK_SUBAGENT環境変数で個別に上書きできます。

値効果
未設定効果対話セッションはon、非対話・Agent SDKはoff(既定)
1効果非対話モード・Agent SDKでもon
0効果すべてのセッションでoff

v2.1.232より前の版でonにするには、この変数を1にします。/subtaskはfork modeのon/offにかかわらず使えます。

settings.jsonでの設定例
{
  "env": {
    "CLAUDE_CODE_FORK_SUBAGENT": "1"
  }
}

fork自体は残したまま、Claudeがforkを起動する経路だけを塞ぐこともできます。Agent(fork)ルールで拒否すれば、forkを除く通常のサブエージェントをバックグラウンドで起動する挙動は維持したまま、fork起動だけを止められます。定義ファイルでcontext: forkを指定したSkillは、この既定on/offとは別のルールに従います。

並列設計はどこを見直すか

fork既定化が変えたのは実装の細部ではなく、並列タスクを組むときの既定の選び方です。同じ文脈を必要とする作業は、general-purposeで新規コンテキストを渡すより、forkで会話を複製するほうが既定で安価な選択になりました。Anthropic APIで自前のコーディングエージェントを組む場合は、サブエージェント実行中もリードを待たせない設計も参考になります。

上限に張り付くほど並列度を稼ぐ設計で、並列ワークフローの再現性を上限の影響から切り離したいなら、/subtaskを併用する選択肢があります。Agent Teamsのteammateは同時実行上限の数え方の外にあり、独自の上限に従います。また、in-processのteammateが起動したサブエージェントは、fork既定onでもフォアグラウンドで動きます。

まとめ

同じ文脈を何度も使う作業はfork、文脈を絞りたい調査は定義ファイルのサブエージェント、と使い分けを先に決めておくと、並列の組み方がぶれません。

この記事を共有:XはてブLinkedIn