跳转到内容

03 — 子 Agent 角色分类:并发执行与上下文 Fork

子 Agent 角色分类:并发执行与上下文 Fork

学习目标

理解 CodeWhale 的子 Agent 角色分类学:6 种角色 + 自定义模式,输出契约、并发控制与上下文 Fork 策略。

前置知识

下文假设你已理解上述概念,直接聚焦 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 引用与关键发现;每条一个 bullet
RISKS: 可能出错的地方 / 父 Agent 应复核的事项
BLOCKERS: 阻碍你完成的事项;"None." 表示正常完成

格式定义在 crates/tui/src/prompts/subagent_output_format.md。父 Agent 直接从 EVIDENCE 段读取工作集用于下一轮推理。

上下文 Fork 策略

agent_open 默认启动 fresh session(子 Agent 只收到角色提示 + 任务描述)。

fork_context: true 时:

  1. 保持父的预填/提示前缀字节级一致(DeepSeek 前缀缓存复用)
  2. 追加结构化状态快照
  3. 在尾部添加子 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_handleNone 的 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 — 输出契约定义