委譲とサブエージェント
SubAgentは、親エージェントによって生成される一時的な子runであり、デフォルトで親のアイデンティティを継承します。つまり、同じエージェントエイリアス、同じSecurityPolicy、同じメモリ許可リスト、同じ設定済みモデルプロバイダー、同じツールレジストリを引き継ぎます。トレーシングスパンagent.<alias>.subagent.<run_id>を通じて子として監査可能です。
SubAgentは独立した設定の概念ではありません。スキーマに[subagents.*]ブロックは存在しません。すべてのSubAgentのアイデンティティは、それを生成した親のエージェントループによって決まります。
spawn_subagent と delegate の使い分け
2つのツールが近くにあります。これらは互換性がありません。
spawn_subagent: 同一エージェントを独自のアイデンティティで再度実行し、特定のサブタスクに集中させます。子エージェントは親の完全な権限範囲から、絞り込まれた分を差し引いたものを参照します。アイデンティティを変更せずに、内部のサブタスクをメインの会話履歴から切り離してスコープを限定したい場合に使用します。delegate: リクエストを(エイリアスで指定された)別の設定済みエージェントに引き渡します。ターゲットエージェントは自身のアイデンティティとモデルプロバイダーの下で実行されますが、委譲はゲートされます。呼び出し元のリスクプロファイルでdelegation_policy mode = "allow"を設定する必要があります(デフォルトは"forbidden")、ターゲットは同一プロファイルのピアとして、または明示的なdelegatesエントリとして到達可能でなければなりません。明示的なエントリではmode = "bounded"またはmode = "independent"を選択し、これにより呼び出し元のツール上限が引き続き適用されるかどうかが決まります。別の設定済みスペシャリストが作業を担当すべき場合に使用します。以下の Delegation gating を参照してください。
このページでは spawn_subagent を最初から最後まで説明します。delegate は crates/zeroclaw-runtime/src/tools/delegate.rs にあり、別のサーフェスです。
SubAgentがインスタンス化される仕組み
2つのスポーン箇所がSubAgentSpawn(crates/zeroclaw-runtime/src/subagent/mod.rs:97)に集約されます:
- エージェントループから: モデルは
prompt文字列を指定してspawn_subagentツールを呼び出します。このツールは、レジストリ内の他のツールと同様に登録されています(crates/zeroclaw-runtime/src/tools/mod.rs、SpawnSubagentTool::new)。 - cron から:
JobType::Agentジョブはrun_agent_job(crates/zeroclaw-runtime/src/cron/scheduler.rs)を通じて実行され、同じSubAgentContextを構築しますが、子をトップレベル実行(SubAgent ではない)としてフラグ付けするため、それ自体が 1 階層分のサブエージェントを生成できます。
両方のパスで以下が呼び出されます:
#![allow(unused)]
fn main() {
SubAgentSpawn::for_agent(config, parent_alias)? // 親アイデンティティを解決する
.build(SubAgentOverrides::default())? // 任意の絞り込みを検証する
}
for_agent は親の risk_profile と [agents.<alias>.workspace.read_memory_from] を読み取って継承された許可リストを構築します。親自身のエイリアスは常に追加されるため、SubAgent は常に親自身のメモリ行を参照できます。build はオプションの絞り込みを適用し(下記の権限の継承を参照)、検証済みの SubAgentContext を返します。
ライフサイクル
同期、インプロセス、単一の tokio ランタイム。プロセス境界を越えるものはありません。
- 親のツールループが
spawn_subagentをディスパッチします。ツールはprompt引数を読み取り、空の場合は拒否します。 - ツールは2つのガードを順番にチェックします:
- 深さ1の上限。 呼び出し元の実行自体がSubAgentであった場合(
AgentRunOverrides.is_subagent == true)、"spawn_subagent: a subagent may not spawn its own subagents (depth-1 cap)"を返して拒否します。SubAgentは再帰できません。 - リスクプロファイルのツールゲート。 親の
[risk_profiles.<alias>].deny_all_toolsがtrueの場合、またはallowed_toolsが空でなくspawn_subagentを含まない場合、またはexcluded_toolsがspawn_subagentを含む場合は、親のエイリアス名を含むメッセージを返して拒否すること。allowed_toolsが省略されているか空の場合はツールは利用可能のまま(従来の無制限状態)となる。
- 深さ1の上限。 呼び出し元の実行自体がSubAgentであった場合(
- ツールは
SubAgentSpawn::for_agentとbuildを呼び出します。失敗(不明な親エイリアス、エスカレートするオーバーライド)はToolResult { success: false, error: "subagent spawn failed: ..." }として返されます。 - このツールは
AgentRunOverrides { security, memory: None, is_subagent: true, suppress_memory_inject: true }を構築し(子のSubTurnオリジンはすでにエンジンのメモリ注入をスキップしますが、このフラグはオプトアウトを明示的にします)、subagent-<uuid>をキーとするトレーシングスコープ内でcrate::agent::run(crates/zeroclaw-runtime/src/agent/loop_.rs、pub async fn run)を await します。親のtool実行は、子が返るまでブロックされます。 - 子エージェントのループは完了まで実行されます。そのツールレジストリは新規に構築され、
is_subagent_caller: trueが自身のSpawnSubagentToolに流れ込むため、再帰しようとする試みはすべて同じ深さ1のゲートで拒否されます。 - 子プロセスは
Result<String>を返します。親のspawn_subagentツールがそれをラップします:- 成功:
ToolResult { success: true, output: <child's final response>, error: None }。空の出力はリテラル"subagent completed without output"に置き換えられます。 - 失敗:
ToolResult { success: false, error: Some("subagent run failed: ...") }。
- 成功:
- 親の tool ループは、その
ToolResultを会話コンテキストに含めて継続します。子の中間ターンやツール呼び出しは親の履歴に再生されず、最終的なレスポンスのみが表面化します。
アップストリームに還元されるもの
1 つ: 子の final assistant message を文字列として ToolResult.output でラップしたもの。
- 子のツールコール、中間的な推論ターン、および子が実行したメモリ書き込みは、子のトレーシングスパン配下の構造化ログで観察できますが、親の会話履歴には入りません。
- 子セッションはパス
subagent-<uuid>(cron で生成された実行の場合はcron-<uuid>)の下に存在します。これは会話履歴のキーであり、ファイルシステム上の場所ではありません。これにより子の履歴が親の履歴から分離されます。 - 子プロセスによって実行されたメモリ書き込みは、親のアイデンティティに書き込まれます(SQL/Postgresバックエンドでは同じエージェントUUID、Markdownでは同じワークスペースディレクトリ)。Cronで起動された実行では
memory.auto_saveが無効になるため、オプトインの書き込みは引き続き機能しますが、ルーチンの想起は蓄積されません。
同期的な委任には、親へのストリーミングや途中経過を返す経路がありません。長時間実行される SubAgent は、その全時間にわたって親のツール実行を停止させ、呼び出しごとのタイムアウト設定もありません。バックグラウンドの委任では、代わりに check_result と await_sessions を通じて上限付きのライブ進捗が報告されます(下記のバックグラウンド結果の形式にある progress フィールドを参照してください)。
実行ツリーの反復回数の上限
max_execution_tree_iterations を対象の [runtime_profiles.<alias>] エントリに設定すると、1 つのフォアグラウンド実行ツリー全体でのツールループの反復回数の合計を制限できます。
[runtime_profiles.default]
max_execution_tree_iterations = 12
フィールドは省略可能です。省略すると集計予算は無効になります。明示的な 0 は無効な値です。この予算は max_tool_iterations とは別のものであり、max_tool_iterations は引き続き各エージェントターンごとのローカルなループ上限として機能します。
1つの論理的なルートターンは、1つのアトミックなバジェットハンドルを所有します。フォアグラウンドの spawn_subagent、同期または並列で実行される上限付きの delegate ターゲット、および明示的なライブ SOP の子作業は、このハンドルから消費します。子は、残りスロットが2つ以上ある間だけ予約できます。ルートに最後のスロットしか残っていない場合は、そのスロットを既存のツールなし完了パスのために消費します。ビューを使い果たした子は失敗した ToolResult を返し、親のルートターンは処理を継続できます。同時実行される予約によってカウンターがラップアラウンドすることはありません。
独立したフォアグラウンドの delegate ターゲットは、呼び出し元の残りの割り当てではなく、自身のランタイムプロファイルのバジェットを使用します。ターゲットの上限を省略した場合、呼び出し元に実行ツリーのバジェットがあっても、その独立したターンはそのバジェットによる上限を受けません。デタッチされたバックグラウンドの delegate タスクおよびピアターンも、起動元のターンのバジェットを共有せず、受け手のランタイムプロファイルから新しいルートを生成します。
1回のターンでの複数回の呼び出し
エージェントループは、ターン単位の重複呼び出しガードを適用します。同一ターン内で同一の引数によって2回呼び出されたツールは、通常2回目の呼び出しがスキップされます。spawn_subagent と delegate は、このガードの対象外です。同じプロンプトで複数を起動すること(冗長性、サンプリング、ファンアウト)は、偶発的な繰り返しではなく意図的なパターンであるため、同一の呼び出しがそれぞれ実行され、それぞれの結果が返されます。この対象外措置がなければ、最初の同一呼び出しのみが実行され、その出力のみがモデルに到達することになります。
並列ツール実行が有効な場合(ランタイムプロファイルで parallel_tools = true)、1ターンに含まれる複数の spawn_subagent 呼び出しが同時に実行され、各子の最終応答がそれぞれ自身のツール呼び出しに紐付けられて親に返されます。delegate は parallel: [...] 引数による独自の明示的なファンアウトを備えており(output-strings セクションを参照)、このパスでは各ターゲットがそれぞれ専用のタスクとしてスポーンされ、すべての結果が集約されます。
ZeroCode トランスクリプト検査
フォアグラウンドのspawn_subagentとdelegateの呼び出しには、タスク、指定された場合は委任先の名前、保持された結果テキストを表示する展開可能なトランスクリプトカードがあります。並列委任は、既存のターゲットごとの結果ブロックを含む1回の呼び出しのままです。contextなどの追加引数は入力フィールドに引き続き表示されます。不正な形式またはサイズ超過の引数は、汎用ツールカードにフォールバックします。展開時もフィールドごとに8 KiBまたは100行というツール表示の上限が維持されます。Copyは元の保持済みエントリを使用し、その結果は既に16 KiBに制限されている可能性があります。
委任先の名前はカードの見出しに表示され、その後にタスクと Response セクションが続きます。一致する同期的な委任エンベロープの場合、返された回答が最初に表示され、プロバイダー/モデルのメタデータは展開された Details に残ります。その他の結果形式はそのまま維持されます。レスポンスがない場合は Result not recorded と表示されます。カードは、復元された履歴や返された診断テキストを含め、子が現在実行中であることや子が成功したことを主張しません。バックグラウンド起動や check_result や await_sessions などの管理アクションは汎用カードのままであり、起動の確認応答が子の完了として表示されることはありません。
ライブの子プロセス検査には、親のツール呼び出しIDに関連付けられたランタイムメタデータが依然として必要です。具体的には、子のタスク/セッションID、権威ある開始/終了状態と結果、タイムスタンプ、子のツールイベント、または認可された会話ログへのリンクです。現在の親ツール通知にはこれらのフィールドは含まれていません。ZeroCodeは、結果テキスト、トレースログ、プロンプトのマッチング、ターゲットのエイリアスからこれらを推測することはありません。ランタイムのコントロールプレーンがライフサイクルの管理者であり続けます。これは#8763の部分的な実装であり、ライブの子プロセス監視ではありません。
権限の継承
SubAgent は、spawn サイトが絞り込み用の SubAgentOverrides を指定しない限り、親の権限をそのまま継承します。現在、ツリー内の両方の spawn サイトは SubAgentOverrides::default()(すべてを継承)を渡しています。オーバーライドの仕組みは出荷済みで検証も完了しており、将来的に呼び出し元が指定する絞り込みパスは、ランタイムの変更なしで組み込めます。
軸ごとの継承:
SecurityPolicy:Arc<SecurityPolicy>のクローンによって継承されます。オーバーライドパス(SubAgentOverrides::policy = Some(policy))はSecurityPolicy::ensure_no_escalation_beyond(crates/zeroclaw-config/src/policy.rs)を実行し、親が持たない権限を追加するフィールドをすべて拒否します。検証される項目には、自律レベル、allowed_roots(rw + ro + write-only)、allowed_commands、workspace_only、親 ⊆ 子 方向での forbidden_paths、shell_env_passthrough、max_actions_per_hour、max_cost_per_day_cents、shell_timeout_secs、block_high_risk_commands、require_approval_for_medium_riskが含まれます。拒否時には正確なEscalationViolationが連鎖されるため、診断情報で問題のあるフィールドが特定されます。- アクション/コストの予算:
PerSenderTrackerは親子間でArcクローンによって共有されます。継承(そのまま)パス: 子は同じArc<SecurityPolicy>を保持するため、record_action()/record_cost()への書き込みは同じバケットに反映されます。オーバーライドパス:SubAgentSpawn::buildは親のtrackerフィールドを明示的に絞り込んだ子ポリシーにコピーします。SubAgent はスポーンによってmax_actions_per_hourやmax_cost_per_day_centsを回避することはできません。制限は共有されます。 - ツールレジストリ: 子のレジストリは、継承されたポリシーの下で
tools::all_tools_with_runtimeによって新規に構築されます。レジストリはその後apply_policy_tool_filter(crates/zeroclaw-runtime/src/agent/loop_.rs)を通過し、いずれかのゲートで名前が拒否されたツールはすべて除外されます:- ポリシーの
allowed_tools/excluded_tools(親のrisk_profileから取得)。 agent::runに呼び出し側から渡されるallowed_tools引数。spawn_subagentはレジストリに存在しますが、子に対してはis_subagent_callerフラグがtrueに設定されているため、いかなる spawn 処理が行われる前に深度1の拒否が発動します。同じis_subagent_callerフラグにより、子のレジストリからはmodel_switchが完全に除外されます。つまり、SubAgent は親のモデルをそのまま継承し(軸5を参照)、親の知らないうちにアクティブなモデルを切り替えられてはならないため、このツールはそもそも子には提供されません。
- ポリシーの
- メモリ許可リスト: 親の兄弟エージェントへの付与分(
workspace.read_memory_fromによる)と、親自身のエイリアスを含みます。カテゴリ制限は厳密に維持され、SubAgent によって広げられることはありません。親自身のエイリアスは常に再追加されるため、SubAgent は常に親の行を参照できます。現在のSubAgentOverrides::allowed_agent_aliasesオーバーライドは名前の部分集合として検証されるため、参照可能な付与済みの兄弟エージェントを絞り込むことしかできません。 - モデルプロバイダー: 親の
[agents.<alias>] model_provider解決から継承されます。Temperature は親のプロバイダーエントリ(config.model_provider_for_agent(parent_alias).and_then(|e| e.temperature))から取得されます。この継承は単なるデフォルトではなく強制されます。model_switchは SubAgent のツールレジストリから除外されているため(axis 3 を参照)、SubAgent は自身のモデルを切り替えることができません。サブタスクを別のモデルで実行するには、そのモデルをmodel_providerに指定した兄弟エージェントへdelegateを使用してください。 - データレイヤーでの同一性:
agentsテーブル内の同じ UUID(SQL バックエンドの場合)、Markdown 用の同じワークスペースディレクトリ、同じシークレットストア。親と子の区別は純粋に可観測性のためのものです。つまり、別個のトレーシングスパンと、別個の会話履歴セッションキーです。
ユーザーが発火させる方法
これらのツールを自分で呼び出すことはありません。ボットがそのターン内で呼び出します。ユーザーとしては、リクエストの言い回しによってボットの選択に影響を与えます。特別なコマンドも、スラッシュ構文も、ユーザーが入力する JSON もありません。モデルが spawn_subagent を選ぶか delegate を選ぶかは、システムプロンプト、ツールの description テキスト(モデルから見えるもの)、そしてユーザーの言い回しによって決まります。言い回しは影響を与えるものであり、強制するものではありません。
決定論的に制御できるのは利用可能性です。親エージェントのレジストリに存在しないツールは選択されません。リスクプロファイルによる制限は[risk_profiles.<alias>].allowed_tools、[risk_profiles.<alias>].deny_all_tools、[risk_profiles.<alias>].excluded_toolsで行います。空でないallowed_toolsリストでは、モデルにそのツールを見せるためにspawn_subagentまたはdelegateを含める必要があります。allowed_toolsが省略されているか空の場合、excluded_toolsでツールが指定されていない限り、ツールの利用可能性は制限されません。deny_all_tools = trueは明示的な全拒否のゲートであり、両方のツールを非表示にします(空のallowed_toolsリストは_全拒否_ではありません)。設定を編集した後は、デーモンを再起動してください。
検証可能なエンドツーエンドの範囲:
- プロトコル所有のツール出力および拒否文字列は、リテラルな Rust コントラクトです。ユーザーに表示されるターミナル補完の失敗通知は Fluent カタログのコントラクトです。英語のソースは以下に記載されており、同じキーを定義する英語以外のカタログまたはディスク上のオーバーライドによって、表示が異なる場合があります。
- 動作を変更する実際の設定項目(
allowed_tools、max_delegation_depthなど)。 - 子の実行中に出力されるすべてをスコープする、構造化されたトレーシングスパンの形状です。
これらのドキュメントから検証できないこと:
- 特定のボットが、特定のモデルで、特定のシステムプロンプトのもとで、「サブエージェントをスポーンして……」と求められたときに、そのツールを選択するかどうか。言い回しが結果を左右し、その出力はさまざまです。ボットがツールを選択しない場合、最も信頼できる手段は、ボットのシステムプロンプトを明示的な指示で拡張することです(「集中的なサブタスクを求められたときは、
spawn_subagentツールを使用してください」)。 - ボットが最終的な返信であなたに書き込む正確なテキスト。ボットはツールの出力を読み取り、それを基に独自の返信を生成します。ツールの出力テキストは、引用、言い換え、または要約される場合があります。
spawn_subagent: モデルが認識する拒否文字列
これらは crates/zeroclaw-runtime/src/tools/spawn_subagent.rs から取得した正確なものです。モデルはこれらをツールのエラー文字列として受け取り、それに反応します。ユーザーに表示されるボットの返信は、モデルが次に書く内容であり、多くの場合その拒否を参照またはそのまま反映します。
- 空または欠落した
prompt引数:Missing or empty 'prompt' parameter - 呼び出し元自体が SubAgent の場合 (深さ 1 の上限):
spawn_subagent: a subagent may not spawn its own subagents (depth-1 cap) - 親の risk-profile ツールゲートは
spawn_subagentを除外しています:spawn_subagent: refused: agent '<parent_alias>' risk_profile does not list spawn_subagent in allowed_tools - 不明な親エイリアス / spawn ビルドエラー:
subagent spawn failed: <wrapped error> - 子の実行がエラーを返しました:
subagent run failed: <wrapped error>
成功した場合、ツールの出力は子の最終的なレスポンステキストそのものになります。子が空の文字列を返した場合、出力はリテラルのプレースホルダー subagent completed without output になります。成功時に grep で検索できる固定のプレフィックスはありません。
spawn_subagent: 実際に起動したかを確認する方法
ログをtailしてください。ツールが生成した子プロセスは、zeroclaw_scope という名前のtracingスパン(targetは zeroclaw_log_internal_scope)を発行する scope! 内で実行され、agent_alias=<parent> と session_key=<uuid> を保持します。子プロセスの実行中に発行されるすべてのログ行は、これらのフィールドを保持します。親自身のターンには独自の session_key があります。同じ agent_alias に対してターンの途中で新しい session_key 値が現れることが、SubAgentが実行されたことを示すシグナルです。子プロセスの会話履歴セッションパスは subagent-<uuid> です(tracingフィールドとは異なる、ファイルシステム的な識別子)。
Cron で起動されたエージェントジョブは、別のより明示的なスパン名 subagent(リテラル)を使用し、フィールド category="cron"、agent_alias=<owning agent>、cron_job_id=<id>、run_id=<uuid>、spawn_site="cron" を持ちます。Cron のパスは簡単に grep できます: grep 'spawn_site="cron"' zeroclaw.log。Cron で起動された実行はトップレベル(is_subagent=false)であることに注意してください。これらは自身で spawn_subagent を一度だけ呼び出す場合があります。
これは、エージェントループの spawn パスに対する弱いシグナルです。attribution_span!(tool) を経由してルーティングされる専用の「サブエージェント開始/完了」レコードは、コード側のフォローアップとして追跡されます。エージェントループがツール実行を attribution span でラップすると、そのツール内のすべての record! は自動的に tool=spawn_subagent を持つようになり、この問題は単純な grep で解決できるようになります。
委任ゲーティング
delegate は、ターゲットエージェントが実行される前に、crates/zeroclaw-runtime/src/tools/delegate.rs で次の順序で2つのゲートを適用します:
-
delegation_policy.mode: 呼び出し元のリスクプロファイルが委任を許可している必要があります。[risk_profiles.<alias>].delegation_policyはデフォルトで{ mode = "forbidden" }です。委任を許可するにはmode = "allow"を設定してください。禁止されている場合の拒否は次のとおりです:呼び出し元「<caller>」は、リスクプロファイル「<caller_profile>」のdelegation_policyによって委任が禁止されています。[risk_profiles.<caller_profile>].delegation_policyのmode = "allow"を設定してくださいこれはゲートウェイダッシュボードおよびzerocodeの Config → Risk profiles →
<profile>→delegation_policy.mode(forbidden/allowの選択)で編集できます。 -
到達可能性: ターゲットエージェントは、
Config::reachable_delegate_target_configsによって解決される呼び出し元の到達可能セットに含まれている必要があります。到達可能セットは、[agents.<caller>]上のエージェントごとの2つのソースの和集合から、呼び出し元自身を除いたものです:-
同一プロファイルのピア: 呼び出し元のリスクプロファイルを共有する他のすべてのエージェント。
delegate_same_risk_profile = true(デフォルト)の場合に含まれます。falseに設定すると、呼び出し元はピアの自動許可から除外されます。 -
明示的ロスター:
delegates、呼び出し元がリスクプロファイルをまたいで委譲できるターゲットの、空でもよいリスト。文字列エントリは手動編集に便利で、境界付きターゲットを表します。オブジェクトエントリはモードを明示します:delegates = [ "reviewer", { agent = "sysadmin", mode = "independent" }, ]設定が保存されると、各エントリは
mode = "bounded"またはmode = "independent"を含むオブジェクト形式で書き込まれます。この設定形式は、デーモンおよび UI バイナリが委任モードをサポートするビルドにアップグレードされるまで展開しないでください。古いバイナリはdelegatesが文字列のみを含むことを想定しており、オブジェクトエントリがあるとそのバイナリにとってagentsセクションが無効になります。その場合、耐障害性ローダーがセクションを破棄するため、修復用インターフェースは引き続き起動できます。 ターゲットがそのセット外にある場合、拒否は原因を示します。例:
delegate ターゲット "<target>" は "<caller>" から到達できません: リスクプロファイルが異なります (caller は "<caller_profile>" を使用、target は "<target_profile>" を使用)。delegate_same_risk_profile は同じリスクプロファイルを持つエージェントにのみ到達します。意図したモードで明示的な [agents.<caller>].delegates エントリを追加するか、いずれかのエージェントの risk_profile を変更してください。委譲先ターゲット "<target>" は "<caller>" から到達できません: delegate_same_risk_profile が無効であり、ターゲットが [agents.<caller>].delegates に記載されていませんデリゲート先の "<target>" は "<caller>" からアクセスできません: ターゲットエージェントが無効になっています境界付きのターゲットは、呼び出し元のアクション/コストトラッカーを継承し、有効な上限は各ホップで呼び出し元の値に制限されます。そのため、ターゲット自身のプロファイルで許可されている内容にかかわらず、呼び出し元のアクション予算がサブツリー全体を制約します。生成されたバックグラウンドワーカーと並列ワーカーは、最初の受付の前に送信元のレート制限バケットを復元するため、生成境界をまたいでも予算は送信元ごとに保たれ、スコープなしのバケットにフォールバックしません。コスト上限も各ホップで同じ方法で結合され、委任されたサブループ内で、ターゲットエージェント自身の日次支出に対して適用されます(下記のコスト追跡の段落を参照)。境界付きのターゲットが呼び出し元と同じリスクプロファイルを共有する場合、呼び出し元のセッションのワークスペース境界も継承します。クロスプロファイルの境界付きターゲットは、呼び出し元の委任先一覧と
delegation_policyを通じて到達可能であれば許可され、ターゲットの解決済みポリシーの下で実行されます。ただし、エージェント的なツールの利用可否は呼び出し元のツールレジストリによって制限されます。独立ターゲットは、
mode = "independent"で明示的にリストされている場合にのみ利用できます。依然としてdelegation_policy.mode = "allow"とdelegatesを通じた到達可能性が必要ですが、一度選択されると、呼び出し元の非エスカレーション上限、セッションワークスペースのオーバーライド、またはアクション/コストトラッカーなしで、ターゲットエージェント自身のポリシーを解決します。削除されたサブループはすべて、有界・独立のいずれであってもコスト追跡の対象になります。委譲されたサブループは、ターゲットのエージェントエイリアスに帰属するコスト追跡スコープの下で実行され、そのトークン使用量は共有の日次コスト台帳にそのエイリアスの下で記録されます。ターゲットのホップごとの実効上限は
[runtime_profiles.<target>].max_cost_per_day_centsから求められ(値が0の場合はグローバル上限を継承)、台帳上のターゲットエージェント自身の日次支出と比較されます。一方、プロセスグローバルの[cost] daily_limit_usdと月次上限は共有合計に対して引き続き適用されます。両者のうち低い方が優先されます。スコープの種類(
[cost] track_per_agentによるエージェント単位または共有上限付き)、委譲実行がスコープされるかどうか([cost] enabled)、およびその台帳行のエージェント単位の帰属は、委譲ツリーのルート委譲時に一度だけ決定され、以降のすべてのネストされたホップに継承されます。ネストされた委譲は委譲元の親からトラッカーを派生させるため、ツリーが開始されたモードがツリー全体を拘束します。実行中にこれらのフラグをリロードしても、スコープされた実行の追跡を解除したり、帰属されていない実行の帰属を途中から開始したりすることはできません。上限と価格マップは、すべてのホップで常に最新の状態が維持されます。派生トラッカーはベースのconfigハンドルを共有し、価格マップは解決済みのconfigから再構築されるため、オペレーターがグローバルの
[cost]上限をリロードすると、実行中の委譲は次回の予算チェック時にそれが反映されます。ターゲット自身の支出がその上限を超えている場合、委譲はターゲットのプロバイダーが呼び出される前に失敗し、その拒否はエージェント名を含むdelegateツールのエラーとして呼び出し元に返されます。祖先の上限は、その祖先が委譲したすべてのものにも及びます。有界なホップの予算スコープはすべて祖先チェーンを保持するため、有界な子孫の支出は、その子孫の台帳行が子孫自身のエイリアスに帰属したままであっても、それぞれの祖先のホップごとの上限に計上されます。独立したターゲットは、代わりに自身のホップごとの上限と共有のグローバル上限の下で実行されます。ツリーの実行モードは継承しますが、呼び出し元の上限やチェーンは継承しないため、呼び出し元の上限超過や子孫の累積値が独立ホップを制限することはありません。
子孫の会計は、プロセスの稼働期間およびUTCの日単位で管理されます。台帳の日次合計とともにUTC深夜にロールオーバーし、デーモンを再起動すると子孫の支出は忘れられますが、各エージェント自身の台帳合計は保持されます。
[cost] track_per_agent = falseの場合、エージェント単位の日次合計は存在しないため、ホップごとの上限は共有の日次合計に対するチェックに縮退します。非エージェント型の委譲ターゲット(単一のモデル呼び出しでツールループを持たないもの)も同じスコープの下で実行されますが、そのディスパッチ経路はトークン使用量を報告しないため、現状では、これらの単一呼び出しは価格計算も予算チェックも行われません。
-
広告されるロースターは、ツールスキーマ内の agent パラメータの説明に含まれます。これは到達可能な集合を正確に列挙し、delegation_policy.mode = "allow" の場合にのみ表示されます。無効化されたエージェント(enabled = false)は、同一プロファイルのピアであっても、明示的な delegates エントリであっても、到達可能になることはありません。
境界付きエージェント委譲では、サブエージェントのツールは、呼び出し元の既にポリシーでフィルタリングされたレジストリから取得され、それがターゲット自身の allowed_tools と交差されます。ターゲットの allowed_tools が省略されているか空の場合は「継承」を意味します。サブエージェントは拒否されるのではなく、呼び出し元の委譲可能なレジストリ全体を使って実行されます(空のリストは旧来の無制限状態であり、全拒否ではありません)。deny_all_tools = true は明示的な全拒否ゲートであり、空の子レジストリになります。空でないリストは、そのレジストリと交差されます。いずれの場合も、呼び出し元のレジストリが上限となります。ターゲットのリスク・プロファイルが、呼び出し元に付与されていないツールを指定している境界付きのクロスプロファイル・ターゲットは、そのツールを受け取りません。
したがって、境界付き委譲はツールによる境界付けであり、完全な SecurityPolicy::ensure_no_escalation_beyond チェックではありません。その交差が空の場合でも、ターゲットは通常のエージェント型モデルのターンを、ツールなしで受け取ります。
delegate ツール自体も同じ範囲制限の形を踏襲します。ターゲットのリスクプロファイルで delegation_policy mode = "allow" が設定されていない限り、子レジストリから除外されます(デフォルトの "forbidden" では、現在のサブ委任なしの動作が維持されます)。また、呼び出し側のゲートも引き続き適用されます。呼び出し元の allowed_tools/excluded_tools で delegate が許可されている必要があり、ターゲットのツールポリシーで delegate が除外されていてはなりません。許可された場合、子の delegate インスタンスはターゲットのアイデンティティにバインドされます。その呼び出しは、delegation_policy、到達可能性、および公開されるロスターを、委任元の親ではなく、ターゲット自身のエイリアスおよび delegates 設定から解決します。さらに、許可された委任のホップごとに深度カウンターが増加します(下記参照)。
付与されたインスタンスには、承認とスコープに関する2つのルールが適用されます。1つ目として、このインスタンスはdelegate-onlyです。子インスタンスのIDは一時的なものであるため、タスク管理アクション(check_result、list_results、cancel_task、await_sessions)は拒否されます。タスク行には呼び出し元エイリアスの委任チェーンが記録されるため、子のすべての祖先はオーナーチェックを通過します。ただし、自身の delegate ツールがタスク管理を備えている祖先(通常はルートの委任エージェントです。境界付きの中間子もdelegate-onlyであるためです)のみがその許可に基づいて動作できます。そのため、子はタスクIDを、いずれかの該当祖先に到達するまで上位へ渡します。この境界を正確に理解してください。これは delegate API の範囲のみを限定します。基盤となる delegate_results/*.json アーティファクトは、タスクIDと出力のみを保持する通常のワークスペースファイルのままであり、オーナーチェックで制御される対象はタスク行に存在します。2つ目として、サブエージェントのループにはオペレーターの承認経路がありません。対象のリスクプロファイルが delegate について確認を求める場合(監視下のデフォルト、または always_ask に記載されたツールの場合)、子の承認ゲートは delegate 実装が実行される前に呼び出しを拒否し、オペレーターの判断が利用できなかったことを報告します。delegate 実装は、到達した呼び出しに対して独自のターゲットポリシーチェックを保持します。したがって、入れ子の委任には、ターゲットプロファイルが delegate を明示的に自動承認するか、完全自律モードで動作することが必要です。確認を求める呼び出し元プロファイルは、そのループ内で変わらず確認を求め続けます。
同じオペレーター不在の境界は、有界なエージェント型の子プロセスにおけるツール呼び出しにも適用されます。子ループは、ディスパッチの接合点で、ターゲットのリスクプロファイルに基づいて新しい非対話型の承認マネージャーを作成します。承認プロンプトが必要な呼び出しは、ツール実装が実行される前に拒否されます。一方、auto_approve に明示的に記載された呼び出し(または完全自律モードで許可された呼び出し)は続行できます。呼び出し元が所有し、継承されたコマンドツール(shell、schedule、cron_add、cron_update、cron_run)については、このターゲットプロファイルによる判断はツールの呼び出しのみを承認します。呼び出し元のコマンドポリシーが消費するコマンド固有の承認を提供したり、上書きしたりするものではありません。呼び出し元のコマンドポリシーが引き続き優先されるため、これらのツールは同ポリシーの承認要件を引き続き適用します。継承された呼び出し元による上限付きのレジストリは変更されません。承認は、機能フィルタリングを通過した呼び出しを評価するものであり、追加のツールを付与したり、呼び出し元の承認セッションを転送したりするものではありません。
独立したエージェント的委任では、サブエージェントのツールは、対象エージェント自身の設定済みポリシーとランタイムレジストリから構築されます。これは、その対象との新しいチャットを開くのに似ています。親のレジストリは上限として使用されません。delegate ツールはその子レジストリから削除されます。独立した委任は、別の delegate 呼び出しを経由して再帰することはありません。
委譲の深さは、委譲元の親エージェントの runtime_profile.max_delegation_depth によって上限が定められます。許可された各委譲ホップは、その上限を次の委譲先エージェント自身のプロファイル上限と比較して小さい方(min)に絞り込んだ値を持ちます。これにより、親の上限はそのサブツリー全体に適用され、連鎖では上限が厳しくなることはあっても緩むことはありません。上位エージェントに委譲を1ホップだけ許可し、それ以上のサブ委譲を行わせない場合は、1 に設定します。
エージェント型ターゲットツールポリシー
ターゲットエージェントの [runtime_profiles.<target>].agentic = true の場合、delegate は親の利用可能なツール(mode = "bounded")またはターゲット自身のランタイムレジストリ(mode = "independent")のいずれかからターゲットサブループのツールレジストリを構築します。ターゲットのリスクプロファイルがそのレジストリをフィルタリングします:
[risk_profiles.<target_profile>].allowed_toolsフィールドが省略されているか空の場合、選択されたレジストリは制限されないままになります。deny_all_tools = trueは明示的な全拒否ゲートであり、空の子レジストリを生成します。allowed_toolsリストに値が指定されている場合、完全に一致するツール名のみが保持されます。[risk_profiles.<target_profile>].excluded_toolsは常に結果から差し引かれます。delegateは、境界付きサブ委任が許可されていない限り、子レジストリから削除されます。delegation_policy mode = "allow"を設定したリスク・プロファイルを持つ境界付きターゲット(かつ呼び出し側のゲートがそのツールを許可しているもの)は、ターゲットに紐付けられた delegate 専用インスタンスを受け取ります。このインスタンスは、ターゲットのプロファイルが委任について確認を求める場合には、さらに委任を拒否します。独立した子レジストリは、常にdelegateを削除します。
このポリシーは呼び出し元ではなくターゲット側に存在します。同一プロファイルのピアは共有リスクプロファイルを使用します。明示的なクロスプロファイルの委任は、到達可能性と委任ポリシーのゲートを通過した後、ターゲットのリスクプロファイルを使用します。境界付きエージェント委任は、呼び出し元で上限が設定されたツールレジストリとターゲットのツールポリシーの積集合のみを受け取ります。独立したエージェント委任は、ターゲットが所有するツールレジストリを受け取ります。ターゲットのリスクプロファイルが欠落している場合は、サブループが開始する前に拒否されます。実行可能な子ツールがゼロになるように設定されたプロファイルであっても、ツールなしの通常のモデルターンは許可されます。
対象に設定された Reliable プロバイダーチェーンで、ネイティブツール対応の候補とテキストのみの候補が混在している場合、strict_tool_parsing = false では、到達可能なすべてのフォールバックでツールを実行できるよう、エージェントターン全体で 1 つのテキスト/XML ツールプロトコルを使用します。有効なツールが残っていて strict_tool_parsing = true の場合、厳格なパースではそのテキスト/XML フォールバックプロトコルが禁止されるため、ZeroClaw はプロバイダーへのリクエストを行う前に混在チェーンを拒否します。均一なチェーンは変更されません。すべてネイティブのチェーンではネイティブツールトランスポートを使用し、意図的にすべてテキストにしたチェーンでは設定されたテキストツールポリシーに従います。
独立したデリゲート承認
独立型のエージェント対象は、自身のリスクプロファイルの approval_route によって選択されたオペレーターに対して、ツール承認リクエストを送信できます。approver_channel と空白以外の approver_recipient の両方を設定し、そのチャネルを有効にして、実行中のデーモンに登録された状態を維持してください。呼び出し元は、独立モードにおいてその指定された対象をすでに承認している必要があります。承認ルートは、委任の到達範囲や追加のツールを付与するものではありません。
For example, a target using the worker risk profile can route its approvals to a configured operations channel:
Wait, I need to correct that. The source is English and must be translated to Japanese, so here is the translation:
例えば、worker リスクプロファイルを使用するターゲットは、設定済みの運用チャンネルに承認を振り分けることができます:
[risk_profiles.worker.approval_route]
approver_channel = "telegram.ops"
approver_recipient = "operations-room"
on_no_approver = "deny"
timeout_secs = 120
フォアグラウンド、バックグラウンド、および並列の独立したデリゲートは、すべて同じターゲットルート承認ゲートを使用します。プロンプトには、ターゲットと呼び出し元が表示され、実際のツール名と引数の概要が保持され、設定された受信者が使用されます。実際の承認があると、ターゲットが所有する呼び出しは、既存のツールおよびコマンドポリシーに従って許可されます。拒否、チャネルの欠落、未対応の承認サーフェス、切断、またはタイムアウトの場合、ツールは実行されないままになります。子の実行タイムアウトは承認待機の上限にもなり、ルートのタイムアウトより前に期限切れになる場合があります。
対象のチャンネルターン用にプロファイルが既に設定されている場合でも、完全なターゲットルートは適用されます。Supervised の独立した子プロセスでは、対象が明示的に shell を自動承認していない限り、シェル呼び出しには現在オペレーターの判断が必要です。これには、以前はシェルツールのコマンドポリシーに直接渡されていた低リスクのコマンドも含まれます。チャンネルレジストリのない処理(例:1回限りの CLI 実行)では、判断が必要な呼び出しは拒否されます。完全自律と明示的な auto_approve エントリは既存の動作を維持し、always_ask は引き続き判断を必要とします。対象のポリシーに合致する場合にのみ、このルートを削除して以前の非対話型シェル経路を復元してください。
Each child run has a fresh approval manager. An “Always” answer applies only within that child run; it cannot approve the caller’s calls, later delegate runs, or parallel siblings. Tools in always_ask still require a decision for every call, including under Full autonomy. Explicitly auto-approved tools otherwise retain their target-profile behavior.
Note: the source text is already in English, but the task is to translate it to Japanese. Here is the translation:
子の実行ごとに新しい承認マネージャーが作成されます。「Always」の回答は、その子の実行内でのみ適用されます。呼び出し元の呼び出し、後続の委任実行、または並列の兄弟実行を承認することはできません。always_ask に含まれるツールは、Full autonomy の下でも、呼び出しごとに必ず判断を求められます。明示的に自動承認されたツールは、それ以外の場合、ターゲットプロファイルの動作を維持します。
委譲先は、呼び出し元の CLI、ACP、RPC、ダッシュボードの承認セッションを引き継ぎません。on_no_approver = "inherit-originator" を設定していても、設定された承認者が応答できない場合は拒否されます。独立した子プロセスには、フォールバック先となる元の承認チャネルが存在しないためです。保留中の承認はプロセスローカルであり、子のキャンセルまたはタイムアウト時に破棄されます。デーモンを再起動しても、その承認要求は再開されず、子の承認許可も復元されません。
Ń×ö„Éľ„ā≤„ÉɄɹ„ĀģŚģĆŚÖ®„Ā™ŚģõŚÖą„Éę„Éľ„Éą„Āę„ĀĄŚ†īŚźą„ÄĀalways_ask „ĀĆÁ©ļ„Ā߄™„ĀĄÁč¨Áęč„Āó„Āü„Éľ„ā≤„ÉɄɹ„ĀĮ„ÄĀ„Éź„ÉÉ„āĮ„āį„É©„ā¶„É≥„ÉČ„āĄšł¶Ť°Ćšĺ°ŚÄ§„ĀģŚŹóšĽė„ā팟ę„āÄŚģüŤ°ĆŚČć„Āęśč팟¶„Āē„āĆ„Āĺ„Āô„ÄāŚźĄ„Āē„āĆ„Āü„Éó„É≠„É≥„Éó„Éą„ĀĆŚŅÖŤ¶Ā„Ā™„Āģ„ĀĮ„ÄĀ„ÉĄ„Éľ„Éę„Āģ„Éá„⣄āĻ„ÉĎ„ÉÉ„ÉĀ„Ā߄™„Āä„Éē„ā߄⧄Éę„āĮ„É≠„Éľ„āļ„Āē„āĆ„Āĺ„Āô„ÄāśúČŚäτ™ŚĀīÁęĮŚÖ•Śäõ„Āģ„Ā™„ĀĄŚĹĻŚČ≤ŚąÜśčÖ„ā®„Éľ„āł„āß„É≥„Éą„ĀĮ„ÄĀśóĘŚ≠ė„Āģ„Ā≤„Ā®„ĀĄ„♄Éö„ɨ„Éľ„āŅ„Éľ„Āę„ĀĄŚčēšĹú„Ā®„ÄĀŚĎľ„Ā≥Śáļ„ĀóŚÖÉ„ĀĆśČÄśúČ„Āô„āč„ā≥„Éě„É≥„ÉČ„É̄ə„ā∑„Éľ„āíÁ∂≠śĆĀ„Āó„Āĺ„Āô„Äā
delegate: モデルが参照する出力文字列
ユーザーに表示される失敗文字列は、ローカライズされた Fluent メッセージです。英語版の正本は crates/zeroclaw-runtime/locales/en/cli.ftl です。以下の例は現在の英語カタログ値を示すものであり、ワイヤーレベルの文字列コントラクトではありません。このセクションで Fluent キーとしてラベル付けされていない限り、残りの列挙された文字列はプロトコル/ツールの出力です。
-
同期実行の成功: 出力は
[Agent '<target>' (<provider_type>/<model>)]\nで始まり、その後に空でないターゲットエージェントの応答が続きます。ターゲットが設定済みのプロバイダーフォールバックによって復旧した場合、そのヘッダーには要求されたプロバイダー/モデルと、実際に提供されたプロバイダー/モデルが代わりに示されます。例:[Agent 'reviewer' (requested: anthropic.primary/claude; served: openai.terra/gpt-5.6-terra, agentic)]。agentic ターゲットの場合、この表示は最終応答を生成したモデルリクエストを示すものであり、ツール呼び出しだけを生成した以前のリクエストを示すものではありません。結果はローカライズされたdelegate-provider-fallback-warningでも終了します。英語では:Warning: The delegated agent recovered through a provider fallback. Provider failure details were logged and omitted from this result.この表示と警告は委譲された結果に属するものであり、呼び出し元エージェントのフォールバックとして提示してはなりません。これらには、拒否されたプロバイダーのエラー詳細、エンドポイント、認証情報は意図的に含まれていません。同じ設定済み候補を再試行してもこの警告は発生しませんが、後の設定済み候補に到達すると発生します。後の候補のプロバイダーとモデルのラベルが最初の候補と一致する場合も同様です。 -
終端の空のレスポンスは同期的な失敗です。エラーフィールドでは
cli-delegate-error-invalid-semantic-completionを使用し、agent_nameにはターゲットを設定します。英語では:Agent '<target>' failed: model provider returned an invalid semantic completion. -
その他の同期失敗: error フィールドは
Agent '<target>' failed: <wrapped error>で始まります。構成されたすべてのプロバイダー候補が失敗した場合、<wrapped error>は、失敗イベント、再試行回数、失敗クラス、フェーズ、および固定の対処ヒントを Reliable が順序付けして安全に要約したものです。プロバイダーのレスポンスボディ、エンドポイント、エイリアス、モデル、認証情報は呼び出し元のエージェントには返されません。さらに詳細が必要な場合は、インストール環境の通常のオペレーター向けログポリシーに従って、プロバイダー試行ログを調査してください。結果は引き続きエラーであり、リカバリ警告ではありません。 -
同期タイムアウト(ターゲットのランタイムプロファイルが
delegation_timeout_secsを設定している場合):error フィールドはAgent '<target>' timed out after <N>sになります。 -
バックグラウンド生成成功: 出力は3行のリテラルです
Background task started for agent '<target>'. task_id: <uuid> Use action='check_result' with task_id='<uuid>' to retrieve the result.ライフサイクルの状態は
data/control_plane.dbに保存されます。<workspace>/delegate_results/<uuid>.jsonにあるワークスペースの成果物には、タスクの識別情報と出力のみが含まれ、ステータスの記録を二重に持つものではありません。タスクが開始されると、check_resultビュー(およびawait_sessions結果の各エントリ)はprogressオブジェクトを保持します。一度も記録されなかった行の場合はnullになります。フィールドは次のとおりです:last_activity_at、iterations(発行されたモデルリクエスト数)、tools_completed、last_tool(name、started_at、finished_at、successを持ち、実行中のもっとも新しい呼び出し(finished_atが null のもの)を指します。なければ完了した最後の呼び出し)、timeout_budget_secs(エージェント型のタイムアウト。ツールループを持たないターゲットの場合は単一呼び出しのタイムアウト)、recent_tools(完了した直近5件の呼び出し)、receipt_tail(これまでに収集された直近5件のレシート。<tool>: <token>の形式で、ツールレシートが無効の場合は空)、そしてelapsed_secs(読み取り時に計算されます)。レシートはツールのバッチが返されたときに収集されるため、呼び出しはそのレシートが現れる前にカウントされることがあります。単一呼び出しのターゲットでは、その1回のリクエストがiterationsにカウントされます。ツール名は、レシートの末尾を含め、現れるすべての箇所で64文字に切り詰められます。署名付きトークンは切り詰めずにそのまま保持されます。ツールの引数、ツールの結果、エラーテキスト、プロンプトの内容は記録されません。進捗情報はタスクの行に保存されるため、そのタスクの出力を読み取れる呼び出し元にのみ正確に見えます。タスクの実行中は、その行のハートビートが少なくとも1分に1回更新されます。 -
action="check_result"で不明なタスク ID を指定した場合: エラーはNo result found for task_id '<uuid>'となります。 -
task_ids: [<uuid>, ...]を指定したaction="await_sessions"は、複数のバックグラウンド結果ファイルを一度に待機します。出力はstatus(completeまたはtimeout)、completed、pending、missing、failed、resultsを含む JSON オブジェクトです。timeout_msはデフォルトで 30000 で、上限は 120000 です。タイムアウト時には、ツールが部分的な結果と、1 つ以上のタスクがまだ保留中または見つからないことを示すエラーを返します。重複するタスク ID は拒否されます。 -
並列ファンアウト出力は
[Parallel delegation: <N> agents]\n\nで始まり、\n\nで区切られたエージェントごとのブロックが続きます。各ブロックは--- <target> (success=<bool>) ---\nで始まります。復旧したターゲットでは、要求先と提供先の帰属情報および汎用フォールバック警告が自身のブロック内に保持されます。エージェントごとの失敗時、内部ブロックは--- <target> (success=false) ---\nError: <wrapped error>です。 -
不明なターゲットエージェント: エラーは
Unknown agent '<target>'. Available agents: <comma-separated list>です。 -
深さの上限を超過しました(親の
runtime_profile.max_delegation_depthで制御され、デフォルトは 3):エラーはDelegation depth limit reached (<depth>/<max>).です。 -
不明なアクション: エラーは
Unknown action '<value>'. Use delegate/check_result/list_results/cancel_task/await_sessions.です。 -
always_askエントリを持つリスクプロファイルのターゲットで、完全な明示的承認ルートがない独立ターゲットの場合、次のエラーを返します:delegate target "<target>" cannot run in independent mode from "<caller>": risk profile "<profile>" has always_ask entries (<list>). See ZeroClaw docs, "Delegation & SubAgents" > "What's not supported".境界付きエージェント型ターゲットの場合も同じエラーを返しますが、cannot run in bounded agentic modeというメッセージになります。 -
エージェント型ターゲットでリスクプロファイルが見つからない場合のエラー:
Agent '<target>' is agentic but risk_profile '<target_profile>' is not configured。 -
実行可能な子ツールがゼロの Agentic ターゲット: 空のツールセット自体に対してエラーは発行されず、ターゲットはツールなしの通常のモデルターンを受け取ります。
delegate: 実際に発火したかを確認する方法
delegate は現在、専用のトレーシングスパンを出力しません。シグナルとなるのは、対象エージェントのループがログに現れることです。このループは、親のツール呼び出しディスパッチが実行されていたスコープを引き継ぎます。バックグラウンドモードでは、check_result または await_sessions を使用して、権威あるライフサイクル状態と出力を併せて読み取ってください。これらのアクション、一覧表示、およびキャンセルは、記録されたチェーンに呼び出し元のエイリアスが含まれる delegate の行に限定されます。ワークスペースのアーティファクトは出力が永続化されたことを確認できますが、ステータスは保持していません。ツールレシートが有効な場合、バックグラウンドの委任は、親のジェネレーターを共有し独自のコレクターを使用するレシートスコープで実行されます。そのため、ツール結果は署名され検証可能ですが、起動元のターンのレシートブロックには追加されません。
(Cron で起動されたエージェントジョブは別の生成サイトであり、上記で説明した明示的な subagent スパンを使用します。delegate と cron は同じパスではありません。)
delegate: ターンを停止すると取り消されるもの
Ch ターン中のdelegateを呼び出したターンを停止すると(例: POST /api/sessions/{id}/abort、またはチャネルによる割り込み)、子タスクは独自のタスクで実行されており、親の破棄されたツールFutureより長く生き続けるにもかかわらず、そのターンが開始した作業にまで停止が及びます。
spawn_subagent にはこれらの機能がありません。バックグラウンド実行や並列実行のモードはなく、子エージェントは親の future と一緒にキャンセルされます。
このページに含まれていないもの(意図的に)
- 会話記録の例。ここで「ボットが何と言うか」について記述した内容は、モデルに依存します。ボットの応答は、ツールの出力、モデル、システムプロンプト、および現在の会話状態の下流にあり、これらはいずれもこのページでは制御できません。検証可能なレイヤーは、ツールが返すもの(上記)とログが記録するものです。
- 専用の「subagent fired」/「delegate fired」ログマーカー。コード側のフォローアップとして追跡されます。現在、オペレーターは上記で説明したスコープの形状(既存の構造的シグナル)と、バックグラウンドモードの結果ファイルを通じて検証します。
spawn_subagent と delegate の使い分け
spawn_subagent | delegate | |
|---|---|---|
| 識別情報 | 親と同じ(同じUUID、同じリスクプロファイル) | ターゲットエージェントのアイデンティティ(別のエイリアス。同一プロファイルのピア、または明示的なクロスプロファイルのデリゲート) |
| 権限モデル | 親のポリシーをそのまま使用(または絞り込んだサブセット) | 制限付きターゲットは、呼び出し元のエージェンティックツールレジストリを上限としてターゲットポリシーの下で実行され、独立ターゲットはターゲットポリシーとターゲット所有のレジストリの下で実行されます |
| モデルプロバイダー | 親要素の | ターゲットエージェントに設定されたプロバイダー |
| スポーン深度 | 1での上限制限 | 最大 runtime_profile.max_delegation_depth(デフォルト 3) |
| バックグラウンドモード | 非対応 | background: true は task_id を返します |
| 並列ファンアウト | 組み込み引数なし。parallel_tools = true の場合、1ターン内の複数呼び出しは並行して実行されます | parallel: [...] は複数のターゲットを並行して実行します |
| ゲーティング | 省略された、または空の risk_profile.allowed_tools では spawn_subagent が利用可能なままになります。deny_all_tools = true の場合は拒否されます。空でないリストには spawn_subagent を含める必要があります。excluded_tools にはこれを列挙してはなりません。 | 呼び出し元の allowed_tools が省略されているか空の場合、delegate は利用可能なままです。deny_all_tools = true の場合は delegate が拒否されます。空でないリストには delegate を含める必要があります。excluded_tools に delegate を含めてはなりません。呼び出し元の delegation_policy mode = "allow" が設定されていて、対象が呼び出し元の到達可能セット(同一プロファイルのピア、または明示的な delegates エントリ)に含まれている必要があります。制限付きの対象は、自身のリスクプロファイルで delegation_policy mode = "allow" が設定されている場合に限り、delegate ツールを受け取れます。付与されたインスタンスは delegate 専用であり、対象のプロファイルが委任時に確認を求める場合は、委任を拒否します。 |
| 使用する場面 | 同一のアイデンティティ内に留まるべき内部サブタスク | 境界付きまたは独立した委任のもとで、別の構成済みスペシャリスト(別のモデル、別のエイリアス)にタスクを担当させたい |
サポートされていないもの
- 深さ1を超える再帰。 SubAgent は自身の SubAgent を生成できません。この上限はツール側での明確な拒否であり、予算ではありません。Cron で起動された実行は深さ0から開始し、1レベルの生成が可能です。エージェントループで起動された SubAgent は深さ1にあり、それ以上の生成を拒否します。
- 子に対する別個のアイデンティティ。 SubAgent は親のエージェント UUID を共有します。別のアイデンティティで実行するには、
delegateを使用して設定済みの兄弟エージェントに引き渡します。 - 実行時間の上限。
timeout_secs引数は存在しません。親は子の実行が完了するまで全時間ブロックされます。キャンセルは、より広い中断の範囲を通じて行う必要があります(delegateについては、ターンの停止によってキャンセルされるものを参照してください)。 - 進捗を親へストリーミングで返す。 親は子の最終レスポンスを、完了後に単一の文字列として受け取ります。
[agents.<alias>].subagent_*設定ブロック。 バリデーターとオーバーライド型は本日提供されますが、呼び出し元が定義する絞り込みを配線するオペレーター向けの設定サーフェスは今回のリリースには含まれていません。両方のスポーン箇所では、そのサーフェスが実装されるまでSubAgentOverrides::default()を渡します。- 送信元セッションへの承認転送と有界承認。 独立したデリゲートは、ターゲット側が所有する明示的な承認ルートを利用できますが、呼び出し元の発信元 CLI、ACP、RPC、ダッシュボードのセッションへ承認を転送することはできません。空でない
always_askを持つ有界のエージェント型ターゲットは、バックグラウンド実行および並列委譲を含め、実行前にブロックされたままです。非エージェント型の有界委譲は子ツールを実行できないため、引き続き利用可能です。