委譲とサブエージェント
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>].allowed_toolsが空でなく、spawn_subagentを含んでいない場合、またはexcluded_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 は、その実行が完了するまで親のツール実行を停止させます。呼び出しごとのタイムアウト設定はありません。
1回のターンでの複数回の呼び出し
エージェントループは、ターン単位の重複呼び出しガードを適用します。同一ターン内で同一の引数によって2回呼び出されたツールは、通常2回目の呼び出しがスキップされます。spawn_subagent と delegate は、このガードの対象外です。同じプロンプトで複数を起動すること(冗長性、サンプリング、ファンアウト)は、偶発的な繰り返しではなく意図的なパターンであるため、同一の呼び出しがそれぞれ実行され、それぞれの結果が返されます。この対象外措置がなければ、最初の同一呼び出しのみが実行され、その出力のみがモデルに到達することになります。
並列ツール実行が有効な場合(ランタイムプロファイルで parallel_tools = true)、1ターンに含まれる複数の spawn_subagent 呼び出しが同時に実行され、各子の最終応答がそれぞれ自身のツール呼び出しに紐付けられて親に返されます。delegate は parallel: [...] 引数による独自の明示的なファンアウトを備えており(output-strings セクションを参照)、このパスでは各ターゲットがそれぞれ専用のタスクとしてスポーンされ、すべての結果が集約されます。
権限の継承
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を参照)、親の知らないうちにアクティブなモデルを切り替えられてはならないため、このツールはそもそも子には提供されません。
- ポリシーの
- メモリ許可リスト: 兄弟エージェントのエイリアス(
[agents.<alias>]設定キー)のHashSet<String>。親のworkspace.read_memory_fromに親自身のエイリアスを加えたものを継承します。オーバーライドパス(SubAgentOverrides::allowed_agent_aliases)はサブセットとして検証され、親のリストに存在しないエイリアスは名前で拒否されます。親自身のエイリアスは常に再追加されるため、SubAgent は常に親の行を参照できます。 - モデルプロバイダー: 親の
[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>].excluded_tools にあります。allowed_tools リストが空でない場合、モデルが spawn_subagent または delegate ツールを認識するには、そのリストにこれらを含める必要があります。allowed_tools リストが空の場合、excluded_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) - 親プロセスのリスクプロファイルツールゲートが
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を通じた到達可能性が必要ですが、一度選択されると、呼び出し元の非エスカレーション上限、セッションワークスペースのオーバーライド、またはアクション/コストトラッカーなしで、ターゲットエージェント自身のポリシーを解決します。 -
広告されるロースターは、ツールスキーマ内の agent パラメータの説明に含まれます。これは到達可能な集合を正確に列挙し、delegation_policy.mode = "allow" の場合にのみ表示されます。無効化されたエージェント(enabled = false)は、同一プロファイルのピアであっても、明示的な delegates エントリであっても、到達可能になることはありません。
境界付きエージェント委譲では、サブエージェントのツールは呼び出し元の既にポリシーでフィルタされたレジストリから取得され、ターゲット自身の allowed_tools との積集合が取られます。ターゲット上の 空の allowed_tools は「継承」を意味します。サブエージェントは拒否されるのではなく、呼び出し元の完全な委譲可能レジストリで実行されます。空でないリストはそのレジストリとの積集合が取られます。いずれの場合も呼び出し元のレジストリが上限です。リスクプロファイルが呼び出し元に付与されていないツールを指定する境界付きクロスプロファイルターゲットは、そのツールを受け取りません。したがって境界付き委譲はツール境界付きであり、完全な SecurityPolicy::ensure_no_escalation_beyond チェックではありません。その積集合が空の場合でも、ターゲットはツールなしの通常のエージェントモデルターンを受け取ります。
独立型エージェント委任では、サブエージェントのツールはターゲットエージェント自身が構成したポリシーとランタイムレジストリから構築され、そのターゲットで新しいチャットを開くのと同様です。親レジストリは上限として使用されません。delegate ツールは子レジストリから引き続き削除されるため、エージェント委任が別の delegate 呼び出しを通じて再帰することはできません。
深さは親の runtime_profile.max_delegation_depth によって制限されます。これを 1 に設定すると、トップエージェントに 1 回の委任ホップを許可し、それ以上のサブ委任は行われません。
エージェント型ターゲットツールポリシー
ターゲットエージェントの [runtime_profiles.<target>].agentic = true の場合、delegate は親の利用可能なツール(mode = "bounded")またはターゲット自身のランタイムレジストリ(mode = "independent")のいずれかからターゲットサブループのツールレジストリを構築します。ターゲットのリスクプロファイルがそのレジストリをフィルタリングします:
- 設定された空の
[risk_profiles.<target_profile>].allowed_toolsリストは、選択されたレジストリを制限なしにします。 allowed_toolsリストに値が指定されている場合、完全に一致するツール名のみが保持されます。[risk_profiles.<target_profile>].excluded_toolsは常に結果から差し引かれます。delegateは常に子レジストリから削除されるため、エージェント委任が別のdelegate呼び出しを通じて再帰することはありません。
このポリシーは呼び出し元ではなくターゲット側に存在します。同一プロファイルのピアは共有リスクプロファイルを使用します。明示的なクロスプロファイルの委任は、到達可能性と委任ポリシーのゲートを通過した後、ターゲットのリスクプロファイルを使用します。境界付きエージェント委任は、呼び出し元で上限が設定されたツールレジストリとターゲットのツールポリシーの積集合のみを受け取ります。独立したエージェント委任は、ターゲットが所有するツールレジストリを受け取ります。ターゲットのリスクプロファイルが欠落している場合は、サブループが開始する前に拒否されます。実行可能な子ツールがゼロになるように設定されたプロファイルであっても、ツールなしの通常のモデルターンは許可されます。
対象に設定された Reliable プロバイダーチェーンで、ネイティブツール対応の候補とテキストのみの候補が混在している場合、strict_tool_parsing = false では、到達可能なすべてのフォールバックでツールを実行できるよう、エージェントターン全体で 1 つのテキスト/XML ツールプロトコルを使用します。有効なツールが残っていて strict_tool_parsing = true の場合、厳格なパースではそのテキスト/XML フォールバックプロトコルが禁止されるため、ZeroClaw はプロバイダーへのリクエストを行う前に混在チェーンを拒否します。均一なチェーンは変更されません。すべてネイティブのチェーンではネイティブツールトランスポートを使用し、意図的にすべてテキストにしたチェーンでは設定されたテキストツールポリシーに従います。
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.結果ファイルは
<workspace>/delegate_results/<uuid>.jsonにあります。実行中は、ファイルのstatusフィールドはrunningで、終了状態はcompleted、failed、またはcancelledです。設定済みのプロバイダーフォールバックによって復旧した完了タスクでは、要求されたプロバイダーと実際に使用されたプロバイダーの帰属情報、および一般的な復旧警告が同じようにoutputに保存されます。check_resultまたはawait_sessionsで取得できます。失敗したタスクには、同期的な委譲と同じ安全な終了サマリーが保存され、プロバイダーの応答詳細は保存されません。 -
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". -
エージェント型ターゲットでリスクプロファイルが見つからない場合のエラー:
Agent '<target>' is agentic but risk_profile '<target_profile>' is not configured。 -
実行可能な子ツールがゼロの Agentic ターゲット: 空のツールセット自体に対してエラーは発行されず、ターゲットはツールなしの通常のモデルターンを受け取ります。
delegate: 実際に発火したかを確認する方法
delegate は現在、専用のトレーシングスパンを出力しません。シグナルとなるのは、ログ上に現れるターゲットエージェントのループであり、これは親のツール呼び出しディスパッチが属していたスコープを継承します。バックグラウンドモードのスポーンは帯域外で検証する方が簡単です。結果ファイル <workspace>/delegate_results/<uuid>.json がディスク上に存在し、ターゲットエージェントの status + output フィールドを保持します。cat や jq を使えば、ログにまったく触れることなく確認できます。
(Cron で起動されたエージェントジョブは別の生成サイトであり、上記で説明した明示的な subagent スパンを使用します。delegate と cron は同じパスではありません。)
このページに含まれていないもの(意図的に)
- 会話記録の例。ここで「ボットが何と言うか」について記述した内容は、モデルに依存します。ボットの応答は、ツールの出力、モデル、システムプロンプト、および現在の会話状態の下流にあり、これらはいずれもこのページでは制御できません。検証可能なレイヤーは、ツールが返すもの(上記)とログが記録するものです。
- 専用の「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 を列挙する必要があります。excluded_tools には列挙してはいけません | 呼び出し元の空でない risk_profile.allowed_tools には delegate が含まれている必要があります。excluded_tools には含まれていてはいけません。呼び出し元の delegation_policy mode = "allow" であり、ターゲットが呼び出し元の到達可能セット(同一プロファイルのピア、または明示的な delegates エントリ)に含まれている必要があります |
| 使用する場面 | 同一のアイデンティティ内に留まるべき内部サブタスク | 境界付きまたは独立した委任のもとで、別の構成済みスペシャリスト(別のモデル、別のエイリアス)にタスクを担当させたい |
サポートされていないもの
- 深さ1を超える再帰。 SubAgent は自身の SubAgent を生成できません。この上限はツール側での明確な拒否であり、予算ではありません。Cron で起動された実行は深さ0から開始し、1レベルの生成が可能です。エージェントループで起動された SubAgent は深さ1にあり、それ以上の生成を拒否します。
- 子に対する別個のアイデンティティ。 SubAgent は親のエージェント UUID を共有します。別のアイデンティティで実行するには、
delegateを使用して設定済みの兄弟エージェントに引き渡します。 - スポーンごとの時間予算。
timeout_secs引数はありません。親は子の実行が完了するまでの全期間ブロックされます。キャンセルは、より広範な中断スコープを通じて伝播される必要があります。 - 進捗を親へストリーミングで返す。 親は子の最終レスポンスを、完了後に単一の文字列として受け取ります。
[agents.<alias>].subagent_*設定ブロック。 バリデーターとオーバーライド型は本日提供されますが、呼び出し元が定義する絞り込みを配線するオペレーター向けの設定サーフェスは今回のリリースには含まれていません。両方のスポーン箇所では、そのサーフェスが実装されるまでSubAgentOverrides::default()を渡します。- 独立した
delegateターゲットとalways_ask。 ターゲットエージェントのリスクプロファイルに空でないalways_askエントリがある場合、独立したデリゲーションはブロックされます。ランタイムはターゲットの開始前に拒否します。これにはバックグラウンドおよび並列デリゲーションも含まれます。このブロッカーは、独立した子エージェントの承認フォワーディングが将来の ZeroClaw バージョンでサポートされるまで残ります。