最近重读了一遍《人月神话》,越读越觉得这本书没有过时,只是里面的变量变了。
Brooks 当年反复打的那个假设是:人和月不能互换。一个延期的软件项目,不会因为多塞几个人就自然变快。新人要学习上下文,老成员要解释上下文,模块要重新切分,接口要重新协调,测试还会变长。最后经常不是加速,而是更晚。
Agent 时代也有一个很相似的幻觉:
既然执行便宜了,交付也应该便宜。
我觉得这是新的 mythical man-month。
先区分执行和交付
现在让 agent 做事确实便宜了很多。
改一个脚本,补一段测试,写一个 parser,扫一遍代码库,找某个 bug 的可能位置,甚至并行开几个 worker 去探索不同方向,这些事情的成本都在下降。
以前我们会问:谁有时间做?
现在更常见的问题是:让哪个 agent 去试?
这是真的变化。没必要假装没有。
但这只覆盖了软件工作里的一部分。我更愿意把它叫执行成本,而不是交付成本。
交付包含的东西更麻烦:
- 需求到底是什么。
- 哪些边界不能碰。
- 这个改动和现有系统怎么合在一起。
- 代码风格、抽象层次、错误处理、权限模型是否一致。
- 测试怎么证明它是对的。
- 出问题时谁能理解、回滚、继续维护。
agent 可以很快产出代码,但代码不是交付。
Brooks 在书里有个区分很有用:一个可以在作者机器上跑的 program,和一个别人能使用、测试、维护、扩展的 programming systems product,不是一回事。后者要多出接口、文档、测试、集成和维护成本。
放到今天也是一样。agent 生成一个功能是 program。能进主干、能过测试、能被团队长期维护,才接近 product。
加更多 agent,不等于更快
人月神话里最出名的一句是:
Adding manpower to a late software project makes it later.
Agent 时代可以改写一下:
Adding agents to a confused software project makes it more confused.
这里的重点不是 agent 没用。相反,agent 很有用。问题在于它会放大项目已有的状态。
如果项目有清晰的接口、稳定的测试、明确的 owner、可以执行的验收标准,并行 agent 会很好用。你可以让一个 agent 写迁移,一个 agent 补测试,一个 agent 做 review,一个 agent 查历史 bug。它们的输出能被 CI 和人类 reviewer 接住。
如果项目本来就没有这些东西,agent 会很快制造出更多局部合理的东西。
局部合理最麻烦。它不是一眼就错。每一小块代码都说得过去,每个函数名都像那么回事,每个解释也都有点道理。但放在一起以后,抽象开始重复,边界开始漂,错误处理出现三套,配置读取出现两套,测试覆盖的是 happy path,真正难的状态没人碰。
人类写代码也会这样。agent 只是把这个过程加速了。
真正贵的是上下文
Brooks 讲沟通成本时,核心不是开会多讨厌,而是系统编程本身就有大量相互依赖。任务能不能拆开,拆开以后要不要沟通,沟通要花多少成本,这些决定了加人有没有用。
agent 也一样。
我们经常低估了 agent 工作里的上下文成本。问题不在 prompt 长短,而在这些东西有没有被明确表达出来:
- 当前系统的设计哲学是什么。
- 哪些历史方案试过,为什么放弃。
- 哪些接口看起来别扭,但不能随便改。
- 哪些测试是真正的兜底,哪些测试只是样子。
- 哪些错误信息对用户有意义,哪些只是内部日志。
- 哪些代码属于临时债务,哪些代码是稳定边界。
这些上下文如果只存在于几个人脑子里,agent 不会自动知道。你可以把一堆文件塞进上下文窗口,但窗口大不等于理解稳定。
更现实的问题是,agent 很会补空白。上下文缺一块,它不一定停下来问,很多时候会生成一个看起来合理的假设。这个假设一旦进了代码库,就变成下一轮 agent 的上下文。几轮之后,项目里会多出一些没人真正设计过的事实。
这就是 agent 时代的沟通成本。
概念完整性更难了
《人月神话》里我最喜欢的概念不是人月,而是 conceptual integrity。
Brooks 说,一个系统最好从一个头脑,或者少数几个能相互共振的头脑里长出来。不是因为其他人不聪明,而是因为系统对用户是否容易理解,很大程度取决于它有没有统一的概念。
agent 会冲击这件事。
多个 agent 并行做任务时,很容易出现这种情况:
| 局部任务 | agent 可能做出的选择 |
|---|---|
| 新增 API | 按自己看到的文件模仿一套 response shape |
| 新增配置 | 在另一个目录加一套 env 读取逻辑 |
| 新增错误处理 | 写一个局部 helper,名字很合理 |
| 新增 UI 状态 | 新增一组 loading/error/empty 文案 |
| 新增测试 | mock 掉真正复杂的边界 |
每一个选择都不一定错。但如果没人管整体,系统会变成一组局部最优的拼贴。
这也是为什么我越来越觉得,agent 时代的工程负责人不是更轻松了,而是职责变了。以前很多时间花在催进度、分任务、补代码。现在更需要做的是守住概念边界:
- 这段逻辑应该放在哪一层。
- 这个 helper 是否已经存在。
- 这个异常应该向上抛,还是在这里转成用户错误。
- 这个状态是否属于领域模型,而不是 UI 临时状态。
- 这个实现是否在绕过系统已有的抽象。
这些判断很难完全外包。可以让 agent 帮你找证据、列方案、做对比,但最后要有人拍板。
交付能力会变成组织能力
如果执行变便宜,团队真正的差距会在哪里?
我觉得会落到交付系统上。
不是谁更会写 prompt,而是谁有更好的工程接收面。
比如:
- spec 是否能写清楚成功条件。
- issue 是否能切到合适粒度。
- 测试是否能快速告诉你改对了没有。
- review 是否能发现概念漂移,而不只是语法问题。
- CI 是否足够快,失败信息是否足够可读。
- 文档是否记录了设计决策,而不是只记录安装步骤。
- repo 结构是否让新贡献者和 agent 都不容易走偏。
这些东西以前也重要,只是以前人的执行速度没那么快,很多问题暴露得慢。现在 agent 把执行吞吐提上来,薄弱处会更快被打穿。
一个没有测试的项目,以前是人类慢慢猜。现在是 agent 快速猜。
一个没有架构边界的项目,以前是几个人慢慢写散。现在是十几个 agent worker 一起写散。
一个没有好 issue 的团队,以前是工程师反复问需求。现在是 agent 直接把误解实现出来。
新的外科手术团队
Brooks 提过 surgical team:不是十个人平等地切同一个问题,而是一个主程序员保持完整设计,其他角色围绕他提供支持。
这个模型放到 agent 时代反而更像了。
一个比较健康的 agent 工作流可能是:
- 人类或主 agent 负责问题定义和设计边界。
- 实现 agent 负责具体代码修改。
- 测试 agent 负责补测试和跑回归。
- review agent 负责找重复抽象、边界破坏和潜在 bug。
- CI 负责给出硬反馈。
- 文档和 ADR 负责留下决策。
这里最要紧的是,必须有一个地方维护”什么是对的”。
如果没有这个中心,multi-agent 很容易变成多线程写代码。速度很快,锁很少,数据竞争很多。
以后贵的东西
我不觉得 agent 会让软件工程变简单。它会让一部分动作变简单,然后把剩下的问题暴露得更清楚。
便宜的是:
- 生成代码。
- 改重复代码。
- 写样板测试。
- 查文档。
- 搜索代码。
- 跑命令。
- 做第一版方案。
仍然贵的是:
- 判断需求。
- 设计边界。
- 保持概念完整性。
- 识别错误假设。
- 证明正确性。
- 做取舍。
- 承担后果。
这些东西不会因为 agent 多而自动出现。
所以我现在更愿意用一句话概括 Agent 时代的人月神话:
执行是廉价的,交付仍然是昂贵的。
这句话听起来有点冷,但它能挡住一个很常见的误判:看到代码产出变快,就以为工程交付也等比例变快。
不会的。
如果团队原来缺的是执行,agent 会很明显地帮上忙。如果团队缺的是清晰需求、稳定接口、测试纪律和架构判断,agent 会先让你感觉更快,然后让你在集成和维护时付账。
Brooks 当年提醒大家,不要把人月当成可以随便互换的单位。
今天可能也要提醒自己:不要把 agent-month 当成交付单位。
它只是执行单位。交付还是要靠系统来接住。