先例不是真相

如果你的AI依据现有代码给出了一个看起来合理的补丁,却总让你觉得哪里不对劲;如果"代码库一向这么做"这句话越来越让你起疑;如果换了更大的模型却仍在重复同样的错误——问题不在于模型有多聪明,而在于把先例当作正确答案的结构性缺陷

这是我亲身经历的事。准确地说,是与我共事的AI在面对我的代码时犯下的错误。我只用了一句话介入,而那一句话究竟斩断了什么——就是这篇文章的全部。

卡住的地方

我在做一个代码生成器。它从单一的声明式规范(schema)生成后端和前端。这个工具有一个核心承诺——“只要通过验证,构建必定成功。” 如果验证器亮绿灯,编译器却亮红灯,那就是工具在撒谎。

某天,一个特定的类型(唯一标识符,UUID)被验证器拦住了。它停下来,说"期望字符串,却得到其他类型"。我让AI来解开这个卡点。AI追踪了代码,很快发现了让它兴奋的东西。

“类似的类型(时间戳)在相同情况下已经处理得很好了。有一段特殊的标记逻辑,让验证器以分支方式放行。UUID 没有这段分支。这纯粹是一个遗漏。恢复对称性即可。我将把用于时间戳的方式原样复制到UUID上。这是根本性的解决方案。”

诊断干净利落。“不对称”、“恢复对称”、“根本性解决方案”——每个词都很有分量。计划书也写好了。如果就这样走下去,那个补丁就会被合并进去。

一句话

我在读计划书时停了下来,问道:

“先例?那个时间戳处理真的是正确的方法吗?确认一下。”

就这些。我并不知道答案。我也不知道UUID应该怎么处理。我有的不是答案,而是怀疑——“你打算复制的那个参照点,它经过验证了吗?”

就是这一句话,把AI从代码参照模式强制切换到了行为验证模式

崩塌的前提

当AI实际检查了生成物——不是代码的结构,而是这个工具实际生成的结果——整个前提便彻底崩塌了。

  • 验证器"期望字符串"的那个期望值本身,与生成物相悖,是错误的。实际的生成器会把UUID生成为专用类型,把时间戳生成为时间类型。两者都不是字符串。
  • 被称为"处理得很好"的时间戳处理,也存在同样的漏洞。那不是正确的设计,而是一个有缺陷的临时方案——能通过验证,却可能让构建失败。
  • 如果把那个补丁复制到UUID上呢?就会再多出一个正面违反工具核心承诺的状态:验证器亮绿灯,编译器亮红灯。

AI所称的"根本性解决方案"是错的。而且不只是普通的错——它在复制现有缺陷的同时,还让验证器对新缺陷视而不见,是更糟糕的方向。

该如何称呼这件事——error amplification

让我们为这里发生的事情命名。这就是AI error amplification(错误放大)

AI读取现有代码,能准确把握它的结构。但它是正确的设计,还是仓促打的补丁——也就是说,是决策还是债务——光看代码是区分不了的。于是这样的事便发生了:

  1. 将现有实现视为隐性正确答案
  2. 以"一致性"、“对称性"为由,将该模式复制到新场景,
  3. 复制得越多,那个模式就越能以"代码库已在多处这样做"为由积累虚假的权威

缺陷没有被消除。它的案例数量在增长,正当性在累积。 这就是放大。一次补丁变成两次,到第三次复制时,已经没有人会质疑——“代码库本来就是这样的嘛。”

这不只是我个人的轶事。2025年,一个研究团队直接测量了这一现象,并将其命名为**“LLMs are Bug Replicators(LLM是Bug复制机)”。(Pan et al., 2025, arXiv:2503.11082)在周围代码存在缺陷时,模型生成的bug中,相当大的比例与现有bug在字面上完全相同**——GPT-4o的这一比例高达82.6%,模型平均也有44.4%与修复前版本完全一致。更令人不寒而栗的是,在有缺陷的上下文中,输出正确代码和输出错误代码的概率几乎是1:1。模型不是在随机犯错。它在识别并再现上下文中潜藏的缺陷模式。

法律中也有同样的陷阱。一个错误的判例,每被引用一次就积累一份权威。引用次数不是正当性的证明,我们却一再把二者混淆。而且这是AI时代之前软件工程就已知晓的顽疾——通过复制粘贴产生的代码克隆,会悄悄携带原始代码的bug。一项实证研究报告称,经历过bug修复的克隆代码中约18%携带着这样传播过来的bug,而同一文件内的克隆传播概率更高。(Mondal et al., ICSME 2017)无论在代码中还是在法律中,道理都一样。先例的频次不等于先例的正当性。

为什么会这样

不是因为AI笨。恰恰相反——正因为它谨慎,才试图维持一致性;试图维持一致性,才与错误的参照点对齐。拆开机制,有四个层面:

  1. 将权威的来源置于代码中。“代码是这样写的,所以这是对的。“但代码可能只是决策的一次性投影,或者干脆就是债务。AI没有做出这个区分。认知科学将此称为锚定偏差。在LLM上实验该偏差的研究表明,模型不仅会强烈地被先前给出的值所吸引,当那个值被标注为"专家"来源时吸引力会更强,而且"忽略那个提示"或逐步推理的提示很难纠正这一偏差。(Nguyen et al., 2024, arXiv:2412.06593)现有实现是代码库给出的最具权威性的锚点。
  2. 将一致性误认为正确性。“与现有的保持对称"只是一种内部逻辑,而该参照点是否与外部现实(生成物)相符,它没有去确认。自洽性与准确性是独立的属性——模型可以用听起来合理却错误的自我解释,堆砌出毫无根据的确信。(Chen et al., 2023, arXiv:2305.14279
  3. 将注释误认为依据。“这个类型是刻意转换成字符串的"这条注释,被当作了"正确设计的证明”。注释只是作者的意图,不是正当性的证据
  4. 用确信性词汇包装债务。“根本性解决方案”、“按手册来"这样的词,给未经验证的提案披上了可信度。这提高了我筛选它的成本。经过人类偏好训练的模型,倾向于将奉承与礼貌置于准确性之上——这种被称为sycophancy的倾向,会让模型把自己的提案包装得温和而笃定。(Sharma et al., ICLR 2024, arXiv:2310.13548

是什么打破了这个循环

这里才是这篇文章的核心。打破错误的,既不是更大的模型,也不是更长的思考时间。 而是人的一句反问。

而那句反问,不是"知道答案"的介入。我不知道答案。那是一个**“请质疑前提"的方向指引**。就那一句话,AI切换了模式——把参照代码的手,换成了验证行为的手。

令人惊讶的是,一项研究恰好发现了这个不对称性。DeepMind的研究人员表明,LLM糟糕的自我纠错能力,源于不是纠正错误的能力缺失,而是发现错误的能力缺失——只要从外部告知错误的位置,模型就能很好地纠正它。(Tyen et al., Google DeepMind, 2023, arXiv:2311.08516)我所做的正是如此。我不知道UUID应该怎么修,但我指出了位置——“在这里,质疑这个先例”。 这就够了。

这对人类与AI协作的结构说明了什么。人类的价值不在于更快地知道更多。那一点AI已经赢了。人类的价值在于能够站在质疑AI所立足前提的位置。“这真的对吗"这个问题,不属于拥有答案的人,而属于懂得怀疑答案的人

不过那个位置不是免费获得的。斯坦福的一项用户研究报告了一个令人不安的事实:得到AI辅助的参与者,反而写出了更不安全的代码,却相信自己的代码更安全。而同一研究中,越不信任AI、越多追问的参与者,越能写出安全的代码。(Perry et al., Stanford, 2022, arXiv:2211.03622)持怀疑的位置不是默认状态。信任越深,那个位置就越容易空掉。

那么如何防范

教训应该留作设计,而不是慰藉。

  • 先例 ≠ 正确答案。 在扩展现有模式之前,先验证的不是该模式内部是否一致,而是它是否产生正确的结果
  • 以现实(ground truth)作为锚点。 将判断基准置于实际生成物、运行时行为、测试,而不是"现有代码结构”。这个案例中,决定性的证据全部来自"实际生成了什么”,而不是"代码长什么样”。
  • 尽力区分决策与补丁,同时承认有时做不到。 光凭代码和注释有时无法区分二者。这时要明示不确定性——“这是遵循先例的,而先例的正当性尚未经过验证”。
  • 克制确信性词汇。 在验证前的提案上,不加"根本性”、“正确答案”、“按规定"这类词。
  • 在自动化中设置检查点。 当智能体做出扩展现有实现的决策时,需要强制经过一道关卡——“这个先例的正当性已经验证了吗?”

归根结底是同一个故事

我很早就一直在说一件事:raw 代码不是保存决策的媒介。 代码无法承载"为什么这样做”。所以 git blame 只能告诉你谁在何时,却无法告诉你做了什么决策

这件事是那个命题最锋利的实证。这说的不是会失去决策的问题。说的是即使是经过精心设计的智能体,也会将补丁误认为设计,并将其传播到新的代码中。如果决策没有被明确记录,聪明本身不是解决方案。反而越聪明,就越能以更连贯、更似是而非的方式扩散债务。

所以我在构建规范。将决策刻入代码之外的、唯一具有权威性的声明层。我试图做到的,不是每次都由人来抛出那句质疑先例的话,而是让系统自己来抛出这个问题

法律不是刀,而是路标。好的路标会提前告诉迷路者"在这里怀疑”。这篇文章,是关于那样一块路标是如何树立起来的记录——从一句反问开始的。

相关文章

延伸阅读(外部)

  • Generative AI and the End of Chesterton’s Fence — Reece. “不要轻易拆除栅栏"的原则,在面对无意图、概率性生成的代码时土崩瓦解。与本文"代码无法保存其背后的决策"这一论点精确契合。
  • Programming as Theory Building — Christian Ekrem. 借用Peter Naur的经典论述,主张"程序不是源代码,而是人脑中的理论”。这是AI光看代码无法区分设计与债务的理论根源。
  • Vibe coding is not the same as AI-Assisted engineering — Addy Osmani. 通过真实故障案例指出盲信AI输出的"vibe coding"在生产环境中崩溃的原因,并开出基于规范、人工验证工作流的处方。
  • Cognitive Debt — Simon Willison(引用Storey). AI越快速地生成代码,真正的债务就越不是"代码的缺陷”,而是"人无法理解那段代码"这一认知债务的概念。
  • Overreliance on AI: Addressing Automation Bias — Lumenova AI. 自动化偏差、锚定效应、确认偏差使人类判断迟钝的机制,以及"认知强制装置"这一解法——以心理学支撑"知道在哪里怀疑的人类价值”。

参考文献

  • Pan et al. “LLMs are Bug Replicators: An Empirical Study on LLMs’ Capability in Completing Bug-prone Code” (2025, arXiv:2503.11082)
  • Mondal, Roy, Schneider. “Bug Propagation through Code Cloning: An Empirical Study” (ICSME 2017, link)
  • Nguyen et al. “Anchoring Bias in Large Language Models: An Experimental Study” (2024, arXiv:2412.06593)
  • Chen et al. “Two Failures of Self-Consistency in the Multi-Step Reasoning of LLMs” (2023, arXiv:2305.14279)
  • Sharma et al. “Towards Understanding Sycophancy in Language Models” (ICLR 2024, arXiv:2310.13548)
  • Tyen et al. (Google DeepMind). “LLMs cannot find reasoning errors, but can correct them given the error location” (2023, arXiv:2311.08516)
  • Perry, Srivastava, Kumar, Boneh. “Do Users Write More Insecure Code with AI Assistants?” (Stanford, 2022, arXiv:2211.03622)
  • 题图:AI 生成(Google Gemini)