作用
Subagent 让主 agent 把「可独立处理、上下文重、可并行」的任务委派出去,避免主线程膨胀。每个 subagent 都是一个临时
HarnessAgent(或 remote stub)实例,拥有独立子会话,最终通过工具结果回传。
何时启用
当HarnessAgent.build() 满足以下条件时,才会装载 subagent 能力:
- 当前 agent 不是 leaf subagent
- 没有
disableSubagents() - 已配置
model
SubagentsHook(priority=80),并通过 hook 暴露:
agent_spawn/agent_send/agent_listtask_output/task_cancel/task_list
PreReasoningEvent,SubagentsHook 会向 SYSTEM 注入:
- 子 agent 使用规则
- 当前可用
agent_id列表 - 当前 session 的异步任务摘要(最多 10 条)
声明来源
buildSubagentEntries(...) 会合并四类来源:
- 内置
general-purpose - 编程声明:
builder.subagent(SubagentDeclaration) - 文件声明:
workspace/subagents/*.md(AgentSpecLoader非递归加载) - 自定义工厂:
builder.subagentFactory(name, factory)或builder.subagentFactory(name, description, factory),后者可为编排器提供有意义的描述而非仅有名称(省略或为空白时回退为名称)
声明模型(SubagentDeclaration)
SubagentDeclaration 支持 3 种互斥来源模式:
- Definition workspace 模式
workspace(path)指向定义目录(通常含AGENTS.md)
- Inline 模式
inlineAgentsBody(...)直接作为系统提示词 base
- Remote HTTP 模式
url(...)+ 可选headers(...),通过 task protocol 走远端执行
build() 校验):
url不能与workspace或非空inlineAgentsBody同时出现workspace与非空inlineAgentsBody不能同时出现
运行时工作区五行判定表
WorkspaceMode 决定 runtime workspace root:
补充:
tools是继承工具的 allowlist:仅过滤父 toolkit,不影响子 agent 后续自动注册的本地工具- 同一个 definition workspace 可被多个声明复用
workspace.path相对路径会按mainWorkspace.resolve(...).normalize()解析
声明文件(workspace/subagents/<id>.md)
文件名(去掉 .md)就是 agent_id,不从 front matter 读取 name。
AgentSpecLoader):
- 必填:
description - 仅扫描
subagents/目录第一层.md文件(非递归) - 如果配置了
workspace.path且 body 非空:会记录 warning,body 被忽略 - markdown 声明当前不解析
url/headers(remote 声明建议走编程 API)
编程式配置
内置 general-purpose
内置 general-purpose 不需要写声明文件,会始终加入 entry 列表。它的目标是「能力镜像主 agent」,核心行为:
- 共享主 workspace(
SHARED语义) - 继承并镜像主 agent 的:
- toolkit(父工具)
- hooks
- execution config
- compaction / toolResultEviction
- additional context files / maxContextTokens
- 各类 disable 开关
- 固定是 leaf subagent(不能继续 spawn)
防递归与深度保护
双保险:- 所有通过声明/内置生成的子 agent 都会
asLeafSubagent(),leaf 不再注册SubagentsHook AgentSpawnTool还有动态深度上限MAX_SPAWN_DEPTH = 3
RuntimeContext 透传
agent_spawn / agent_send 调用子 agent 时:
- 子会话
session_id为新值(sub-<uuid>) userId从父RuntimeContext透传给子RuntimeContext
调用工具与关键参数
注意:
agent_send的agent_key必须使用agent_spawn返回值中的完整agent_key: ...(不是agent_id/session_id/task_id)- 异步任务刚创建时不要立即轮询;优先先返回用户,再用
task_output(block=false)或task_list查最新状态
异步任务生命周期与存储
默认情况下,主 agent 使用WorkspaceTaskRepository(除非显式 taskRepository(...) 覆盖)。
生命周期(简化):
putTask(...)写入TaskRecord(PENDING)到 workspace- 提交本地执行 future(local 或 remote)
- 执行中更新为
RUNNING - 结束写入
COMPLETED / FAILED / CANCELLED
- 内存层:
localTasks(本节点加速句柄,重启丢失) - 持久层:
agents/<parentAgentId>/tasks/<sessionId>.json(状态真源)
分布式语义
- 任务执行粘在创建节点,但任意节点都可通过 workspace 读取状态
task_output(block=true)在跨节点场景下会优雅降级,不会无休止阻塞task_cancel会把cancelRequested=true持久化;执行节点轮询该标记后中止- orphan sweeper 会把长时间无心跳的本地任务标记为
FAILED(remote transport 任务不走该判定)
Remote subagent 行为
当声明配置url(...) 时:
- 工厂返回
RemoteSubagentStub(占位,不做本地真实推理) - 实际执行通过
TaskRunSpec.RemoteTaskRunSpec+AgentProtocolTaskClient委派到远端 task HTTP 服务 - 可同步(
timeout_seconds>0)或异步(timeout_seconds=0)
实践建议
description要写清「何时使用 / 输出格式 / 禁止事项」,这是主模型是否委派的关键依据- 子 agent
maxIters通常设得比主 agent 小,避免子线程吞噬过多 token - 会话压缩或恢复后,先用
task_list()恢复任务全量状态,再做单任务查询