很多人使用 Codex 时,习惯把所有事情塞进一个会话:先让它读代码,再让它改接口,接着补测试,最后还要写文档。任务少的时候这样做很顺手,但一旦项目变大,问题就会出现:上下文越来越长,任务之间互相覆盖,某一步失败后很难判断是哪里出了问题,多个方向也无法真正同时推进。
多 Agent 编排解决的不是“多开几个聊天窗口”这么简单。它真正解决的是:如何把一个目标拆成边界清楚的工作单元,如何让不同 Agent 在合适的环境中执行,如何收集过程证据,以及如何判断最终结果是否可以交付。本文以 Codex 的 Task、Subagent、Worktree 等概念为例,给出一套适合个人开发者和小团队的拆分方法。
一、先判断任务是否值得拆分
并不是所有任务都适合多 Agent。一个只涉及一两个文件、步骤之间高度依赖、需要连续修改的任务,交给一个 Task 往往更简单。拆分前可以先问四个问题:
- 任务是否包含两个以上互相独立的工作方向?
- 不同方向是否可以在没有实时沟通的情况下完成?
- 每个方向是否都能定义明确的输入、输出和验收标准?
- 任务之间是否有文件冲突或严格的先后依赖?
如果前两个问题的答案是否定的,不必为了“使用 Agent”而拆分。如果答案是肯定的,再继续判断是使用同一个 Task 里的 Subagent,还是创建多个持久 Task。
一个实用的拆分单位不是“一个人负责前端、一个人负责后端”这种模糊角色,而是一个能独立验收的结果。例如:
错误处理调查:列出 auth、billing、search 三个模块中未覆盖的异常路径,并给出 file:line 证据。
接口迁移方案:比较三种兼容策略,输出一份包含约束、迁移顺序和回滚方案的设计记录。
测试缺口扫描:只检查本次改动涉及的路径,输出缺失测试及其复现步骤。
这三个任务的共同点是结果可以单独检查。它们不是简单地把一句大任务切成三段,而是把目标转换成三个可验证的交付物。
二、理解 Codex 中几个容易混淆的对象
编排之前,先把对象边界分清楚。不同 Codex 表面和版本的具体功能可能会调整,使用前应以当前界面、命令帮助和官方文档为准,但下面这组概念可以作为稳定的设计模型。
Project 和 Host:工作域与执行环境
Project 表示一个保存下来的项目工作域,可以关联本地目录,也可以关联 SSH 主机上的项目。Host 则表示任务实际运行的位置,例如本机或某一台远程开发机。
它们解决的是“代码和运行环境在哪里”的问题。一个任务如果需要内网服务、特定编译器或浏览器,就不能只看 Agent 的角色,还要把它派到拥有这些条件的 Host 上。
Task 和 Turn:持久角色与一次运行
Task 是一条可以持续存在的会话。它有自己的历史、目标和运行上下文,适合承担长期角色,例如项目 Supervisor、发布验证员或某个仓库的维护者。
Turn 是一次输入及其后续工作。一个 Task 可以有很多 Turn。把“任务”和“一次运行”混为一谈,会导致状态管理混乱:一次 Turn 失败,不等于整个 Task 失败;一个 Task 暂停,也不代表它的产物已经完成。
Subagent:任务内部的短生命周期工作者
Subagent 是某个 Task 临时派生出来的独立 Agent。它适合做范围明确、读写量有限、完成后只需要返回结论的工作,例如扫描一组日志、检查一个模块或对一份补丁做独立评审。
Subagent 的优点是轻量,父 Task 可以只接收摘要。缺点是它通常不适合作为长期角色,也不应该承担需要跨多个阶段反复恢复的工作。
Worktree:并行写入时的隔离边界
Worktree 是同一个 Git 仓库的独立工作树。多个候选方案如果都要修改文件,应该各自在不同 Worktree 中运行,否则一个 Agent 的未提交改动可能被另一个 Agent 看到或覆盖。
Worktree 不是“多一个会话”这么简单,它提供的是文件系统隔离。Git 分支仍然有自己的约束:同一分支不能同时被多个工作树检出。创建并行候选时,要给每个候选分配不同分支,或者让工具使用独立的临时工作树。
三、三种最常用的编排拓扑
1. Supervisor:一个会话负责拆解和收口
Supervisor 模式适合多个方向同时推进,但你希望只和一个入口沟通的情况。Supervisor 不一定亲自改代码,它的主要职责是建立任务清单、选择执行者、跟踪状态、处理阻塞并汇总结果。
用户目标
-> Supervisor 明确范围和验收标准
-> 创建或发现 Worker Task
-> 为每个 Worker 派发结构化任务
-> 等待结果并检查证据
-> 处理失败、冲突和依赖
-> 输出最终结论或交给实现任务
Supervisor 提示词不应只有“帮我完成这个项目”。至少需要写清楚:最终目标、工作目录、禁止触碰的范围、子任务划分、状态定义、完成标准和结果格式。
一个可复用的任务描述可以是:
目标:检查支付模块的重试逻辑是否可能重复扣款。
工作目录:当前仓库,不修改文件。
范围:只阅读 payments/、相关测试和调用方,不扩展到其他业务模块。
约束:不要根据命名猜测行为,必须追踪真实调用路径。
输出:给出结论、证据 file:line、风险等级、建议补充的测试;没有证据的判断标记为待验证。
2. Fan-out 与 Gather:独立方向并行,统一汇总
这是最适合入门的模式。先把任务拆成互不依赖的方向,同时派发给多个 Agent,等结果回来后由一个汇总者去重、排序并提出下一步。
例如一次代码评审可以拆成安全、测试、性能和可维护性四个方向。每个方向都只读同一份代码,不修改工作树,因此不需要 Worktree。汇总者要保留每条发现的文件位置、严重程度、复现条件和是否与其他发现重复。
这里有一个重要边界:如果 A 必须先知道 B 的结论才能继续,A 和 B 就不是独立任务。强行并行只会让两个 Agent 各自猜测,最后在汇总阶段制造更多返工。
3. Generator-Critic:生成与评审分离
生成者和评审者应该是两个不同的角色。让同一个会话刚写完代码就自查,通常只能发现表面问题,因为它会沿用自己刚才的假设。独立 Reviewer 只接收代码、规格和验收标准,不接收生成过程中的自我解释,更容易发现遗漏。
Worker 完成实现
-> 保存 diff、测试输出和变更说明
-> 独立 Reviewer 检查规格和证据
-> Reviewer 返回结构化问题
-> Worker 只修复明确问题
-> 重新运行相关测试
-> 达标后结束,或达到最大修正轮次后交给人工决策
评审结果最好使用固定结构:问题标题、严重程度、位置、复现条件、为什么违反验收标准、建议修复方向。不要只返回“看起来没问题”,这种结果无法支持后续审计。
四、把任务状态从聊天记录中独立出来
小任务可以依赖会话历史,大任务最好维护一个简短的 Registry。Registry 不需要一开始就做成数据库,一个 Markdown 或 JSON 文件也可以,但至少要记录:
task_id:任务标识
owner:当前负责的 Agent 或人工角色
status:pending / running / blocked / review / done / failed
depends_on:前置任务
artifact:产物路径或摘要
evidence:测试、日志、diff 或引用位置
next_action:下一步动作
不要把 Thread 历史当成业务状态库。Thread 保存的是对话和工具事件,Registry 保存的是“整个工作流已经推进到哪里”。这两个状态容器分开后,即使某条会话很长、某个 Agent 被换到另一台机器,也可以从 Registry 快速恢复全局视图。
状态值也要有明确含义。例如 done 表示产物已经生成且通过验收,不只是 Agent 说“我做完了”;blocked 表示缺少明确的输入、权限或前置结果,不是单纯运行时间较长;review 表示实现已完成但还没有通过独立检查。
五、如何设计可交接的产物
多 Agent 系统最容易失败的地方是上下文交接。一个 Agent 返回大量自然语言,另一个 Agent 还得重新猜测哪些是结论、哪些是过程,效率会迅速下降。
建议每个任务至少产出以下三部分:
- 结论:一句话说明当前状态和建议动作;
- 证据:文件位置、命令输出、测试名称、日志片段或差异摘要;
- 限制:哪些内容没有检查、哪些结论依赖特定环境、哪些问题仍需人工确认。
如果任务需要进入下一阶段,再增加结构化字段。例如接口迁移任务可以输出旧契约、新契约、兼容窗口、消费者清单、测试命令和回滚条件。后续 Agent 只读取这个产物,不必重放全部对话。
六、失败处理和人工介入
编排系统不是让 Agent 自动做所有决定,而是把需要人工判断的位置提前暴露出来。以下情况不适合无限重试:
- 输入要求互相矛盾;
- 需要访问未授权的目录、服务或凭据;
- 测试结果不稳定,重跑无法改变根因;
- 多个方案都满足局部指标,但取舍涉及业务目标;
- Agent 发现的风险可能造成数据删除、对外发布或不可逆变更。
这些情况应该把状态改成 blocked 或 needs_decision,并附上最小决策问题。比如不要写“请人工处理”,而应写成“是否允许迁移期间保留旧字段 14 天?选择 A 会增加兼容代码,选择 B 需要同时升级三个消费者”。
重试也要有上限。可以按错误类型设置一次网络重试、一次上下文修正和一次人工复核。连续重试同一提示词,通常只会消耗时间和额度,不会增加信息量。
七、验证多 Agent 工作流是否真的有效
不要用“开了几个 Agent”衡量编排质量。更有用的指标是:任务是否减少等待、产物是否更容易复核、失败是否能定位、不同 Agent 的工作是否重复,以及最终交付是否需要大量人工重做。
可以用一件真实的小任务做回归:记录原来单线程完成所需的步骤,再用拆分后的流程跑一次,对比以下结果:
总耗时:从目标提出到最终验收
有效工作时间:Agent 真正执行任务的时间
重复分析:多个 Agent 交付的重叠内容
返工轮次:因范围不清、证据不足或冲突造成的重做
交付质量:测试、diff、文档和限制是否齐全
如果拆分后只是增加了更多摘要和等待,而没有提高结果质量,就应减少角色数量,或者把拆分边界重新放回更大的任务单元。
结语
多 Agent 编排的核心不是“让更多模型同时工作”,而是建立清楚的责任边界:Task 承担持久角色,Subagent 处理局部工作,Worktree 隔离并行写入,Registry 记录业务状态,Reviewer 负责独立验收。
从一个 Fan-out 任务开始最稳妥:选择三个互不依赖的检查方向,为每个方向写清输入、输出和证据要求,再由一个汇总者统一收口。等这条链路稳定后,再增加 Supervisor、DAG 或跨主机路由。复杂度应当来自真实的依赖关系,而不是来自工具数量。