什么时候值得用 Subagent#
适合:
- 可独立并行探索多个模块;
- 需要隔离大量中间上下文;
- 不同角色有不同工具权限;
- 一个任务可以定义清晰输入输出契约;
- 失败可局部重试,不污染主轨迹。
不适合:
- 子任务高度共享状态、频繁同步;
- 任务很小,通信成本大于收益;
- 多个 Agent 会编辑相同文件;
- 没有独立 verifier,只是“多叫几个人想想”;
- 只是为了追逐架构潮流。
Orchestrator–Worker 模式#
子任务契约:
objective: 找出 token refresh 竞态的最小根因
scope:
read:
- src/auth/**
- tests/auth/**
write: []
deliverable:
- 根因
- 证据路径和行号
- 最小复现建议
budget:
max_steps: 20
timeout_minutes: 8
主 Agent 不应把完整子轨迹全部塞回上下文,只接收结果摘要、证据引用、状态和 usage;需要时可按句柄展开。
多 Agent 如何避免写冲突?#
首选把写集按目录/任务分区;需要同时写时使用独立 worktree/branch,让主 Agent 审查并合并 patch。调度器基于声明和实际 file-set 做冲突检测。共享单工作区且并发编辑是最后选择,必须有版本检查和三方合并。Git index 等全局状态不能无保护共享。
如何防止 Subagent 失控或递归爆炸?#
- 限制最大深度、fan-out、总并发和总预算;
- 只有特定 Agent 有 spawn 权限;
- 子 Agent 继承不超过父 Agent 的权限;
- 每个任务有 deadline、取消传播和心跳;
- 主任务结束前回收后台任务;
- 汇总 usage 与成本到父任务;
- 结构化返回,禁止无边界聊天;
- 全局 scheduler 做公平性和背压。
模型路由#
路由维度:
- 任务难度与风险;
- 上下文长度;
- 工具调用可靠性;
- 延迟/成本;
- 多模态需求;
- 数据合规;
- provider 健康状态。
不要只用一个分类器“猜难度”。可以先用低成本模型执行,出现特定信号再升级:
- 多次无进展;
- verifier 失败;
- 跨模块复杂任务;
- 高风险决策;
- 小模型工具 schema 错误率高。
同一 session 中途切模型有什么坑?#
- 不同 provider 消息角色和 tool-call 格式不同;
- tool call ID、thinking block、签名可能不兼容;
- tokenizer 与 context limit 不同;
- 对 system prompt 和并行工具支持不同;
- 历史消息可能需规范化/修复;
- 行为变化会破坏恢复可重复性。
应保存规范化内部 IR,再由 provider adapter 编码;trace 中记录实际模型与 adapter 版本。
Model Gateway 与 Prompt / Tool 兼容层#
内部统一表示#
建议统一:
Message:
role
content_blocks[text | image | tool_call | tool_result | reasoning_ref]
provenance
timestamp
ToolCall:
id
name
arguments
ModelResponse:
blocks
finish_reason
usage
latency
provider_request_id
adapter 负责:
- 请求编码和流式事件归一化;
- tool schema 差异;
- usage 与 finish reason 统一;
- provider 错误分类;
- 最大输入/输出预算;
- 重试与幂等边界;
- history repair;
- 能力声明。
流式解析#
模型可能分片输出:
- 文本 token;
- reasoning;
- tool name;
- tool arguments 的增量 JSON;
- usage;
- finish reason。
Runtime 应在流结束后对 arguments 做最终 schema 验证。不要边收到半个 JSON 边执行。若连接中断,不能假设工具调用完整;记录 interrupted response 并依据 provider 的幂等能力决定是否重试。
缓存#
可缓存:
- 稳定 system/tool 前缀的 provider prompt cache;
- repo index 与文件摘要;
- 不变的工具资源;
- 相同内容 hash 的 embedding;
- eval replay 的确定性工具结果。
缓存键必须包含:
model + provider + prompt/tool version + content hash
+ permission/scope + repo revision + relevant config
缓存最危险的不是 miss,而是错误 hit:把旧分支代码、旧权限或旧工具描述带入当前任务。
Temperature 设为 0,Agent 是否可复现?#
不能保证。provider 后端、模型版本、采样实现、并行工具时序、搜索结果、文件状态都会变化。评测要固定可控变量、记录版本并多次运行;调试可以用 trace replay 隔离模型和环境。目标通常是统计稳定性,而非逐 token 一致。
Subagent 的收益来自隔离,不来自数量#
我判断是否拆 Subagent 时,会先估算三项成本:
收益 = 被隔离的无关上下文 + 可并行的等待时间 + 专用权限带来的安全性
成本 = 任务描述损耗 + 结果汇总损耗 + 额外模型调用 + 并发冲突风险
例如调查一次跨 auth、billing、gateway 三个模块的超时问题,三个只读 explore Agent 并行通常合理:它们各自读取大量日志和调用链,最后只返回证据。若任务是修改一个 80 行函数,拆成“分析 Agent、编码 Agent、审查 Agent”往往只是把一次本可连贯完成的推理切碎。
一个可执行的子任务契约#
自然语言 prompt 不应是唯一契约。调度器还应持有结构化元数据:
task_id: investigate-auth-refresh
goal_revision: 7
objective: 解释刷新请求为什么在高并发下重复发送
inputs:
workspace_revision: 91e83d
evidence:
- artifact://trace/refresh-timeout
scope:
read: [src/auth/**, tests/auth/**]
write: []
capabilities: [read_file, search_text, run_readonly_test]
deliverable_schema:
root_cause: string
evidence_refs: list
confidence: low | medium | high
unresolved: list
budget:
max_turns: 16
max_tokens: 60000
deadline_seconds: 480
返回结果必须绑定 workspace_revision 和 goal_revision。如果主 Agent 在子任务运行期间已经修改相关文件,结果不能直接当作当前事实,只能作为历史线索重新验证。
并发调度要看实际 effect,而不是 Agent 名字#
两个 explore Agent 通常只读,但它们仍可能同时启动构建、占用同一端口或污染共享缓存;两个 coder Agent 也可能因为独立 worktree 而完全不冲突。因此调度器应组合静态声明与运行时观测:
declared_effects = task contract + tool capabilities
observed_effects = 实际打开的路径、进程、端口、Git 状态
conflict = overlap(declared, observed, active_tasks)
发现未声明写入时,最安全的处理不是事后合并,而是立刻暂停该子任务并记录 policy violation。并发上限还要同时受 provider 配额、CPU、内存和用户级公平调度约束,不能只设一个 max_agents=8。
Gateway 的核心不是换 URL,而是保存语义#
模型 provider 的协议差异常被低估。一个内部 tool_result 在不同接口里可能要求:
- 紧邻对应 tool call;
- 使用特定 role;
- 保留或删除中断的 reasoning block;
- tool call ID 满足特定格式;
- 并行调用按原顺序回填;
- 图片、缓存标记和系统指令放在不同位置。
因此 adapter 不只是字段重命名。它应执行一套可版本化转换:
Internal IR
→ capability negotiation
→ history validation / repair
→ provider-specific encoding
→ streamed block assembly
→ finish reason normalization
→ usage and error normalization
我会把“修复历史”限制为保持语义的操作,例如为已明确中断的调用补一个 interrupted result。若无法确认 call/result 对应关系,Gateway 应拒绝请求,而不是猜测一个看起来能通过 provider 校验的历史。
切换模型时应该重建哪些东西#
同一 session 从模型 A 切到模型 B,至少重新计算:
| 项目 | 原因 |
|---|---|
| token 预算 | tokenizer 与窗口不同 |
| tool schema 集合 | provider 能力与 schema 限制不同 |
| system/tool 稳定前缀 | prompt cache 边界不同 |
| 并行调用策略 | 有的模型更容易生成依赖冲突 |
| 历史消息编码 | role、block 和 call/result 约束不同 |
| completion policy 阈值 | 模型自报完成的可靠性可能不同 |
但是任务目标、权限、workspace revision 和历史副作用不能随模型变化。模型是可替换决策器,Runtime 状态不是它的私有记忆。
Kimi Code 的公开文档显示,Subagent 使用独立上下文,只将最终结果带回主 Agent;session 中每个 Agent 也有独立事件流用于恢复和 replay。这类设计的价值正是把上下文污染和运行状态分开,而不是简单增加并行度。参见 Agents and Sub-Agents 与 Sessions and context。