Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

委派与子代理

SubAgent 是由父智能体派生的临时子运行,默认继承父智能体的身份:相同的智能体别名、相同的 SecurityPolicy、相同的内存允许列表、相同的已配置模型提供方、相同的工具注册表。可通过追踪 span agent.<alias>.subagent.<run_id> 作为子运行进行审计。

子智能体不是一个独立的配置概念。schema 中不存在 [subagents.*] 配置块。每个子智能体的身份取决于是哪个父级的智能体循环创建了它。

何时使用 spawn_subagentdelegate

两个工具就在旁边,它们不可互换。

  • spawn_subagent:在其自身身份下再次运行同一个 agent,以执行一个聚焦的子任务。子 agent 会继承父 agent 的完整权限范围,但可能会有所收窄。当父 agent 希望将某个内部子任务从其主对话历史中独立出来,而不改变身份时,可使用此功能。
  • delegate:将请求交给另一个已配置的 DIFFERENT 代理(通过别名命名)。目标代理以其自身的身份和模型提供方运行,但委派是有门控的:调用方的风险配置文件必须将 delegation_policy mode = "allow"(默认是 "forbidden"),并且目标必须可作为同一配置文件的同级代理或一个显式的 delegates 条目被访问。显式条目选择 mode = "bounded"mode = "independent",这决定调用方的工具上限是否仍然适用。用于让另一位已配置的专门代理来承担工作。参见下面的 Delegation gating

本页面完整记录了 spawn_subagent 的端到端流程。delegate 位于 crates/zeroclaw-runtime/src/tools/delegate.rs,是一个独立的接口。

SubAgent 的实例化方式

两个生成点汇聚于 SubAgentSpawncrates/zeroclaw-runtime/src/subagent/mod.rs:97):

  1. 通过 agent 循环:模型调用 spawn_subagent 工具并传入一个 prompt 字符串。该工具像注册表中的其他工具一样进行注册(crates/zeroclaw-runtime/src/tools/mod.rsSpawnSubagentTool::new)。
  2. 来自 cronJobType::Agent 任务通过 run_agent_jobcrates/zeroclaw-runtime/src/cron/scheduler.rs)运行,它构建相同的 SubAgentContext,但将子进程标记为顶层运行(而非 SubAgent),以便它本身可以派生一级 subagent。

两种路径都会调用:

#![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 运行时。任何内容都不会跨越进程边界。

  1. 父级的工具循环会分派 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 中列出了它,则拒绝执行并返回一条指明父级别名的消息。
  3. 该工具调用 SubAgentSpawn::for_agent + build。失败情况(未知的父级别名、升级覆盖)会以 ToolResult { success: false, error: "subagent spawn failed: ..." } 的形式呈现。
  4. 该工具构造 AgentRunOverrides { security, memory: None, is_subagent: true, suppress_memory_inject: true }(子进程的 SubTurn 来源已经跳过引擎记忆注入;该标志使这一退出选项显式化),并在键为 subagent-<uuid> 的跟踪作用域内等待 crate::agent::runcrates/zeroclaw-runtime/src/agent/loop_.rspub async fn run)。父级的 tool 执行会阻塞,直到子进程返回。
  5. 子代理循环运行至完成。其工具注册表会重新构建,并将 is_subagent_caller: true 传入它自己的 SpawnSubagentTool,因此任何递归调用的尝试都会在同一个 depth-1 关卡处被拒绝。
  6. 子任务返回 Result<String>。父任务的 spawn_subagent 工具将其包装为:
    • 成功:ToolResult { success: true, output: <子代理的最终响应>, error: None }。空输出将被替换为字面值 "subagent completed without output"
    • 失败:ToolResult { success: false, error: Some("subagent run failed: ...") }
  7. 父级的工具循环会带着该 ToolResult 在其对话上下文中继续运行。子级的中间轮次和工具调用不会重放到父级的历史记录中;只有最终响应会显示出来。

交付回上游的内容

一点说明:子代理的最终助手消息会以字符串形式包装在 ToolResult.output 中。

  • 子代理的工具调用、中间推理回合及其执行的所有内存写入操作,均可在该子代理追踪范围(span)下的结构化日志中查看,但不会进入父代理的对话历史。
  • 子会话存储在路径 subagent-<uuid> 下(对于 cron 触发的运行则为 cron-<uuid>)。这是对话历史记录的键,而非文件系统位置,它将子会话的历史记录与父会话的历史记录隔离开来。
  • 子进程执行的内存写入会写入父进程的标识(在 SQL/Postgres 后端使用相同的 agent UUID;对于 Markdown 则使用相同的工作区目录)。通过 Cron 启动的运行会禁用 memory.auto_save,因此显式写入仍然有效,但常规的回忆不会累积。

没有向父级回传的流式传输或部分进度通道。长时间运行的 SubAgent 会在其整个执行期间阻塞父级的工具执行;不存在针对单次调用的超时设置项。

单轮多次调用

智能体循环会应用每轮重复调用防护机制:在同一轮中以相同参数调用两次的工具,通常第二次调用会被跳过。spawn_subagentdelegate 不受该防护机制的限制(exempt)。使用相同的提示词启动多个调用(用于冗余、采样、扇出)是一种刻意的模式,而非意外的重复,因此每个相同的调用都会执行,每个结果都会被返回。如果没有这项豁免,只有第一个相同的调用会执行,也只有它的输出会传递给模型。

当启用并行工具执行时(运行时配置文件中的 parallel_tools = true),单轮中的多个 spawn_subagent 调用会并发运行,每个子代理的最终响应都会返回给父代理,并与其各自的工具调用相对应。delegate 通过 parallel: [...] 参数拥有自己显式的分发机制(参见 output-strings 部分);该路径会在各自独立的任务中生成每个目标,并聚合所有结果。

权限继承

子代理(SubAgent)会逐字继承父级的权限,除非生成位置提供了用于收窄权限的 SubAgentOverrides。目前两个内置的生成位置都传入 SubAgentOverrides::default()(继承全部权限)。覆盖接口已发布并通过验证;未来由调用方提供的收窄路径可直接接入,无需运行时改动。

沿坐标轴逐一继承:

  1. SecurityPolicy:通过 Arc<SecurityPolicy> 克隆继承。覆盖路径(SubAgentOverrides::policy = Some(policy))会运行 SecurityPolicy::ensure_no_escalation_beyondcrates/zeroclaw-config/src/policy.rs),并拒绝任何为子代理添加父代理所不具备权限的字段。已验证的维度包括自治级别、allowed_roots(rw + ro + 只写)、allowed_commands、workspace_only、按父 ⊆ 子方向校验的 forbidden_paths、shell_env_passthrough、max_actions_per_hourmax_cost_per_day_centsshell_timeout_secsblock_high_risk_commands 以及 require_approval_for_medium_risk。被拒绝时会串联一个精确的 EscalationViolation,使诊断信息能够指明违规的字段。
  2. 操作/成本预算PerSenderTracker 由父级和子级通过 Arc 克隆共享。逐字继承路径:子级持有相同的 Arc<SecurityPolicy>,因此对 record_action() / record_cost() 的写入会命中同一个桶。覆盖路径:SubAgentSpawn::build 会显式地将父级的 tracker 字段复制到收窄后的子级策略中。SubAgent 无法通过生成(spawn)来绕过 max_actions_per_hourmax_cost_per_day_cents,该限制是共享的。
  3. 工具注册表:子级注册表由 tools::all_tools_with_runtime 在继承的策略下重新构建。随后该注册表会经过 apply_policy_tool_filtercrates/zeroclaw-runtime/src/agent/loop_.rs)处理,凡名称未能通过任一关卡的工具都会被剔除:
    • 策略的 allowed_tools / excluded_tools(来源于父级的 risk_profile)。
    • 调用方传递给 agent::runallowed_tools 参数。spawn_subagent 位于注册表中,但其为子级设置的 is_subagent_caller 标志为 true,因此在任何 spawn 操作之前就会触发深度为 1 的拒绝。同一个 is_subagent_caller 标志会将 model_switch 从子级的注册表中完全移除:SubAgent 会原封不动地继承父级的模型(参见轴 5),并且不得能够在父级不知情的情况下切换正在使用的模型,因此该工具根本不会提供给它。
  4. 内存允许列表:由同级 agent 别名组成的 HashSet<String>(即 [agents.<alias>] 配置键)。继承自父级的 workspace.read_memory_from 以及父级自身的别名。覆盖路径(SubAgentOverrides::allowed_agent_aliases)会作为子集进行校验;任何不在父级列表中的别名都会按名称被拒绝。父级自身的别名总是会被重新添加,因此 SubAgent 始终能看到其父级的记录。
  5. 模型提供方:继承自父级的 [agents.<alias>] model_provider 解析结果。Temperature 来自父级的提供方条目(config.model_provider_for_agent(parent_alias).and_then(|e| e.temperature))。这种继承是强制性的,而不仅仅是默认值:model_switch 已从 SubAgent 的工具注册表中排除(参见 axis 3),因此 SubAgent 无法切换自己的模型。若要在不同的模型上运行子任务,请使用 delegate 委派给某个 model_provider 指向该模型的同级 agent。
  6. 数据层的身份标识:在 agents 表(SQL 后端)中使用相同的 UUID,对 Markdown 使用相同的工作区目录,使用相同的密钥存储。父级与子级的区别纯粹在于可观测性:单独的追踪 span 和单独的对话历史会话键。

用户如何引发火灾

这些工具不是你自己调用的,而是机器人在其回合内部进行调用。作为用户,你可以通过表述请求的方式来影响机器人的选择。这里没有特殊命令、没有斜杠语法,用户也无需输入任何 JSON。模型究竟选择 spawn_subagent 还是 delegate,取决于它的系统提示词、工具的 description 文本(对模型可见)以及用户的措辞。措辞只是施加影响,并不能强制决定。

可以做到确定性的是可用性:不在父代理注册表中的工具无法被选用。风险配置文件门控位于 [risk_profiles.<alias>].allowed_tools[risk_profiles.<alias>].excluded_tools。非空的 allowed_tools 列表必须包含 spawn_subagentdelegate,模型才能看到该工具;空的 allowed_tools 列表则不限制工具可用性,除非 excluded_tools 指定了该工具。编辑配置后请重启守护进程。

端到端可验证的内容:

  1. 协议拥有的工具输出和拒绝字符串是 Rust 字面量契约。用户可见的终端补全失败交付属于 Fluent 目录契约:英文源文本的名称如下,定义相同键的非英文目录或磁盘覆盖项可能会以不同方式呈现该文本。
  2. 改变行为的具体配置项(allowed_toolsmax_delegation_depth 等)。
  3. 用于限定子运行期间发出的所有内容范围的结构化追踪 span 形态。

这些文档中无法验证的内容:

  1. 当用户请求“生成一个子代理来……“时,你的特定 bot、特定模型、特定系统提示是否会选用该工具,这取决于具体情况。措辞会影响结果;结果因情况而异。如果 bot 没有选用该工具,最可靠的办法是用明确的指令扩展 bot 的系统提示(“当被要求执行一个聚焦的子任务时,使用 spawn_subagent 工具”)。
  2. 机器人在最终回复中写给你的确切文本。机器人会读取工具的输出,并在此基础上生成自己的回复。工具的输出文本可能会被引用、改述或概括。

spawn_subagent:模型看到的拒绝字符串

这些内容是精确的,来源于 crates/zeroclaw-runtime/src/tools/spawn_subagent.rs。模型将其作为该工具的错误字符串接收并作出响应。用户可见的 bot 回复是模型接下来所写的内容;它通常会引用或复述这条拒绝信息。

  1. 空/缺失的 prompt 参数:Missing or empty 'prompt' parameter
  2. 调用方本身就是一个 SubAgent(深度上限为 1):spawn_subagent: a subagent may not spawn its own subagents (depth-1 cap)
  3. 父级风险配置工具门控排除了 spawn_subagentspawn_subagent: refused — agent '<parent_alias>' risk_profile does not list spawn_subagent in allowed_tools
  4. 未知父别名 / spawn 构建错误:subagent spawn failed: <wrapped error>
  5. 子运行返回错误:subagent run failed: <wrapped error>

工具成功执行后,其输出即为子代理的最终响应文本。如果子代理返回空字符串,则输出为字面占位符:subagent completed without output。成功情况下没有固定的前缀可供 grep 检索。

spawn_subagent:如何验证它确实已触发

跟踪你的日志。工具生成的子进程运行在一个 scope! 内部,该作用域会发出一个名为 zeroclaw_scope 的跟踪跨度(目标为 zeroclaw_log_internal_scope),携带 agent_alias=<parent>session_key=<uuid>。子进程运行期间发出的每一行日志都会携带这些字段。父进程自己的回合有其自己的 session_key;对于同一个 agent_alias,在回合中途出现一个新的 session_key 值,就是 SubAgent 已运行的信号。子进程的对话历史会话路径为 subagent-<uuid>(类文件系统标识符,与跟踪字段不同)。

由 Cron 启动的 agent 作业使用一个不同的、更明确的 span 名称: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

这是针对 agent-loop 派生路径的轻量信号。通过 attribution_span!(tool) 路由的专用「subagent started / completed」记录被作为代码侧的后续工作进行跟踪,一旦 agent loop 将工具执行包装在归因 span 中,工具内的每个 record! 都会自动携带 tool=spawn_subagent,于是这个问题就变成了一次简单的 grep。

委派门控

delegatecrates/zeroclaw-runtime/src/tools/delegate.rs 中会在目标 agent 运行前按以下顺序强制执行两道关卡:

  1. 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 选择项)。

  2. 可达性:目标代理必须位于调用方的可达集合中,该集合由 Config::reachable_delegate_target_configs 解析得到。可达集合是 [agents.<caller>] 上两个按代理划分的来源的并集,减去调用方本身:

    • 同档同伴:所有与调用方共享相同风险档案的其他代理,在 delegate_same_risk_profile = true(默认值)时纳入。将其设为 false 可使调用方退出自动允许同伴的行为。

    • 显式名册delegates,一个可能为空的目标列表,调用方即使跨风险配置文件也可委派给这些目标。字符串条目便于手动编辑,并表示有界目标。对象条目则使该模式显式化:

      delegates = [
        "reviewer",
        { agent = "sysadmin", mode = "independent" },
      ]
      

      当配置保存时,每个条目都会以对象形式写入,带有 mode = "bounded"mode = "independent"。在守护进程和 UI 二进制文件升级到支持委托模式的构建版本之前,不要推广这种配置结构。较旧的二进制文件预期 delegates 只包含字符串;对象条目会使该二进制文件中的 agents 部分无效,而弹性加载器会丢弃该部分,因此修复界面仍然可以启动。 当目标超出该集合时,拒绝会说明原因。例如:

    delegate target “<target>” 无法从 “<caller>” 到达:风险配置不同(调用方使用 “<caller_profile>”,目标使用 “<target_profile>”)。delegate_same_risk_profile 仅能到达具有相同风险配置的 agent;请在 [agents.<caller>].delegates 中添加带有所需 mode 的显式条目,或更改其中一个 agent 的 risk_profile。

    delegate target "<target>" 无法从 "<caller>" 访问:delegate_same_risk_profile 已禁用,且目标未列在 [agents.<caller>].delegates 中
    
    委托目标 "<target>" 无法从 "<caller>" 访问:目标代理已禁用
    

    有界目标继承调用方的 action/cost 跟踪器。当有界目标与调用方共享其风险配置文件时,它也会继承调用方的会话工作区边界。只有当有界 cross-profile 目标可通过调用方的委派名册和 delegation_policy 访问时才允许;它在目标解析后的策略下运行,而 agentic 工具的可用性则由调用方的工具注册表上限限制。

    只有在使用 mode = "independent" 显式列出时,才可使用独立目标。它仍然需要 delegation_policy.mode = "allow" 并通过 delegates 可达,但一旦被选中,它会在不受调用方的非升级上限、会话工作区覆盖或操作/成本跟踪器影响的情况下,解析目标代理自身的策略。

工具模式中的 agent 参数说明里包含了宣传的 roster。它仅列出这个可达集合,而且只有在 delegation_policy.mode = "allow" 时才会列出。已禁用的 agent(enabled = false)永远不可达,无论是作为同一 profile 的同级节点,还是作为显式的 delegates 条目。

在有界的代理式委派中,子代理的工具来自调用方已按策略过滤过的注册表,并与目标自身的 allowed_tools 取交集。目标上的 allowed_tools 表示“继承”:子代理以调用方完整的可委派注册表运行,而不会被拒绝。非空列表则与该注册表取交集。无论哪种情况,调用方的注册表都是上限:一个有界的跨配置文件目标,即使其风险配置文件中列出了调用方从未获授的工具,也不会获得它。 因此,有界委派是以工具为边界,而不是完整的 SecurityPolicy::ensure_no_escalation_beyond 检查。如果该交集为空,目标仍会收到一次正常的代理式模型轮次,只是没有任何工具。

在独立的 agentic 委派中,子代理的工具是根据目标代理自身已配置的策略和运行时注册表构建的,就像为该目标新开一个聊天一样。父级注册表不会作为上限使用。delegate 工具仍会从子注册表中移除,因此 agentic 委派不能通过另一次 delegate 调用递归。

深度受父级的 runtime_profile.max_delegation_depth 限制。将其设置为 1 可允许顶层 agent 进行一次委派跳转,但不能进一步进行子委派。

智能体目标工具策略

如果目标代理的 [runtime_profiles.<target>].agentic = truedelegate 会从父级可用工具(mode = "bounded")或目标自身的运行时注册表(mode = "independent")构建目标子循环的工具注册表。随后,目标风险配置文件会对该注册表进行过滤:

  1. 已配置的空 [risk_profiles.<target_profile>].allowed_tools 列表会使所选注册表不受限制。
  2. 非空的 allowed_tools 列表仅保留完全匹配的工具名称。
  3. [risk_profiles.<target_profile>].excluded_tools 始终从结果中移除。
  4. delegate 始终会从子注册表中移除,因此代理委托无法通过另一次 delegate 调用进行递归。

此策略驻留在目标端,而不是调用方。同一配置文件的对等方使用共享风险配置文件。显式跨配置文件委托在可达性和委托策略门控之后使用目标的风险配置文件。受限的代理式委托仅接收与目标工具策略相交的、由调用方上限约束的工具注册表;独立的代理式委托接收目标拥有的工具注册表。缺少目标风险配置文件会在子循环开始前拒绝。一个配置好的配置文件即使使可执行的子工具数量为零,仍然允许一次不使用工具的正常模型轮次。

当目标配置的 Reliable 提供商链混合了支持原生工具和仅支持文本的候选提供商时,strict_tool_parsing = false 会在整个代理回合中使用一种文本/XML 工具协议,以便每个可到达的回退提供商都能执行工具。如果仍有有效工具且 strict_tool_parsing = true,ZeroClaw 会在发起提供商请求之前拒绝该混合链,因为严格解析禁止使用这种文本/XML 回退协议。统一链的行为不变:全原生链使用原生工具传输,而有意配置的全文本链遵循已配置的文本工具策略。

delegate:模型看到的输出字符串

面向用户的失败字符串是本地化的 Fluent 消息。其英文权威来源是 crates/zeroclaw-runtime/locales/en/cli.ftl;下面的示例展示的是当前英文目录值,而不是线级字符串契约。本节未将其标记为 Fluent 键的其余字符串均为协议/工具输出。

  1. 同步成功:输出以 [Agent '<target>' (<provider_type>/<model>)]\n 开头,后跟非空的目标代理响应。当目标通过已配置的提供商回退机制恢复时,其标头会改为标识所请求和实际提供的提供商/模型,例如 [Agent 'reviewer' (requested: anthropic.primary/claude; served: openai.terra/gpt-5.6-terra, agentic)]。对于代理型目标,此归属信息描述的是生成最终响应的模型请求,而不是仅生成工具调用的较早请求。结果还会以本地化的 delegate-provider-fallback-warning 结尾。英文为:Warning: The delegated agent recovered through a provider fallback. Provider failure details were logged and omitted from this result. 此归属信息和警告属于委派结果;不得将其呈现为调用方代理的回退。它们有意省略被拒绝的提供商的错误详细信息、端点和凭据。重试同一个已配置候选项不会产生此警告;即使后续已配置候选项的提供商和模型标签与第一个候选项相同,到达该候选项也会产生此警告。

  2. 终端空响应是同步失败:其 error 字段使用 cli-delegate-error-invalid-semantic-completion,并将 agent_name 设置为目标对象。英文表述为:Agent '<target>' failed: model provider returned an invalid semantic completion.

  3. 其他同步失败:错误字段以 Agent '<target>' failed: <wrapped error> 开头。如果所有已配置的提供商候选项均失败,<wrapped error> 是 Reliable 按顺序生成的安全摘要,其中包含失败事件、重试次数、失败类别、阶段和固定的修复提示。不会将提供商响应正文、端点、别名、模型和凭据返回给调用方代理;需要更多详细信息时,请根据安装环境的常规操作员日志记录策略调查提供商尝试日志。结果仍然是错误,而不是恢复警告。

  4. 同步超时(当目标的运行时配置文件设置了 delegation_timeout_secs 时):错误字段为 Agent '<target>' timed out after <N>s

  5. 后台生成成功:输出为三行字面量

    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;终止状态为 completedfailedcancelled。通过已配置的提供商回退机制恢复的已完成任务,会在其 output 中存储相同的请求与实际服务归属信息以及通用恢复警告;可使用 check_resultawait_sessions 检索该结果。失败任务存储的安全终止摘要与同步委派相同,而不是提供商响应详细信息。

  6. action="check_result" 使用未知的任务 ID:错误为 No result found for task_id '<uuid>'

  7. action="await_sessions" 配合 task_ids: [<uuid>, ...] 一次等待多个后台结果文件。输出是一个 JSON 对象,包含 statuscompletetimeout)、completedpendingmissingfailedresultstimeout_ms 默认值为 30000,且上限为 120000;发生超时时,工具会返回部分结果以及一条错误,说明一个或多个任务仍处于待处理或缺失状态。不允许重复的任务 ID。

  8. 并行扇出输出:以 [Parallel delegation: <N> agents]\n\n 开头,后跟由 \n\n 分隔的各代理块,每个块以 --- <target> (success=<bool>) ---\n 开头。恢复的目标会在其自身块中保留请求目标与服务目标的归属信息以及通用回退警告。单个代理失败时,内部块为 --- <target> (success=false) ---\nError: <wrapped error>

  9. 未知的目标 agent:错误信息为 Unknown agent '<target>'. Available agents: <comma-separated list>

  10. 深度超出限制(由父级的 runtime_profile.max_delegation_depth 控制,默认值为 3):错误为 Delegation depth limit reached (<depth>/<max>).

  11. 未知操作:错误为 Unknown action '<value>'. Use delegate/check_result/list_results/cancel_task/await_sessions.

  12. 独立目标,其风险配置文件具有 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".

  13. 智能体目标缺少目标风险配置时:错误为 Agent '<target>' is agentic but risk_profile '<target_profile>' is not configured

  14. Agentic 目标没有可执行的子工具:空工具集本身不会发出错误;目标会接收到一次正常的模型轮次,不带任何工具。

delegate:如何验证它确实已触发

delegate 目前不会发出专用的追踪跨度(tracing span)。其信号体现为目标智能体的循环出现在日志中,并继承父级工具调用调度所处的任意作用域。后台模式的生成更易于带外验证:结果文件 <workspace>/delegate_results/<uuid>.json 存在于磁盘上,并携带目标智能体的 statusoutput 字段;使用 catjq 即可读取,完全无需查看日志。

(由 Cron 启动的 agent 作业属于独立的生成位置,使用上文所述的显式 subagent span;delegate 与 cron 并非同一路径。)

本页未涵盖的内容(有意为之)

  1. 示例对话记录。我在这里所写的任何关于“机器人会说什么”的描述都依赖于具体模型。机器人的回复取决于工具的输出、模型、系统提示词以及当前对话状态,而这些都不在本页面的控制范围之内。可验证的层面是工具返回的内容(如上所示)以及日志所捕获的内容。
  2. 一个专门的 “subagent fired” / “delegate fired” 日志标记。已作为代码侧的后续事项进行跟踪。目前,操作员通过上文描述的 scope 结构(即现有的结构性信号)以及 background-mode 结果文件进行验证。

spawn_subagentdelegate 之间进行选择

spawn_subagentdelegate
身份与父级相同(相同 UUID,相同风险概况)目标代理的身份(不同的别名;同配置文件的对等代理或明确的跨配置文件委托方)
权限模型父级策略的逐字内容(或缩小后的子集)有界目标在目标策略下运行,并以调用方的 agentic 工具注册表为上限;独立目标在目标策略和目标自有注册表下运行
模型提供商父级目标智能体配置的提供商
生成深度硬性上限为 1最多 runtime_profile.max_delegation_depth(默认值为 3)
后台模式不支持background: true 返回一个 task_id
并行分发无内置参数;当 parallel_tools = true 时,单轮中的多次调用会并发执行parallel: [...] 并发运行多个目标
门控非空的 risk_profile.allowed_tools 必须列出 spawn_subagentexcluded_tools 不得列出它调用方非空的 risk_profile.allowed_tools 必须列出 delegateexcluded_tools 不得列出它;调用方的 delegation_policy mode = "allow";并且目标位于调用方的可达集合中(同 profile 的对等项或显式的 delegates 条目)
使用场景应保持在同一身份内的内部子任务希望由另一个已配置的专员(不同模型、不同别名)在有界或独立委派下负责该任务

不支持的内容

  1. 超过深度 1 的递归。 SubAgent 无法生成自己的 SubAgent。该限制是工具层面的硬性拒绝,而非预算限制。Cron 启动的运行从深度 0 开始,可以生成一级;由 agent-loop 启动的 SubAgent 处于深度 1,会拒绝进一步生成。
  2. 子代理的独立身份。 子代理共享父代理的 agent UUID。如需以不同身份运行,请使用 delegate 移交给已配置的同级代理。
  3. 单次生成时间预算。 没有 timeout_secs 参数。父进程会在子运行的整个持续期间保持阻塞;取消操作必须通过更大范围的中断作用域来传递。
  4. 向父级流式传输进度。 父级在子级完成后将其最终响应视为单个字符串。
  5. [agents.<alias>].subagent_* 配置块。 验证器和覆盖类型今日已发布;面向操作员的配置层(用于贯通调用方定义的收窄逻辑)不在本次发布范围内。在该层落地之前,两个生成点均传递 SubAgentOverrides::default()
  6. 带有 always_ask 的独立 delegate 目标。 当目标代理的风险配置文件包含非空的 always_ask 条目时,独立委派会被阻止。运行时会在启动目标之前拒绝,包括后台和并行委派。此阻塞将一直保留,直到未来的 ZeroClaw 版本支持为独立子代理转发审批。