在过去两个月中,我一直在用一套自己写的多 Agent 系统做实际开发,已经取代了所有官方的平台工具。作为我目前的主力开发工具,它需要读代码、改代码、跑测试、被打回、重试,经常还会在我不看着的时候继续工作。我会经常深夜开启一个复杂任务,然后第二天再进行验收。
这套系统叫 Owlery,是一个开源的 Web 看板加 FastAPI 后端,底层驱动 Claude Code 和 Codex CLI。系统里有五个常任 Agent,各自运行在独立进程中,也有各自独立的记忆:
- 设计者负责把需求拆成任务书;
- 实现者负责写代码、跑测试并交付;
- 复核官不负责鼓励,只负责挑错,而且有一票否决权;
- 另外两个角色处理调度、验收和杂项工作。
标准流水线是:
用户提需求 → 设计者写任务书 → 看板建卡 → 实现者执行并自报完成 → 复核官在全新上下文中独立复核 → 四道测试门全绿 → 用户决定是否合并。
四道门包括 1,400 多个后端单测、270 多个前端单测、TypeScript 类型检查,以及 76 条端到端测试。
更重要的是,所有任务、执行、重试、复核和模型用量都会自动写入 SQLite。所以跑了两个月之后,我终于可以不靠感觉,直接问数据库:
多 Agent 到底有没有用?
如果只说一句结论,那就是:
多 Agent 最确定的收益不是"同时干很多活",而是让一个带着全新上下文、利益立场相反的 Agent,对另一个 Agent 的完成声明保持制度化的不信任。
至于自动化、并行和无人值守,确实有用,但远没有名字听起来那么顺滑。
一、最硬的一组数据:测试全绿之后,51.1% 仍被打回
我先查了 Task Board 上线后的全部严格匹配样本。
口径是:实现者已经自报完成,测试通过,并且同一任务生命周期里确实进行过一轮独立代码复核。满足条件的交付一共 45 笔。
其中有 23 笔,被复核官明确标出了至少一个 blocker。
也就是:
23 / 45,抓伤率 51.1%。
换句话说,略多于一半的交付,在"实现者认为自己做完了、自动测试也绿了"之后,仍然存在足以阻止交付的问题。
这里必须说明:
这个数据证明的是独立、对抗式复核机制有价值,不证明多 Agent 普遍优于单 Agent。
我没有设置同一批任务的单 Agent 对照组,也没有随机实验。不能因为多个 Agent 参与了流程,就把所有收益都归因于"数量更多"。
真正起作用的变量,大概率不是 Agent 的数量,而是两件事:
第一,复核者拥有全新上下文,不会自动继承实现者的思维惯性。
第二,复核者的任务目标不是"把功能做完",而是"证明这个交付还不能上线"。
这和让同一个模型在对话末尾说一句"请检查你刚才的代码"不是一回事。后者仍处在原来的推理轨道里,对自己的设计选择、错误假设和注意力盲区都有强烈路径依赖。
独立复核则像换了一位没有参与施工的验收人员。他不需要维护前一个人的叙事完整性,也不知道实现过程中有哪些"来之不易"的妥协。一个分支写了两小时还是两天,对他没有区别:有洞就是有洞。
一个最典型的例子,是 Agent 身份与所有权改造。这个功能表面上并不复杂:退役受保护的默认 Agent,同时保证会话始终挂在一个有效的所属 Agent 下面。实现者交付后,复核官第一轮就打回两个 blocker,核心都是 owner 冲突没有处理干净。修完以后,第二轮发现其中一个缺口其实还没有闭环;第三轮继续沿着状态转换往下追,又发现“取消归档”这条路径根本没有重新验证 owner。直到第四轮才通过。
这不是四次重复挑同一个错。实现者每次都修掉了眼前能看到的失败路径,但复核者不断换一个状态追问:如果所属 Agent 已经归档呢?如果旧数据里的 owner 已经缺失呢?如果一个只读的归档会话重新变成可写呢?单看每一处代码都说得通,连起来却会制造一个界面里找不到、运行时又可能被唤醒的“孤儿会话”。
另一个案例更危险。发布系统支持多个 Task Board 共用蓝绿部署槽位。一次复核构造了这样的顺序:看板 A 上线,随后看板 B 切换上线,再由看板 A 发布新版本。此时如果 A 点击回滚,系统按“最近一个被替换的槽位”找目标,就可能拿到属于 B 的版本。代码没有崩,测试主路径也可以全绿,但回滚动作会跨越看板边界,把错误项目提升为线上版本。第一轮复核抓出“发布中的版本会被错误 supersede”和“跨 board 回滚越权”两个 blocker;修复后第二轮发现前一个竞态仍留着缝,第三轮才真正放行。
这种案例比“模型写错了一行代码”更能说明独立复核的价值:实现者验证的是自己设计里的正常世界,复核者专门构造两个正确局部相撞时的异常世界。
更扎心的是,系统自己后来还做了八次"复核的复核"。
这些交付已经在原任务中通过过复核、被标记为 done,随后又单独创建一张全新的复核任务,让复核官从头再看一次。
八笔里又抓出两笔新 blocker,二层抓伤率 25%。
样本很小,不能泛化,但足以提醒我:所谓"通过复核",也只是一次有限上下文、有限预算下的观察结果,不是数学证明。
其中一笔交付已经被判定"两轮通过",二次独立复核仍发现深度研究路径绕过模型路由和预算门禁。另一笔则发现,界面上声称实现的删除回退逻辑实际上并没有真正落地。
测试没有红,前一位复核官也放行了,但问题依然存在。
多 Agent 没有消灭错误。它做的事情,是把"完成"从一句模型自述,改造成一个可以被挑战的状态。
二、所谓并行,大部分时间其实只有一条线在跑
多 Agent 最容易被营销的能力是并行:一声令下,多个数字员工同时开工。
数据库给出的现实要克制得多。
我把每次 Task Board 执行和裸委派都视为一条工作区间,按照开始、结束时间做扫描线统计。全周期共 369 个区间,其中 151 个是任务执行,218 个是独立委派。
峰值并发确实到过 8。
但这个峰值只出现在 7 月 20 日到 26 日那一周,而且是在 Task Board 正式上线之前。当时预算系统、模型路由、飞书桥等几条战线恰好同时收尾。
看板上线后,周峰值反而始终不超过 4。
更能说明常态的是中位数:
- 把全天自然时间都算进去,每周并发中位数都是 0;
- 只看至少有一条任务正在执行的活跃时段,除峰值那周外,中位数长期是 1。
因此,真实的多 Agent 并行更像一种稀疏爆发能力,而不是稳定运行状态。
大部分时候,瓶颈不是 Agent 数量不够,而是没有那么多彼此独立、信息完备、可以安全同时推进的任务。
很多开发工作有天然依赖:
后端接口没定,前端无法可靠开工;底层数据结构没稳定,测试只能反复重写;前一个任务没有交付明确的 commit、约束和验收口径,后一个 Agent 就只能重新侦查。
这也意味着,真正决定并行效率的不是"能启动几个进程",而是任务边界是否足够清楚。
如果任务书不能把输入、输出、不可触碰范围、测试要求和交付协议写明白,多开几个 Agent 只会把模糊性并行放大。你会同时得到几条看似繁忙、实际上彼此等待或互相踩踏的战线。
我现在更愿意把并行视作流水线设计的副产品:边界清楚时自然能并行,边界不清楚时,调高并发数只是给混乱加速。
三、可以做到无人值守,但"全自动离席"还有距离
这套系统的 151 次任务执行,全部由调度器自动认领。
乍看之下,可以写成一个很漂亮的数字:
151 / 151,100% 自动启动。
但这个数字几乎没有行为意义。系统设计本来就要求任务由 dispatcher 认领,用户不会手工点击某一次 run 的"开始"。因此它只是一个架构重言式,不能证明我真的可以撒手不管。
更有信息量的是重试数据。
在 66 次最终完成的 run 中,有 25 次在完成前至少经历过一次失败或中断,占 37.9%。
这 25 次重试里:
- 11 次由 Agent 或系统自行触发;
- 14 次需要用户手工 unblock,占 56%。
所以实际状态不是"Agent 永远自动续跑",而是两种模式并存:
一部分失败,系统可以自行恢复;另一部分失败,仍然需要人判断现场、补充指令或重新放行,然后再由 worker 无人值守执行。
另一个角度更接近我对"离席"的真实体验。
Task Board 运行的 30 个自然日里,有 14 天我对看板没有任何操作,占 46.7%。而在这些用户零触碰的日子里,仍然有 12 个任务完成。
这个数字支持一个较弱、但可信的说法:
相当一部分交付可以发生在用户没有实时盯盘的时候。
它不支持"人类已经退出开发流程"。
人类只是从持续盯着终端,变成了在关键状态转换时介入:定义任务、处理阻塞、判断返工策略、决定最终合并。
这已经很有价值。它把人的注意力从"陪模型把每条命令跑完",迁移到"在哪些节点必须做不可委托的判断"。
但它和全自动软件公司仍然隔着很远。类似于自动驾驶L3到L4的距离,看似很接近,实际有很多问题需要解决。
四、两个月花了数亿token,真正危险的是长会话
成本方面,数据库里只有 Claude Code 后端可以可靠计费。
Codex 的 cost 字段一直为空,而且它上报的 total_tokens 是整个线程截至当前的累计值,不是单次 turn 增量。如果直接求和,会把用量严重放大。因此以下经济数据只统计 Claude Code,不能当成整个系统的总账。
从 7 月 9 日到 8 月 29 日:
- 2,009 次 turn;
- 总花费 4,517.06 美元;
- 总 token 约 37.93 亿。
这不是一个适合拿来宣传"低成本数字员工"的数字。
成本分布还有明显长尾:最贵的 10 次 turn 只占调用次数的 0.5%,却消耗了总费用的 6.9%。最贵单次 turn 是 57.25 美元。
其中有一个 20.70 美元的案例很典型。
那是一个跨越四天、累计 66 个 turn、从未清空或分叉的规划会话。该会话累计花费 235.75 美元。
某次调用距离上一次活动已经过去 4 小时 9 分钟。恢复时,这个会话只新增了 7 个输入 token,却产生了:
- 1,049,591 个 cache read token;
- 974,755 个 cache creation token;
- 3,080 个输出 token。
直接原因是缓存过期后,近百万 token 的历史上下文需要整体重新写入缓存。账单与这次实际要解决的问题有多大关系不大,主要取决于会话此前膨胀到了多大。
这件事改变了我对 Agent 成本控制的理解。
很多人会关注模型单价、输出长度和调用次数,但在长时间运行的工程 Agent 中,会话拓扑本身就是成本变量。
侦查、规划、实现、复核如果全部塞进一个不断增长的线程,短期看似节省了重新解释背景的麻烦,长期却可能形成巨大的上下文负债。一旦跨过缓存存活期,恢复一次会话,就像给一座长期无人维护的工厂重新通电。
更合理的策略不是无脑保留上下文,而是主动分叉:
- 侦查完成后,把结论压缩成任务书;
- 实现者只接收实现需要的边界;
- 复核者使用新上下文;
- 长会话在阶段完成后归档,不把全部探索史带入下一阶段。
这不仅省钱,也减少不同阶段之间的认知污染。
五、多 Agent 还会制造一些单 Agent 没有的新故障
多 Agent 不是把单 Agent 复制五份。它会形成一个分布式系统,而分布式系统会产生自己的故障。
两个月里,我记录了五类比较典型的失效。
1. 同机进程互相伤害
早期监管进程只杀直接子进程,没有清理整个进程组。一个 Agent 遗留的孤儿 pytest 会继续占用 loopback 和数据库资源,另一个测试命令甚至带有 pkill -9 pytest,可能误杀别人的测试。
表面症状是"测试总卡在固定百分比"和"委派出现网络错误",一开始还被误诊成代理问题。
这条事故的细节只有操作者复盘口述,没有保存完整进程日志,所以不能当作精确测量数据。但它揭示的问题很真实:多个 Agent 共享一台机器时,进程、端口、数据库和临时目录都必须有隔离或租约,不能指望它们天然守规矩。
2. 跨后端模型名错配,产生静默空交付
Codex 会话如果误配了 Claude 系模型名,CLI 可能以退出码 0 正常结束,事件流也呈现成功终态,但 transcript 里没有任何 assistant 回复。
系统看到的是"执行成功",用户收到的是一片空白。
这比显式报错更危险,因为所有传统健康指标都可能是绿的。后来我给 harness 增加了空 turn 检测:没有实际交付内容就合成显式错误,不能把"进程正常退出"当作"任务完成"。
3. 本地代理劫持回环地址
审批中继需要回调本机服务,但 Clash 常驻环境中的全局代理接管了请求,导致发往 127.0.0.1 的回调返回 502。审批卡片永远弹不出来,最初却被归因于模型行为不确定。
最终通过真机复现,才确认本地回调必须绕过全局 fetch 的代理路径,改为直连。
Agent 系统经常同时连接模型 API、本地服务、浏览器和审批中继。网络拓扑比普通 CLI 工具复杂得多,"localhost 一定是本机直连"不再是安全假设。
4. Agent 忙了 2 小时 47 分钟,零产出
有一张"终验收"任务,worker 没有按验收清单执行,而是重新推导整个项目。
这次 attempt 运行了 166.9 分钟,只产生两个模型 turn,消耗 5.21 美元和约 257 万 token,最终没有提交任何测试日志,以"worker 已空闲但未调用 complete 或 block"失败结束。
人工重新下达一份操作清单后,下一次 attempt 只用了 11 分钟就完成了。
这说明 Agent 的失败不一定表现为报错。有一种更贵的失败叫目标漂移:它一直在工作,但工作内容已经和任务目标脱离。
因此,长任务不能只监控"进程还活着",还要监控阶段性证据是否出现。验收任务在合理时间内没有测试日志,系统就应该尽早中止,而不是等三个小时后才发现它什么也没交付。
5. 多角色交接制造竞态
有一次交付流程中,"打开 PR"在特定窗口必然触发 500。
根因是终态通知逻辑用两次独立读取拼接结算目标。两次读取之间如果插入新的 goal operation,就会发生错误归因。
第一轮复核抓出一个 blocker;修复后第二轮复核又发现,看起来已经补上的逻辑仍然留着两次读取之间的竞态缝隙。最后只能把 delivery 和完整操作台账放进同一把锁内原子读取。
这类问题很能代表多 Agent 系统的难点:每个局部模块看起来都正确,但在"谁声明完成、谁接收交付、谁更新终态、谁发送通知"的交界处,系统会出现单 Agent 脚本根本没有的竞态和幂等性问题。
六、跑完两个月后,我会怎么搭下一套系统
如果今天重新开始,我不会先追求更多 Agent,而会先去做这四件事。
第一,把"完成"定义成协议,不是自然语言
Agent 说"已经完成"没有太大意义。
完成必须绑定可验证的证据:代码位置、commit、测试命令、测试结果、已知限制,以及明确的 complete/block 终态调用。
没有这些,所谓交付只是一次语气坚定的聊天回复。
第二,把独立复核放在并行之前
峰值并发 8 很醒目,但长期活跃并发中位数只有 1。相比之下,51.1% 的复核抓伤率直接改变了交付质量。
资源有限时,我宁愿保留一个实现者加一个真正独立的复核者,也不愿让四个实现者同时往前冲,却没有人负责否决。
第三,把上下文当成有生命周期的资产
上下文不是越长越好。
它会过期、污染、产生缓存重建费用,也会让后续 Agent 被前序结论锚定。不同阶段应该通过结构化交付物连接,而不是共享一条无限生长的聊天记录。
第四,默认系统需要人类处理异常
37.9% 的完成路径经历过重试,其中 56% 的重试需要人工 unblock。
所以人类控制面不是过渡时期的临时补丁,而是当前系统的一部分。真正务实的目标,不是删除所有人工介入,而是把介入压缩到少数高价值节点,并给人足够清楚的现场信息。
结语:Agent 不是员工,更像不可靠的分布式执行节点
两个月前,我对多 Agent 的想象更接近《西游记》的取经团队:目标一致,各司其职,各有神通。
现在我更愿意用另一个比喻:
它们更像是《大明王朝》的廷议,堂上没有庸手,经得住轮番诘问,方可付诸实行。
这个比喻听起来不浪漫,却更有建设性。
因为一旦不再把 Agent 拟人化成"会自觉负责的员工",很多设计选择就会变得清楚:
- 不相信自报完成,要验证;
- 不相信退出码 0,要检查实际交付;
- 不相信测试全绿,要安排新鲜上下文复核;
- 不相信进程会自行收尾,要管理进程组;
- 不相信长时间运行等于有进展,要检查阶段性证据;
- 不相信失败都能自动恢复,要保留人工控制面;
- 不相信上下文免费,要管理会话生命周期。
多 Agent 的价值并不是把一个聪明模型变成五个更聪明的模型。
它真正提供的是一种组织能力:把规划、执行、质疑和验收拆成彼此独立、可以追踪、可以打回的状态转换。
其中最有价值的,也许不是合作,而是冲突。
一个 Agent 负责说"我做完了",另一个 Agent 的职责则是回答:
空口无评,验过再议。