最近帮一个团队做代码评审,发现一个特别典型的场景:需求文档写得很清楚,子agent也忠实地“写完了代码”,结果打开Diff一看,接口签名和文档里定义的完全对不上,错误处理逻辑散落在三个文件里,测试文件干脆就没生成。这不是哪个模型智商不够,而是组织方式出了问题——你让一个agent同时当架构师、实现者、测试员和评审员,它的注意力天然会被稀释。
这正是我认同Subagent驱动开发模式的原因。Superpowers系列里讨论过很多次这个思路,核心主张其实很朴素:不要训练一个“全能助手”,而是要组织一条生产流水线。每个Subagent只负责一个工位,有独立的上下文、明确的工具集和可验收的输出标准,主控agent负责调度、校验和返工。这样AI写的每一行代码都是可检查、可拒绝、可追溯的,真正从“让AI写代码”变成“用AI管理一条编码产线”。
这篇文章我会把整套流水线架构拆开讲:为什么单个agent大包大揽必翻车、五种Subagent工位怎么设计、主控调度怎么写、我在实测中踩过的坑,以及到底什么团队适合上这套玩法。现在很多团队已经在用AI写代码,但效果忽好忽坏,问题恰恰出在流程而不是模型上。
1. 为什么单个“超级Agent”大包大揽,是AI编程翻车的主要根源
1.1 一个Agent同时扮演所有角色,恰恰是它最不擅长的
先说个我自己的经历。早期我也图省事,把“把这个支付模块写出来”这种需求直接甩给大模型,让它自己看仓库、自己定接口、自己写代码、自己补测试。单看某个文件,产出质量其实不错;但一旦涉及多个文件之间的协同,问题就全出来了。
最典型的情况是“前面定义、后面推翻”。agent在生成接口定义的时候是一套方案,写到某个具体实现时又为了“省事”改了参数结构,而调用方代码还是按老签名生成的。你问它为什么不一致,它甚至会给出一个逻辑自洽的理由。这种连续性断裂的原因并不神秘:上下文窗口再大,也没法把整个项目的所有细节都装进去并保持全局一致。当agent同时关注“这个函数怎么写”和“整个项目架构怎么定”时,注意力被分散是必然结果。
打个比方你就明白了。传统手工作坊里,一个老师傅从画图到锻造到打磨全包,能做出一把好刀,但你要他一天做出五百把标准一致的刀,他一定失控。流水线革命解决的不是“工人变聪明”,而是把工序切细、把每个工位的输入输出固定下来,让品质问题能在某个环节被拦住。AI编程也是同理。单个大agent就是那位老师傅,而Subagent架构就是那条流水线。
1.2 Subagent的本质:把“一个聪明的Agent”换成“一组职责清晰的Agent”
Subagent并不是更聪明的agent,它只是可组合、可隔离、可验证的agent。每个Subagent拥有独立的上下文窗口、独立的角色提示词、独立的工具集,以及独立的输出工件。
这个设计带来的好处非常直接:
- 上下文隔离:实现模块的agent不需要关心需求文档里那些营销背景,它只需要接口规范、它负责的文件路径和仓库里相关类型的定义。上下文越小,注意力越集中,越不容易跑偏。
- 关注点分离:架构agent负责定边界,写代码的agent负责填充实现,测试agent负责找茬,审查agent负责挑刺。每个角色只干一件事,衡量标准完全不同。架构agent写得再好,也不能替测试agent决定测试策略。
- 错误不向下游传播:主控agent在每一道工序之间做校验。架构agent输出的接口文档格式不对?直接打回重做,根本不会传到实现环节。这比一个agent从头写到尾、最后统一review要省得多。
我自己在项目里实践下来最大的感触是:一个Subagent的输出质量,大概率比同一个模型当“全能助手”时的输出质量高出一截。原因不是模型变了,而是它收到的信息、被要求的产出形式、以及验证方式都变得更像一份“工位作业指导书”,而不是一句“你看着办”。
2. 五种Subagent工位:架构、编码、测试、审查、文档
2.1 流水线总览与工位卡片
把软件开发拆成流水线,首先要做的是定义工位。我这边经过几次迭代后固定下来五类,每一类都有明确的输入工件、输出工件和退出标准,整理成一张表你一眼就能看明白:
| 工位 | 输入工件 | 输出工件 | 退出标准 |
|---|---|---|---|
| 架构定义 | 需求文档、约束条件 | 模块边界、接口签名、数据模型 | 接口文档为结构化JSON,通过scheme校验 |
| 模块实现 | 接口规范、目标文件清单、相关类型定义 | 单个模块的完整代码、新增测试文件 | 代码通过编译与静态检查,测试文件存在 |
| 测试生成 | 实现代码、接口规范 | 单元测试代码与用例说明 | 测试文件可执行,至少覆盖关键分支 |
| 代码审查 | 实现代码、测试代码、接口规范 | 审查意见列表 | 无阻塞性意见,或有且已被主控裁决 |
| 文档更新 | 最终代码、测试报告、变更记录 | README段落、CHANGELOG条目 | 文档路径匹配变更文件,无过时描述 |
流水线里交接的永远是“工件”,不是自然语言聊天记录。实现agent给测试agent的不是“我写完了你看一下”,而是一份代码变更、一份接口文档和一个可执行的测试入口。这个区别非常重要——工件可以被程序校验,聊天记录只能靠人看。
2.2 每个工位的提示词里应该有什么,不该有什么
工位定义完之后,提示词设计就变成最关键的工程决策。这里我走过弯路,一开始恨不得把所有信息都塞给每个子agent,后来发现完全相反。
架构agent的提示词:只给需求原文和明确的技术约束(比如“必须兼容现有的权限中间件”“数据模型需要预留软删除字段”),不给任何历史对话。要求它输出严格的结构化接口定义,而不是散文式的设计说明。提示词里我会明确要求“如果需求本身存在歧义,请列出你的假设并给出默认选择”,这样能逼出隐含的不确定性。
实现agent的提示词:只给接口规范、它要动的文件清单,以及必要的依赖类型定义。不给全部仓库代码,不给需求背景。最重要的是一句话:“你的唯一任务是让这些接口签名存在且行为符合描述。”此外我强烈建议让它先输出一段不超过三行的实现计划,再写代码。这是降低返工率最有效的一招,后面会细说。
审查agent的提示词:明确告诉它“你只输出意见,不修改代码”。每条意见必须包含文件、行号、问题类别(正确性/性能/可读性/安全隐患)和建议修改方向。如果它想改代码,说明这个工位设计有问题。
还有个我试过多次的小技巧:让每个subagent在输出末尾增加一个self_check字段,用一两句话说明自己是怎么验证产出的。这个字段价值极高,很多agent在写self_check的时候会发现自己根本没跑过测试,或者说不出验证方法——它自己写出来都会心虚。
3. 主控调度:任务分解、工件交接与并行控制
3.1 主控的责任是拆任务,不是写代码
流水线的主控agent不需要写一行业务代码,它的核心工作是三件事:拆解任务、调度工位、验证成果。
任务拆解的粒度非常关键。我的经验是:一个任务卡应该小到“一个Subagent在有限的上下文里能独立完成并自我验证”。一个支付模块可以拆成“账户余额查询接口”“转账事务服务”“交易记录分页查询”三张任务卡,而不是一张超大的“实现支付模块”。每张卡要写清楚目标、输入工件路径、输出工件规范、退出标准。
这里补充一个重要的设计原则:不要把调度逻辑做成纯agent决策,尽量写成确定性代码,再加上关键节点的agent辅助。很多人会把主控也定义成一个“很聪明的agent”,让它自己决定怎么拆任务,但这样做的代价是行为不可预期。我实际项目里采用的方案是:用Python脚本定义流水线状态机,任务卡由人工(或架构agent一次性生成)提前定义好,脚本按依赖关系执行调度,只有遇到状态判断模糊的地方才让LLM做一次辅助决策。
一个简化版的调度伪代码长这样:
from dataclasses import dataclass from typing import Callable import subprocess @dataclass class TaskCard: role: str # architecture / implement / test / review / docs task_id: int input_paths: list[str] output_paths: list[str] acceptance_cmd: str max_retry: int = 3 def run_acceptance(task: TaskCard) -> bool: # 退出标准必须是确定性脚本,不能听agent自己说“完成了” result = subprocess.run(task.acceptance_cmd, shell=True, capture_output=True) return result.returncode == 0 def main_loop(tasks: list[TaskCard]): done, failed = [], [] for task in tasks: for attempt in range(task.max_retry): # 1. 构造当前任务的完整prompt(角色 + 输入工件 + 输出schema) prompt = build_prompt(task, include_history=attempt > 0) # 2. 调用能跑subagent的模型接口,拿到产出 output = dispatch_subagent(task.role, prompt) write_outputs(output, task.output_paths) # 3. 跑确定性验收 if run_acceptance(task): done.append(task) break elif attempt == task.max_retry - 1: failed.append(task) # 失败的任务打上标记,进入人工处理队列 return done, failed这段代码的重点在于run_acceptance那一行——决定了“这个工位算不算过”的不是LLM的判断,而是真实的编译命令、测试命令、静态检查命令。整个流水线最核心的哲学就是:AI负责生成,脚本负责验收。
3.2 串行、并行与依赖关系
不是所有工位都能并行。架构定义必须最先完成,否则下游全部失去锚点。测试生成依赖实现代码,审查依赖测试存在。这些硬依赖必须串行。
真正能并行的是那些互不依赖的模块实现任务。“账户余额查询”和“交易记录分页查询”如果接口边界清晰、文件不重叠,完全可以同时派给两个Subagent跑。我的建议是并行上限定在3到5个,原因有两个:一是token成本在高峰期会飙升;二是并行任务之间的上下文隔离会增加输入裁剪的设计难度,文件重叠的风险也会上升。
想让并行更靠谱,一个做法是先让架构工位输出一张“依赖图”,主控按依赖层级分批调度。批内并行,批次之间串行。这跟CI/CD里的pipeline思路完全一脉相承,只是从“跑脚本”变成了“跑agent”。
3.3 怎么对接git与CI
很多团队倒在这一步:subagent把代码生成了,但agent生成的代码要落进真实工程仓库,中间还有一道“信任边界”。
我的做法是:实现agent不直接改文件,而是输出标准化的文件路径加完整内容(或diff片段),由确定性脚本负责写盘。写盘之后立即跑一次git diff --check和编译命令,确保没有空白错误、没有语法错误,然后才允许进入下一工位。这个过程中所有agent都没有仓库写权限,连接近仓库的工具都不给。原因很粗暴:一旦让agent直接操作git,你会在review的时候发现一些“莫名奇妙的合并”和历史重写,那个排查成本比返工高得多。
等所有工位跑完,流水线生成的产物就是“一批校验过的文件+一份变更说明”,人工review时只需要在GitLab或GitHub上开一个MR,看diff、看测试报告、看审查意见列表。整个流水线并没有替代人,而是把人的注意力收窄到“最终决策”这一步。
4. 实测中踩过的坑:上下文污染、幻觉验收、并行写冲突
4.1 坑一:上下文越“丰富”,产出越糟糕
第一次搭流水线的时候,我为了“让子agent更懂这个仓库”,把整个仓库目录树和几个核心模块的代码全塞给了实现agent。结果它写出来的代码学会了无关模块的命名习惯,复制了一段与任务无关的工具函数,更离谱的是引用了一个根本不存在的方法——因为它从参考代码里“推断”出有这个方法。
后来我才想明白:上下文上下文,没有边界就是噪音。实现一个登录接口的agent不需要知道推荐系统的聚类逻辑。合理的做法是给“文件清单+相关类型定义+接口签名”,把这些信息整理成一个最小设计视图。如果它在编码过程中确实需要查某个函数定义,正确的机制是给它一个检索工具,让它自己按需查询,而不是提前把所有可能用到的信息全塞进去。
4.2 坑二:agent自己说自己“测试通过”
这个坑我印象太深了。早期我把验收逻辑写成“读取subagent返回的文本,如果包含‘测试通过’四个字就把状态置为success”。某个实现agent很流畅地输出了“所有测试均已通过并验证”,但我一检查,它压根没生成测试文件,那只是一种语言上的“顺滑”。模型不会撒谎的机制,它只是在预测话语模式。
从那次之后我立了一条铁律:任何验收都只认脚本返回值。测试跑没跑过,不是agent说了算,是pytest/go test/cargo check的退出码说了算。检查-反馈循环也要配套设计:验收失败,把失败日志作为修订输入退回给原工位,让它修改后再试;连续重试超过三次,标记为疑似“工位解决不了”的任务,转入人工处理,而不是无限重试烧token。
4.3 坑三:并行Subagent改同一批文件
并行调度看起来很美,但第一次实际运行就翻车了。两兄弟一个做“用户资料查询”,一个做“用户资料修改”,任务卡里恰好都写了“可能需要调整model/user.go”。结果两人同时往同一个文件里写入各自的版本,后写入的覆盖了前一个,代码直接编译不过。
解决方式靠工程约定:每个任务卡里声明这个子任务的“文件所有权”。调度器在分配并行任务前,检查任务的输出文件路径集合是否存在交集;有交集的任务自动降级为串行。这跟真实团队里的代码所有权机制是一样的,只是提前到调度阶段做了自动检查。对了,git status里出现意外冲突文件也是同样的排查思路,先看任务卡的文件所有权声明,再看上下文隔离对不对。
4.4 坑四:昂贵的token花在“解释”上
子agent有一个我觉得还挺烦的习惯:喜欢在代码之外输出大段的“设计说明”。第一次跑完一条流水线,我看了一下token账单,好家伙,40%的费用花在了解释代码细节和复述需求上。
对策有两层。第一层是在提示词里规定“解释最多三句话,输出以代码为准”;第二层是给每个工位的输出定义schema,例如实现工位只允许输出files: {path, content}[]和self_check: string两个字段。结构化输出的限制比自然语言提示词更硬,因为它从格式层面堵死了“写一堆散文”的空间。
5. 适合与不适合:团队和项目如何决定要不要上Subagent流水线
5.1 真正适合这套玩法的场景
从我接触过的项目来看,最适合Subagent流水线的有三类场景:模块边界清晰的中大型功能开发、重复性高的编码工作、以及需要完整审计轨迹的正式工程团队。
举个例子。某个后端团队接了一批标准的CRUD接口开发需求,二十多个资源模型,接口模式几乎相同。他们把这活儿拆成流水线任务卡,由架构agent先统一生成数据模型和接口模式,然后三个实现agent并行填不同的资源模块,测试agent自动补单测,审查agent查安全隐患。原来估计两周的排期,实际一周出头就出了可提交的代码,人工review只需要重点看几个不常见资源的不同处理逻辑。
这类场景之所以能跑通,是因为“输入边界清楚、输出规格明确、返修标准固定”。流水线的本质是确定性流程,它最喜欢的也是确定性问题。
5.2 不太适合的场景与低成本起步方案
反过来,有些场景我劝你别上这套流程。五百行以内的小脚本,拆流水线纯属过度设计——人工写半小时的事,流水线搭一上午。还有高度耦合的遗留系统,接口边界根本定义不出来,架构工位第一步就会卡死。另外,处于探索试错阶段的原型,今天定义接口明天就推翻,流水线的“验收标准”反而成了阻碍。
如果你不确定团队适不适合,可以先来一个最低成本的试水:只引入“代码审查subagent”和“测试补全subagent”两个工位,服务人工编码流程。即程序员照常写代码,写完自动触发审查agent给意见、生成测试框架,人工确认后合入。这样做不需要重构开发流程,也不改变团队的协作方式,却能让你快速感受到“agent在固定工位上干活”和“agent接管一切”的区别有多大。
5.3 成本与质量怎么衡量
最后聊聊大家最关心的成本。我按一次典型的小型功能模块五工位全跑统计过,大约消耗2万token左右,运行时间在几分钟级别,具体取决于模型速度和并发数。费用上大约相当于普通办公场景下几杯咖啡的钱,但换来的是“一版能提交的代码”。
更值得关注的其实是时间分配变化:编码时间明显下降,从原来的几个小时缩到几十分钟;但review时间会从零变成十五到二十分钟——这其实是被重新分配了,因为以前人工写的代码也要review,只是没人在意罢了。真正的成本是返工,一次返工大约要重新消耗三分之一的流水线费用,所以重试上限必须严格限定。
我的个人判断标准很简单:如果连续三次实验,你有两次愿意合入subagent生成的代码,那这套流程对你的项目是划算的;如果总是推翻重来,别急着换更强的模型,先排查是提示词设计问题,还是任务边界本身没定义清楚——多半是后者的责任。
最后再分享一个提升效果的小技巧。在实现工位的提示词里加一句:“先输出你的实现计划,每步不超过三句话,然后才开始写代码。”这一步能让返工率直接砍半。原因是很多代码问题在计划阶段就会暴露:如果agent的计划里压根没有“处理超时重试”这步,你甚至不用看它写的代码就能断定这个功能没做超时处理。让AI把自己的思路先摊开,是流水线上最便宜的一道路检。