03 — 子 Agent 角色分类:并发执行与上下文 Fork
子 Agent 角色分类:并发执行与上下文 Fork
学习目标
理解 CodeWhale 的子 Agent 角色分类学:6 种角色 + 自定义模式,输出契约、并发控制与上下文 Fork 策略。
前置知识
- 子 Agent 委派模式 — 子 Agent 的隔离上下文与并行执行通用原理
下文假设你已理解上述概念,直接聚焦 CodeWhale 的具体实现。
项目实践
角色分类表
| 角色 | 写入? | Shell 策略 | 典型使用 |
|---|---|---|---|
general | 是 | 是 | 默认,多步任务 |
explore | 否 | 只读 | ”查找 Foo 的所有调用点” |
plan | 最小 | 最小 | ”设计迁移方案,不执行” |
review | 否 | 只读 | ”审计这个 PR 的 bug” |
implementer | 是 | 是 | ”重写 bar.rs::Foo::bar” |
verifier | 否 | 测试导向 | ”运行 cargo test —workspace” |
custom | 取决于 allowlist | 取决于 allowlist | 锁定的工具集派发 |
生命周期
输出契约
每个子 Agent 的最终结果字符串包含五个固定段:
SUMMARY: 一段话概述做了什么、结果如何CHANGES: 修改的文件及一行描述;只读角色写 "None."EVIDENCE: path:line-range 引用与关键发现;每条一个 bulletRISKS: 可能出错的地方 / 父 Agent 应复核的事项BLOCKERS: 阻碍你完成的事项;"None." 表示正常完成格式定义在 crates/tui/src/prompts/subagent_output_format.md。父 Agent 直接从 EVIDENCE 段读取工作集用于下一轮推理。
上下文 Fork 策略
agent_open 默认启动 fresh session(子 Agent 只收到角色提示 + 任务描述)。
fork_context: true 时:
- 保持父的预填/提示前缀字节级一致(DeepSeek 前缀缓存复用)
- 追加结构化状态快照
- 在尾部添加子 Agent 角色指令和任务
何时用哪种:
- Fresh session:独立的探索、不依赖父的决策上下文
- Forked session:任务依赖父会话中已有的决策、文件、todo 或 plan 状态
并发控制
- 默认上限:10 个并发子 Agent
- 硬上限:20(通过
[subagents].max_concurrent配置) - 触顶时
agent_open返回错误,附带当前上限值 - 只统计 running 状态的 Agent——completed/failed/cancelled 不占槽位
- 丢失
task_handle的 Agent(如进程重启后)不占槽位
每步 API 超时
每个子 Agent 的 DeepSeek create_message 调用包裹了每步超时:
- 默认 120 秒
- 可通过
[subagents] api_timeout_secs配置(范围 1-1800 秒) - 长思考的子 Agent(如 review 或 plan)可以设置到 900 秒(15 分钟)
实现细节
- 源码:
crates/tui/src/tools/subagent/mod.rs - 持久化状态:
<workspace>/.deepseek/state/subagents.v1.json - 每个
SubAgentManager实例分配session_boot_id,用于区分当前会话与历史会话 SharedSubAgentManager使用Arc<RwLock<...>>——读路径用读锁,不阻塞主循环的多 Agent fan-out- 父 turn 取消不杀死已启动的子 Agent 会话(使用独立 cancellation token)
问题与规避
陷阱:子 Agent 过多导致上下文混乱
当同时打开 10 个子 Agent 时,父 Agent 需要整合 10 份结果。如果结果不结构化,整合成本极高。
规避:五段式输出契约确保每份结果有统一格式。父 Agent 直接从 EVIDENCE 段提取关键发现,无需阅读完整输出。
陷阱:Fork 模式下的缓存失效
如果父会话的前缀在 fork 后发生变化,子 Agent 的前缀缓存可能失效。
规避:fork 时保持父前缀字节级一致。但如果父在 fork 后修改了系统提示,后续 fork 需要重新建立缓存。
陷阱:进程重启后的幽灵 Agent
进程重启后,持久化状态文件中可能有标记为 “Running” 的 Agent,但实际 task_handle 已丢失。
规避:is_running 检查忽略 task_handle 为 None 的 Agent。这些 Agent 被归类为 Interrupted 状态,父可以选择重新打开或当作终态。
设计取舍
为什么是 6 种角色而不是更多
CodeWhale v0.8.35 移除了 swarm Agent 系统,精简为 6 种角色 + custom。原因是:
- 太多角色增加模型的认知负担(选择哪个角色)
- 6 种角色覆盖了主要的 Agent 工作模式:执行/探索/规划/审计/实现/验证
custom作为安全阀,允许精确的工具集约束- 每个角色有别名支持(如
explorer/exploration都映射到explore),降低模型的拼写错误率
为什么子 Agent 不继承父的完整上下文
默认 fresh session 的设计考量:
- 子 Agent 需要聚焦,而不是被父的完整转记分心
- 每个 fresh session 的 token 成本可控(Flash 约 $0.14/M input)
- 当需要上下文时,
fork_context: true提供精确的状态快照,而非完整转记
参考来源
docs/SUBAGENTS.md— 子 Agent 角色分类与生命周期文档crates/tui/src/tools/subagent/mod.rs— 子 Agent 管理器源码crates/tui/src/prompts/subagent_output_format.md— 输出契约定义