Appearance
子代理是 OpenClaw 后台并行任务的核心机制:通过 sessions_spawn 派生独立会话处理耗时工作,完成时自动通告结果。支持隔离/分支上下文、嵌套深度编排(maxSpawnDepth:2)、Discord 线程绑定和自动归档(默认 60 分钟)。关键配置:agents.defaults.subagents.model 设置子代理模型,tools.subagents.tools 限制工具权限。
OpenClaw 子代理配置:并行任务与嵌套编排
Sub-agents 是从现有 Agent 运行中派生出来的后台 Agent 运行实例。它们在独立会话(agent:<agentId>:subagent:<uuid>)中运行,完成后将结果通告回发起请求的聊天频道。每个子代理运行都被追踪为一个 后台任务。
主要目标:
- 并行化"研究/长任务/慢工具"工作,不阻塞主运行。
- 默认隔离子代理(会话分离 + 可选沙箱)。
- 子代理默认不获取会话工具,降低误用风险。
- 支持可配置的嵌套深度以满足编排模式需求。
::: note 费用提示 每个子代理有独立上下文和 token 用量。对于重度或重复性任务,通过 agents.defaults.subagents.model 或每代理覆盖为子代理设置更便宜的模型,主代理保持高质量模型。 :::
Slash 命令
使用 /subagents 检查或控制当前会话的子代理运行:
text
/subagents list
/subagents kill <id|#|all>
/subagents log <id|#> [limit] [tools]
/subagents info <id|#>
/subagents send <id|#> <message>
/subagents steer <id|#> <message>
/subagents spawn <agentId> <task> [--model <model>] [--thinking <level>]/subagents info 显示运行元数据(状态、时间戳、会话 ID、转录路径、清理信息)。用 sessions_history 获取有界和安全过滤的历史视图;需要原始完整转录时检查磁盘上的转录路径。
线程绑定控制
这些命令适用于支持持久线程绑定的频道(见下方支持线程的频道):
text
/focus <subagent-label|session-key|session-id|session-label>
/unfocus
/agents
/session idle <duration|off>
/session max-age <duration|off>派生行为
/subagents spawn 以用户命令方式启动后台子代理(非内部中继),运行结束时向发起聊天发送一条最终完成通告。
非阻塞、推送式完成
- 派生命令是非阻塞的,立即返回一个 run id。
- 完成时子代理向发起聊天频道通告摘要/结果消息。
- 完成是推送式的。派生后,不要用轮询
/subagents list、sessions_list或sessions_history的循环来等待完成。 - 完成时 OpenClaw 尽力关闭该子代理会话打开的浏览器标签/进程,然后继续通告清理流程。
手动派生投递弹性
- OpenClaw 首先尝试带稳定幂等键的直接
agent投递。 - 直接投递失败则回退到队列路由。
- 队列路由仍不可用则短指数退避重试,最终放弃。
完成交接元数据
完成交接给发起会话的内容是运行时生成的内部上下文(非用户编写的文本),包含:
Result:最新可见的assistant回复文本,或 sanitized 的最新工具/工具结果文本Status:completed successfully/failed/timed out/unknown- 紧凑运行时 / token 统计
- 投递指令:告知发起代理以正常助手语气重写(不转发原始内部元数据)
模式和 ACP 运行时
--model和--thinking可针对该次运行覆盖默认值。- 完成后使用
info/log检查详情和输出。 /subagents spawn是一次性模式(mode: "run")。如需持久线程绑定会话,使用sessions_spawn并设置thread: true和mode: "session"。- 对于 ACP 运行时会话(Codex、Claude Code、Gemini CLI),使用
sessions_spawn并设置runtime: "acp",参见 ACP Agents。
上下文模式
原生子代理默认隔离启动,除非调用方明确要求 fork 当前转录。
| 模式 | 使用场景 | 行为 |
|---|---|---|
isolated | 全新研究、独立实现、慢工具工作,或可简写任务文本的任务 | 创建干净的子转录(默认),token 用量更低 |
fork | 依赖当前对话、先前工具结果或发起方转录中已有详细指令的工作 | 将发起方转录分支到子会话后再启动 |
fork 谨慎使用,它是上下文敏感委派,不能替代编写清晰的任务提示。
工具:sessions_spawn
启动子代理运行(deliver: false,全局通道 subagent),然后运行通告步骤,将通告回复发布到发起聊天频道。
可用性:取决于调用方的有效工具策略。coding 和 full 配置文件默认暴露 sessions_spawn。messaging 配置文件不暴露,需要添加 tools.alsoAllow: ["sessions_spawn", "sessions_yield", "subagents"] 或使用 tools.profile: "coding"。频道/群组、提供者、沙箱和每代理的 allow/deny 策略仍可在配置文件阶段后移除该工具。从同一会话使用 /tools 确认有效工具列表。
默认值:
- 模型:继承调用方,除非设置了
agents.defaults.subagents.model(或每代理agents.list[].subagents.model);显式sessions_spawn.model仍优先。 - 思考级别:继承调用方,除非设置了
agents.defaults.subagents.thinking(或每代理agents.list[].subagents.thinking);显式sessions_spawn.thinking仍优先。 - 运行超时:若省略
sessions_spawn.runTimeoutSeconds,OpenClaw 使用agents.defaults.subagents.runTimeoutSeconds(若已设置),否则回退到0(无超时)。 - 任务投递:原生子代理在第一条可见的
[Subagent Task]消息中接收委派任务。子代理系统提示携带运行时规则和路由上下文,不是任务的隐藏副本。
委派提示模式
agents.defaults.subagents.delegationMode 控制提示指导,不改变工具策略或强制委派。
suggest(默认):保持标准提示提示使用子代理处理较大或较慢的工作。prefer:告诉主代理保持响应,通过sessions_spawn委派更涉及的工作。
每代理覆盖使用 agents.list[].subagents.delegationMode:
json5
{
agents: {
defaults: {
subagents: {
delegationMode: "prefer",
maxConcurrent: 4,
},
},
list: [
{
id: "coordinator",
subagents: { delegationMode: "prefer" },
},
],
},
}工具参数
task(必填):子代理的任务描述。taskName(可选):用于后续subagents定位的稳定句柄。必须匹配[a-z][a-z0-9_]{0,63},不能是保留目标如last或all。当协调者可能需要在派生多个子代理后引导、杀死或识别某个子代理时优先使用。label(可选):人类可读标签。agentId(可选):当subagents.allowAgents允许时在另一个已配置的代理 id 下派生。runtime("subagent" | "acp",默认"subagent")。acp仅用于外部 ACP harness(claude、droid、gemini、opencode 或明确请求的 Codex ACP/acpx)以及agents.list[]中runtime.type为acp的条目。resumeSessionId(可选):仅 ACP,恢复现有 ACP harness 会话(runtime: "acp"时生效);原生子代理派生忽略。streamTo("parent"):仅 ACP,将 ACP 运行输出流式传输到父会话;原生子代理派生省略。model(可选):覆盖子代理模型。无效值被跳过并将子代理以默认模型运行并警告。thinking(可选):覆盖子代理运行的思考级别。runTimeoutSeconds(可选):默认agents.defaults.subagents.runTimeoutSeconds否则为0。设置后子代理在 N 秒后终止。thread(布尔值,默认false):为true时请求该子代理会话的频道线程绑定。mode("run" | "session",默认"run")。若thread: true且省略mode,默认变为session。mode: "session"需thread: true。cleanup("delete" | "keep",默认"keep")。"delete"在通告后立即归档(仍通过重命名保留转录)。sandbox("inherit" | "require",默认"inherit")。require在目标子运行时未沙箱化时拒绝派生。context("isolated" | "fork",默认"isolated")。fork将发起方当前转录分支到子会话。仅原生子代理。线程绑定派生默认fork;非线程派生默认isolated。
WARNING
sessions_spawn 不接受频道投递参数(target、channel、to、threadId、replyTo、transport)。如需投递,从派生运行中使用 message/sessions_send。
任务名称与定位
taskName 是面向模型的编排句柄,不是会话键。当协调者稍后需要引导或杀死该子代理时,用于稳定的子节点名称(如 review_subagents、linux_validation、docs_update)。
定位解析接受精确 taskName 匹配和明确前缀。匹配范围限于 /subagents 编号目标使用的同一活跃/近期目标窗口。如果两个活跃或近期子节点共享同一 taskName,则目标不明确;使用列表索引、会话键或运行 id 代替。保留目标 last 和 all 不是有效的 taskName 值,因为它们已有控制含义。
工具:sessions_yield
结束当前模型轮次,等待运行时事件(主要是子代理完成事件)作为下一条消息到达。在派生了所需的子工作后,当发起方无法在完成事件到达前产生最终答案时使用。
sessions_yield 是等待原语。不要用轮询 /subagents list、sessions_list、sessions_history、shell sleep 或进程轮询来替代等待子代理完成。
只有当前会话的有效工具列表包含 sessions_yield 时才使用。某些最小或自定义工具配置文件可能暴露 sessions_spawn 和 subagents 但不暴露 sessions_yield;此时不要发明轮询循环。
当存在活跃子节点时,OpenClaw 在正常轮次中注入紧凑运行时生成的 Active Subagents 提示块,因此发起方可以无需轮询地看到当前子会话、运行 id、状态、标签、任务和 taskName 别名。该块中的任务和标签字段作为数据引用,而非指令,因为它们可能来自用户/模型提供的派生参数。
工具:subagents
列出、引导或杀死属于发起会话的已派生子代理运行。范围限于当前发起方;子代理只能查看/控制其自己的受控子节点。
使用 subagents 进行按需状态检查、调试、引导或杀死。使用 sessions_yield 等待完成事件。
线程绑定会话
当频道启用线程绑定时,子代理可以保持绑定到某个线程,使该线程中的后续用户消息继续路由到同一子代理会话。
支持线程的频道
Discord 目前是唯一支持的频道。它支持持久线程绑定子代理会话(sessions_spawn 加 thread: true)、手动线程控制(/focus、/unfocus、/agents、/session idle、/session max-age),以及适配器键 channels.discord.threadBindings.enabled、channels.discord.threadBindings.idleHours、channels.discord.threadBindings.maxAgeHours、channels.discord.threadBindings.spawnSubagentSessions。
快速流程
- 派生:使用
sessions_spawn派生,设置thread: true(可选mode: "session")。 - 绑定:OpenClaw 在活动频道中创建或绑定一个线程到该会话目标。
- 路由后续:该线程中的回复和后续消息路由到绑定的子代理会话。
- 检查超时:使用
/session idle检查/更新不活跃自动解绑,/session max-age控制硬性上限。 - 解除:使用
/unfocus手动解除绑定。
手动控制
| 命令 | 效果 |
|---|---|
/focus <target> | 将当前线程(或创建一个)绑定到子代理/会话目标 |
/unfocus | 移除当前绑定线程的绑定 |
/agents | 列出活跃运行和绑定状态(thread:<id> 或 unbound) |
/session idle | 检查/更新不活跃自动解绑(仅聚焦绑定线程) |
/session max-age | 检查/更新硬性上限(仅聚焦绑定线程) |
配置开关
- 全局默认:
session.threadBindings.enabled、session.threadBindings.idleHours、session.threadBindings.maxAgeHours。 - 频道覆盖和派生自动绑定键:适配器特定,见上述支持线程的频道。
白名单
agents.list[].subagents.allowAgents:可通过agentId指定的已配置代理 id 列表(["*"]允许任意已配置目标)。默认仅发起代理。如果你设置列表并且还希望发起方使用agentId派生自己,在列表中包含发起方 id。agents.defaults.subagents.allowAgents:当发起代理未设置自己的subagents.allowAgents时使用的默认目标代理白名单。agents.defaults.subagents.requireAgentId(布尔值,默认false):阻止省略agentId的sessions_spawn调用(强制明确选择配置文件)。每代理覆盖:agents.list[].subagents.requireAgentId。agents.defaults.subagents.announceTimeoutMs(数字,默认120000):每次agent通告投递尝试的超时时间(毫秒)。值必须是正整数毫秒,并限制到平台安全定时器最大值。瞬态重试可能使总通告等待时间超过一次配置超时。
如果发起会话已沙箱化,sessions_spawn 拒绝在非沙箱中运行的目标。
发现
使用 agents_list 查看当前 sessions_spawn 允许的代理 id。响应包含每个列出代理的有效模型和嵌入的运行时元数据,以便调用方区分 PI、Codex app-server 和其他已配置的本机运行时。
allowAgents 条目必须指向 agents.list[] 中已配置的代理 id。["*"] 表示任意已配置目标代理加上发起方。如果代理配置被删除但 id 仍在 allowAgents 中,sessions_spawn 拒绝该 id 并且 agents_list 将其省略。运行 openclaw doctor --fix 清理过期的白名单条目。
自动归档
- 子代理会话在
agents.defaults.subagents.archiveAfterMinutes(默认60)后自动归档。 - 归档使用
sessions.delete,并将转录文件重命名为*.deleted.<timestamp>(同一文件夹)。 cleanup: "delete"在通告后立即归档(仍通过重命名保留转录)。- 自动归档是尽力而为的;网关重启后待处理的定时器会丢失。
runTimeoutSeconds不自动归档;它只停止运行,会话保留至自动归档。- 自动归档同等适用于深度 1 和深度 2 的会话。
- 浏览器清理与归档清理独立:跟踪的浏览器标签/进程在运行结束时尽力关闭,即使转录/会话记录被保留。
嵌套子代理
默认情况下,子代理不能派生自己的子代理(maxSpawnDepth: 1)。设置 maxSpawnDepth: 2 可启用一级嵌套,允许编排模式:主 → 编排子代理 → 工作子子代理。
json5
{
agents: {
defaults: {
subagents: {
maxSpawnDepth: 2, // 允许子代理派生子代理(默认:1)
maxChildrenPerAgent: 5, // 每代理会话最大活跃子代理数(默认:5)
maxConcurrent: 8, // 全局并发通道上限(默认:8)
runTimeoutSeconds: 900, // sessions_spawn 省略时的默认超时(0 = 无超时)
announceTimeoutMs: 120000, // 每次 gateway 通告超时
},
},
},
}深度层级
| 深度 | 会话键形状 | 角色 | 可以派生? |
|---|---|---|---|
| 0 | agent:<id>:main | 主代理 | 始终可以 |
| 1 | agent:<id>:subagent:<uuid> | 子代理(深度 2 允许时充当编排者) | 仅当 maxSpawnDepth >= 2 |
| 2 | agent:<id>:subagent:<uuid>:subagent:<uuid> | 子子代理(叶工作节点) | 从不 |
通告链
结果沿链条向上传递:
- 深度 2 工作节点完成 → 向其父节点(深度 1 编排者)通告
- 深度 1 编排者收到通告,综合结果,完成 → 向主代理通告
- 主代理收到通告并投递给用户
每个层级只看到来自其直接子代理的通告。
::: note 操作指导 启动子任务一次后等待完成事件,而非围绕 sessions_list、sessions_history、/subagents list 或 exec sleep 命令构建轮询循环。sessions_list 和 /subagents list 将子会话关系聚焦于活跃工作——活跃子节点保持连接,已结束的子节点在短暂近期窗口内可见,过期的仅存储子链接在其新鲜度窗口后忽略。这防止旧的 spawnedBy / parentSessionKey 元数据在重启后复活幽灵子节点。如果子完成事件在你已发送最终答案之后到达,正确的后续是精确的静默令牌 NO_REPLY / no_reply。 :::
按深度的工具策略
- 角色和控制范围在派生时写入会话元数据,防止扁平或恢复的会话键意外重获编排权限。
- 深度 1(编排者,当
maxSpawnDepth >= 2):获取sessions_spawn、subagents、sessions_list、sessions_history以便管理其子代理。其他会话/系统工具仍被拒绝。 - 深度 1(叶节点,当
maxSpawnDepth == 1):无会话工具(当前默认行为)。 - 深度 2(叶工作节点):无会话工具——
sessions_spawn在深度 2 始终被拒绝,不能派生更多子节点。
每代理派生限制
每个代理会话(任意深度)最多可以同时有 maxChildrenPerAgent(默认 5)个活跃子代理,防止单个编排者失控扩张。
级联停止
停止深度 1 编排者会自动停止其所有深度 2 子代理:
- 在主聊天中发送
/stop停止所有深度 1 代理并级联到其深度 2 子代理。 /subagents kill <id>停止特定子代理并级联到其子代理。/subagents kill all停止发起方的所有子代理并级联。
认证
子代理认证按代理 id 解析,而非会话类型:
- 子代理会话键为
agent:<agentId>:subagent:<uuid>。 - 认证存储从该代理的
agentDir加载。 - 主代理的认证配置作为回退被合并进来;代理配置在冲突时覆盖主配置。
合并是附加式的,主配置始终作为回退可用。目前尚不支持每代理完全隔离认证。
通告
子代理通过通告步骤回报结果:
- 通告步骤在子代理会话内部运行(而非发起方会话)。
- 若子代理回复恰好为
ANNOUNCE_SKIP,则不发布任何内容。 - 若最新 assistant 文本恰好为静默令牌
NO_REPLY/no_reply,即使之前有可见进度,通告输出也会被抑制。 - 投递取决于发起方深度:
- 顶层发起方会话使用带外部投递的后续
agent调用(deliver=true) - 嵌套发起方子代理会话收到内部后续注入(
deliver=false),使编排者可在会话内综合子结果 - 若嵌套发起方子代理会话已消失,OpenClaw 在可用时回退到该会话的发起方
- 顶层发起方会话使用带外部投递的后续
- 对于顶层发起方会话,完成模式直接投递首先解析任何绑定的会话/线程路由和钩子覆盖,然后从发起方会话存储的路由中填充缺失的频道目标字段,确保完成通告落到正确的聊天/话题。
- 子完成聚合的范围限于当前发起方运行,防止陈旧的先前运行子输出泄漏到当前通告中。
- 通告回复在频道适配器可用时保留线程/话题路由。
通告上下文
通告上下文规范化为稳定的内部事件块:
| 字段 | 来源 |
|---|---|
| Source | subagent 或 cron |
| Session ids | 子会话键/id |
| Type | 通告类型 + 任务标签 |
| Status | 从运行时结果派生(success、error、timeout、unknown),不从模型文本推断 |
| Result | 最新可见 assistant 文本,否则 sanitized 的最新工具/工具结果文本 |
| Follow-up | 描述何时回复与保持沉默的指令 |
终端失败的运行报告失败状态,不重放捕获的回复文本。超时时,若子代理仅完成了工具调用,通告可将历史折叠为简短的部分进度摘要。
统计行
通告载荷末尾包含统计行:
- 运行时(如
runtime 5m12s) - Token 用量(输入/输出/总计)
- 配置了模型定价时的估算费用(
models.providers.*.models[].cost) sessionKey、sessionId和转录路径(主代理可通过sessions_history获取历史或在磁盘上检查文件)
内部元数据仅用于编排;面向用户的回复应以正常助手语气重写。
为什么推荐 sessions_history
sessions_history 是更安全的编排路径:
- Assistant 回放在规范化前剥离:thinking 标签、
<relevant-memories>/<relevant_memories>脚手架、纯文本工具调用 XML 载荷(<tool_call>、<function_call>、<tool_calls>、<function_calls>,包括未正常关闭的截断载荷)、降级的工具调用/结果脚手架、历史上下文标记、泄漏的模型控制 token(<|assistant|>、其他 ASCII<|...|>、全角<|...|>变体)、格式错误的 MiniMax 工具调用 XML。 - 凭证/token 类文本被脱敏。
- 长块可能被截断。
- 非常大的历史记录可能丢弃较旧的行,或用
[sessions_history omitted: message too large]替换过大的行。 - 磁盘上的原始转录是需要完整字节级转录时的回退方案。
工具策略
子代理使用与父代理或目标代理相同的配置文件与工具策略管道。之后 OpenClaw 应用子代理限制层。
无限制的 tools.profile 时,子代理获取除会话工具和系统工具之外的所有工具:
sessions_listsessions_historysessions_sendsessions_spawn
当 maxSpawnDepth >= 2 时,深度 1 编排子代理另外获取 sessions_spawn、subagents、sessions_list 和 sessions_history 以便管理其子代理。
通过配置覆盖
json5
{
agents: {
defaults: {
subagents: {
maxConcurrent: 1,
},
},
},
tools: {
subagents: {
tools: {
// deny 优先
deny: ["gateway", "cron"],
// 若设置 allow,变为仅允许模式(deny 仍优先)
// allow: ["read", "exec", "process"]
},
},
},
}tools.subagents.tools.allow 是最终仅允许过滤器。它可以缩小已解析的工具集,但不能加回被 tools.profile 移除的工具。例如,tools.profile: "coding" 包含 web_search/web_fetch 但不包含 browser 工具。要让 coding-profile 子代理使用浏览器自动化,在配置文件阶段添加 browser:
json5
{
tools: {
profile: "coding",
alsoAllow: ["browser"],
},
}使用每代理 agents.list[].tools.alsoAllow: ["browser"] 当只有一个代理应获得浏览器自动化时。
并发
子代理使用专用进程内队列通道:
- 通道名称:
subagent - 并发:
agents.defaults.subagents.maxConcurrent(默认8)
活跃度与恢复
OpenClaw 不将 endedAt 缺失视为子代理仍然存活的永久证据。未结束且超过陈旧运行窗口的运行在 /subagents list、状态摘要、后代完成门控和每会话并发检查中不再计为活跃/待处理。
网关重启后,陈旧的未结束已恢复运行将被修剪,除非其子会话标记了 abortedLastRun: true。这些重启中止的子会话可通过子代理孤儿恢复流程恢复,该流程在清除中止标记前发送一条综合恢复消息。
自动恢复重启对每个子会话是有界的。如果同一子代理子节点在快速重新楔入窗口内重复被接受进行孤儿恢复,OpenClaw 在该会话上持久化一个恢复墓碑,并停止在以后重启时自动恢复它。运行 openclaw tasks maintenance --apply 协调任务记录,或 openclaw doctor --fix 清理墓碑会话上的陈旧中止恢复标志。
::: note 如果子代理派生失败并返回 Gateway PAIRING_REQUIRED / scope-upgrade,在编辑配对状态前检查 RPC 调用方。内部 sessions_spawn 协调应连接为 client.id: "gateway-client" 和 client.mode: "backend",通过直接环回共享 token/密码认证;该路径不依赖 CLI 配对设备范围基线。远程调用方、显式 deviceIdentity、显式设备 token 路径以及浏览器/节点客户端仍需正常设备批准进行范围升级。 :::
停止
- 在发起方聊天中发送
/stop会中止发起方会话,并停止从中派生的所有活跃子代理运行,级联到嵌套子代理。 /subagents kill <id>停止特定子代理并级联到其子代理。
限制
- 子代理通告是尽力而为的。如果网关重启,待处理的"通告回"工作会丢失。
- 子代理仍共享同一网关进程资源;将
maxConcurrent视为安全阀。 sessions_spawn始终是非阻塞的:立即返回{ status: "accepted", runId, childSessionKey }。- 子代理上下文仅注入
AGENTS.md和TOOLS.md(无SOUL.md、IDENTITY.md、USER.md、MEMORY.md、HEARTBEAT.md或BOOTSTRAP.md)。 - 最大嵌套深度为 5(
maxSpawnDepth范围:1–5)。大多数用例推荐深度 2。 maxChildrenPerAgent限制每会话的活跃子代理数(默认5,范围1–20)。
常见问题
派生子代理后需要轮询状态吗?
不需要。sessions_spawn 是非阻塞的,完成时会自动推送通告。不要用 sleep 循环轮询 sessions_list 或 sessions_history——使用 sessions_yield 等待完成事件即可。如果在主代理已发送最终答案后收到子代理完成通告,用静默令牌 NO_REPLY 不回复即可。
子代理的工具权限怎么限制?
通过 tools.subagents.tools.deny 指定拒绝的工具列表,或通过 tools.subagents.tools.allow 设置仅允许列表。deny 始终优先。注意即使 allow 列表中列了 sessions_spawn,深度 2 子代理仍然无权使用该工具。每代理可通过 agents.list[].tools.alsoAllow 进行个性化控制。
子代理会话多久自动清理?
子代理会话在 agents.defaults.subagents.archiveAfterMinutes(默认 60 分钟)后自动归档。归档会重命名转录文件。runTimeoutSeconds 只停止运行,不触发归档。网关重启后待处理的定时器会丢失,因此长时间运行子代理最好设置合理的超时。