需求文档、代码、测试皆出自一人:你正在悄然失去的独立性
当同一个模型负责编写需求、实现代码并编写用于验证正确性的断言时,“所有测试通过”不再是功能正常工作的证据,而仅仅证明了模型是内部一致的。这是两回事,而这种区别正是编写测试的初衷。
我们通常对测试套件的理解是,它们提供了“第二意见”。作者带着一种对需求的心理模型编写代码,而测试编写者则带着略有不同的心理模型编写断言。这两个模型不一致的地方,往往就是 Bug 潜伏之处。这种说法的前提是测试编写者与代码作者拥有不同的认知视角。如果去掉了视角的差异,测试套件就不再携带关于正确性的任何独立信息——它只携带关于一致性的信息。
大多数在 2026 年发布 AI 辅助软件的团队尚未察觉到他们做了这种交易。模型编写需求文档,编写实现需求的代码,并编写将代码锁定在规范中的单元测试。CI 显示绿色。PR 描述是根据差异自动生成的。审阅者走马观花地看了下摘要便予以批准。每一个产物都由同一个大脑在同一个会话中创作,“所有测试通过”被解读为 功能正常工作的证明,因为在人类编写测试的时代,绿色构建确实意味着这些。
1986 年的一篇论文在 LLM 出现二十年前就预言了这一点
与这种失效模式最接近的现有技术可以追溯到大语言模型出现前的四十年前。1986 年,Knight 和 Leveson 进行了一项实验,该实验悄然终结了将 N 版本程序设计作为一种严肃可靠性策略的领域。他们让两所大学的 27 个团队根据相同的规范独立编写程序。待测试的假设很简单:独立开发的程序会独立地失效,因此运行多个版本并对输出进行投票的系统将比任何单一版本都可靠得多。
数据结果并不支持这一假设。在检测到的 45 个故障中,大约有一半在不同版本之间存在关联。在 100 万个测试用例中,有 1255 次出现了多个版本同时失效的情况,有时甚至多达 8 个版本同时出错。独立性统计测试在超过 99% 的置信度下被拒绝。该领域吸收的教训是:即使是根据相同规范编写程序的独立人类作者,仍然会产生相关的错误,因为他们共享同一种文化、课程体系、对规范的解读,以及作为计算机专业学生对哪些边界情况“显而易见”是棘手的本能反应。
现在将这一视角应用于在三个层面上编写内容的单个模型。Knight-Leveson 的结果表明,具有不同教育背景、犯下不同错误的人类,在同一规范下的关联度仍约为 50%。而模型在一次会话中编写规范、代码和测试,其关联度 接近 100%,因为所有这三个产物都源自同一个先验知识、同一个训练分布和同一种上下文解读。无论模型对需求有什么误解,它都会误解三次。房间里没有“第二意见”。
这种框架在相关领域已经有了专门的词汇。在安全关键系统中,这被称为“共模失效”(common-mode failure),这也是为什么航空领域实际上并不运行相同的飞行计算机进行投票——因为投票结果的相关性太高,不值得付出这种成本。如今团队所谓的“AI 辅助开发”,从安全工程的角度来看,其实是一个 N=1 的系统在伪装成 N=3,因为同一个大脑产生了三个看起来像是出自不同作者之手的产物。
危险的失效是无声的缺失,而非错误的测试
当团队担心模型编写的测试时,他们通常担心的方向不对。最明显的担忧是“模型写了一个错误的测试来断言错误的逻辑”。这种情况确实会发生,但它是显性的。断言错误行为的测试在代码审查中往往看起来很奇怪,并且在第一次真实输入进入生产环境时往往会崩溃。
真正的危险失效是无声的。用户关心的某种行为在所有三个产物中都缺失了,因为模型的先验知识从未让其浮现。规范中没有提到它,代码中没有处理它,测试中也没有对其进行断言。CI 显示绿色并不是因为系统正确处理了该情况,而是因为没有人——无论是实现者还是断言编写者,他们是同一个人——想到要问这种情况是否存在。
这种情况的一个具体表 现:一个计费接口,其规范涵盖了退款,代码实现了退款逻辑,测试也覆盖了显而易见的退款路径。然而,这三个产物都没有提到如果客户的账单货币在原始扣款和退款请求之间发生了变化会发生什么。模型从未想到要对此进行规范,因为其训练数据中没有例子让这种情况变得显著。因此,代码在不进行检查的情况下按原始货币处理退款,测试套件覆盖了规范中提到的案例,而绿色构建则证明“退款逻辑已实现”。上线三天后,一位更改了货币的客户收到了错误金额的退款,事后分析显示,系统中没有任何产物承认过这种情况的存在。
这正是 N 版本程序设计无法捕获的失效模式。如果三个独立的人类都因为规范未提及而忘记处理同一个边界情况,那么投票器就无从投起——三个相同的错误答案会一致投票给错误的结果。测试套件能检测 Bug 的潜前提是:测试编写者提出了实现者没有想到的问题。而同一个作者在两个位置上只会问出相同的问题。
独立性要求在三层架构中至少有一层由不同的作者完成
在“规格-代码-测试”(spec-code-tests)流水线中,有三个地方可以注入真正的独立性。如果“测试通过”想要保持其原有的意义,那么这三个环节中至少有一个需要独立。
第一种方案成本最低也最被低估:由人类编写规格书。这不是指“由人类评审模型编写的规格书”,而是在模型看到代码之前,由人类用自己的语言编写需求文档。这样,规格书就成为了衡量模型的一个外部契约,模型对需求的理解不再等同于需求本身。在模型能够写出像样的 PRD(产品需求文档)之前,产品组织就是这样交付软件的,这种规范依然有效。代价是必须有人写出一份真正的 PRD,这在当下可能显得有些过时;但好处是,你重新引入了流水线中唯一的审计层,其作者在结构上与实现者是不同的。
第二种方案是在测试层使用与代码层不同的模型家族。如果代码由模型 A 编写,而测试由模型 B 编写,且 A 和 B 是在不同的语料库上通过不同的筛选流水线和后期训练完成的,那么它们的先验知识会在关键地方产生分歧。2025 年关于“LLM 作为先知”(LLM-as-oracle)以及跨实现方案的“差分测试”(differential testing)的研究一致表明,跨家族的分歧能够发现同家族流水线会遗漏的漏洞。这里的准则不是“随便用第二个模型”,而是“使用训练数据分布有本质差异的模型”,因为来自同一实验室的两个模型即使权重不同,也会共享大量的先验知识。
第三种方案是增加一层以对抗方式编写的“基于属性的测试”(property-based test)。基于属性的测试断言的是不变性(invariants)——即“对于所有输入,输出必须满足 X”——而不是具体的用例。基于属性的断言背后的认识论立场与单元测试不同:单元测试的立场是“我想象了这个输入并预测了这个输出”,而基于属性的测试则是“我声明这个不变性在所有输入下都成立,证明我是错的”。最近关于 LLM 生成代码的基于属性测试的研究表明,当属性与实现分开编写时,质量会有显著提升,因为不变性迫使测试作者思考问题的本质形状,而不是几个示例的表面。基于属性的层不一定非要 由人类编写——它可以由不同的提示词、在不同的会话中、以与代码生成提示词不同的框架来编写,这样相同的盲点就不会发生转移。
“人类评审”陷阱
当团队听说“你需要一个独立的审计层”时,最常见的反应是增加一个人类 PR 评审员。如果评审员真的重新推导了需求,这当然没问题。但如果评审员只是阅读模型写的内容,那这就成了一场演戏。
陷阱在于 AI 辅助的 PR 是打包交付的。Diff(差异比较)中包含了代码、测试以及生成的 PR 描述,解释了变更的内容和原因。如果评审员先阅读描述,然后浏览 Diff 以确认其与描述匹配,那么他们并没有增加一个独立的观察视角——他们只是在检查三个内部一致的产物是否内部一致。它们自然会相互吻合,这就是“内部一致”的定义。
真正的评审独立性要求评审员阅读 原始的功能需求——工单、错误报告、客户邮件——并在不先阅读模型总结的情况下,判断 Diff 是否解决了问题。更好的是,评审员在打开 Diff 之前,先写下自己对需求的一句话理解,然后检查 Diff 是否符合他们所写的内容。2025 年末的 AI 代码质量数据显示,当 AI 评审工具发布时,人类在 PR 上的评论率下降到了 20% 以下,而 AI 编写的变更每 PR 产生的 issue 数量大约是人类编写的 1.7 倍。这两个数字是正相关的。评审员不再做那些测试套件同样也不再做的工作了。
测试独立性是一种分布式系统属性
思考这一切最清晰的方式是引入分布式系统的框架,不要再把测试看作是特殊的。一个服务的三个完全相同的副本无法捕获彼此的漏洞。请求处理程序中的一个逻辑漏洞会以同样的方式让所有三个副本在相同的输入上崩溃,而负载均衡器会尽职尽责地将流量路由到第一个响应的已崩溃副本。当失败是不相关时(如硬件故障、网络分区、单机内存压力),副本可以增加可用性;而当失败是相关时,副本毫无作用,因为三个相关的答案仍然只是一个答案。
单一作者的“规格-代码-测试”三元组就像是一个戴着三顶帽子的副本。这些帽子看起来不同——一个是 Markdown 文件,一个是函数,一个是断言——但它们都是由同一个大脑在同一个会话中驱动的。当大脑有盲点时,所有三个产物都有盲点。当大脑误解了需求时,所有三个产物都会对这种误解达成一致。流水线底部的投票者——CI 运行器——看到了来自同一个头脑的三张选票,却报告得好像它们来自三个头脑一样。
随之而来的领导力问题是令人不安且值得大声追问的:你的 AI 工程组织是否有任何审计层,其本身不是由它正在审计的同一个模型家族编写的?如果规格书是模型写的,代码是模型写的,测试是模型写的,PR 评审也是模型做的——即使人类名义上点击了批准按钮——那么“测试通过”所承载的认识论权重,大致等同于“作者说这能行”。在测试出现之前,这曾是软件开发的基准状态。测试是该领域脱离这种状态的方式。让一个作者编写所有三个产物,正是在众人惊叹于开发速度的同时,缓慢地退回到过去。
解决办法不是增加更多测试。解决办法是引入不同的测试作者——规格层的核心由人类负责,测试层使用不同的模型家族,或者在不同会话中设计对抗性的基于属性的断言。这些都不昂贵。但所有这些都要求承认:你从单一作者三元组中获得的绿色构建(green build),与人类编写测试时获得的绿色构建是不一样的,你需要将这种差异计入你对它的信任成本中。
- http://sunnyday.mit.edu/papers/nver-tse.pdf
- https://arxiv.org/abs/2506.18315
- https://engineering.fb.com/2025/09/30/security/llms-are-the-key-to-mutation-testing-and-better-compliance/
- https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report
- https://www.shadecoder.com/topics/specification-gaming-a-comprehensive-guide-for-2025
- https://dev.to/markk40123/when-generated-tests-pass-but-miss-the-bug-a-case-of-false-confidence-from-ai-test-generation-1674
- https://law.stanford.edu/2026/02/08/built-by-agents-tested-by-agents-trusted-by-whom/
- https://www.oreilly.com/radar/comprehension-debt-the-hidden-cost-of-ai-generated-code/
- https://dl.acm.org/doi/10.1145/3715107
- https://dl.acm.org/doi/10.1145/3735637
