昨天,我发现我的应用程序中有一半的模块是基于虚构的数据构建的。问题还不在于数据是虚构的,更糟糕的是,所有代码都能正常编译,而且90个测试全部通过。

连贯的虚构现实

我正在开发 BFClaude-9000,这是一个为macOS菜单栏设计的应用程序,用来监控Claude Max的配额。其中一个功能需要通过调用 claude.ai 的API来判断某个Claude账户是付费账户还是免费账户。

所以,我请求Claude Code来实现这个功能。它的输出如下:

  1. 一个DTO OrganizationInfo,包含一个字段 activeFlags: [String]
  2. 一个计算属性 isPaid,用于检查 activeFlags 是否为空
  3. 一个枚举 OrganizationSelection,将账户分类为付费或免费
  4. 几个基于样例数据的测试,用于验证所有功能是否正常

看上去很棒,结构清晰且井然有序。但这一切全是虚构的。

Claude的真实API中并不存在 active_flags 字段。即使这个字段存在,其作用也完全不符合代码中的假设。当我用自己的付费账户登录时,应用却提示我是免费账户用户。

纸上谈兵的模式

问题并不仅仅是它编造了API中的一个字段,更糟糕的是,它围绕这个谎言构建了一个逻辑完整的系统:

// 含有虚构字段的DTO
struct OrganizationInfo: Decodable {
    let uuid: String
    let name: String
    let activeFlags: [String]  // ← 这个字段并不存在

    var isPaid: Bool { !activeFlags.isEmpty }
}

// 基于虚构字段的逻辑
enum OrganizationSelection {
    case paid(id: String, name: String)
    case noPaidOrg    // ← 这个状态不应存在
    case noOrgs
}

// 验证虚构逻辑的测试
let paidOrg = """
{"uuid": "abc", "name": "Acme", "active_flags": ["pro"]}
"""
// 测试通过 ✅ — 但验证的实际上是虚构对虚构

看到了吗?这不仅是一个错误的API字段,而是一个纸上谈兵的构造:DTO定义了一个不存在的字段,业务逻辑依赖该字段,测试用虚构数据验证这个逻辑,也一致验证通过。每个部分彼此确认,从表面来看完全无懈可击,而实际上却毫无真实性可言。

《IEEE Spectrum》给这种现象起了个名字:无声失败(silent failure)。代码不会崩溃,也不会抛出错误或发出警告,它只是安静地输出错误的结果。

这并不是孤立事件

社区中已经为LLM虚构包和依赖的现象起了名字:包幻觉(package hallucination)。根据Snyk的一项研究,主流LLM推荐的包中有5%到20%是虚构的,也就是说这些包根本不存在。

但包的情况已经算是简单的了。当你运行 npm install 虚构包 时会失败,所以很容易发现。而像DTO里虚构的API字段那样,如果使用 try? 来解析JSON并用降级机制处理异常,它并不会显性地出错。即使返回了nil或者空数组,你的代码也能继续运行,处理这些不存在的数据。

即便是Anthropic公司在他们关于降低幻觉的官方文档中,也毫不掩饰地表达了这个问题:

“Claude有时可能会生成包含虚构信息的回复……并且这些信息通常会以一种十分自信和权威的方式呈现。”

以“权威的方式”呈现,这才是关键所在。问题不在于它不确定或偶尔出错,而在于它满怀信心地虚构内容。

为什么测试无法拯救你?

这就是最让人痛苦的地方。我有测试,而且是非常好的测试。90个测试分布在12个测试套件中,所有测试全部通过。又如何呢?

问题在于,测试验证的是内在一致性,而非与现实的匹配。如果DTO声明了一个字段 active_flags,样例数据也包含 active_flags,测试就会验证DTO能解析该字段……一切通过。虚构验证虚构,绿灯通行。

这就像一个学生自己编造了一条物理公式,再用这条公式完成了一份考试卷,并给自己打了满分。每一步看起来都是内部一致的,但结果却和真实的物理世界毫无关系。

现实:API中不存在字段X
   ↓(不可见)
DTO:定义了字段X         ← 虚构的
样例数据:包含字段X      ← 虚构数据验证DTO
测试:DTO能够正确解析样例数据 ← 用虚构验证虚构
结果:✅ 全部通过       ← 连贯的虚构现实

在这条链条中的任何一点上,都没有验证是否对应真实的API。而这就是问题所在。

现有措施都是预防为主

如果你正在寻找预防此类情况发生的方法,当前文献和经验提供了一些建议。但所有这些措施都是预防性的:

措施类型问题
在CLAUDE.md文件中写明说明:“不要编造”预防性执行这些规则的正是会编造的AI
推理链:“引用你的来源”预防性可能会引用虚构的来源
降低温度预防性降低创造性但无法完全避免虚构
基于文档的根源性检查预防性仅当文档是完整时有效
明确禁令行为预防性LLM可能会"合理化"其例外情况
搜索增强生成(RAG)预防性依赖于数据库的完整性

注意到规律了吗?这些措施都是为了避免AI编造,但没有一个可以检测AI何时已经编造了数据。

这就像在一家没有安保摄像头、报警器或保安的商店里挂上“禁止偷窃”的标语。或许会奏效,也或许不会。而你只有在清点营业款时才会发现问题。

缺失的核心:事后检验

我们急需的,也是目前还不存在的,是一套事后检测系统:一种能在虚构事实发生之后,特别是在进入生产环境之前检测出问题的机制。

设想以下场景:

  • 基于真实API的合同测试(Contract Testing):一种能够调用真实API(使用测试用的凭据)并比较实际数据结构和DTO的测试。如果DTO中存在API未返回的字段,就应当报警。

  • 样例数据验证:一种检查测试样例数据是否基于真实捕获的数据,而非手动编写或由AI生成的检查工具。这类似于快照测试(snapshot testing),但针对实际生产环境中的真实响应。

  • 使用真实数据的冒烟测试(Smoke Test):在合并代码之前,CI系统中增加一个步骤,使用API的沙盒环境执行调用,验证DTO可以无问题地解析真实数据。

  • 解析异常检测:如果一个标记为可选的字段在生产环境中100%都会返回nil,显然不正常。可以通过监控无数据或异常数据的字段并报告问题来检测。

  • 生成后的语义差异检测:通过第二个模型(或使用不同提示词的同一模型)来检查生成的代码,并标记与已知文档不一致的字段或结构。

今天,这些功能中没有一项以产品的形式存在。有些团队可能会手动实现一些方法(比如合同测试就是一种常见实践)。但当前市场上还没有一个类似*幻觉追踪器(HallucinationTracker)*的工具,能直接插入到你的CI中告知你“嘿,这个 active_flags 字段在API的文档或实际响应中都找不到”。

是的,华盛顿大学确实有一篇论文(HallucinationTracker),提出了一些检测虚构的度量方法。但目前还处于研究阶段,并未发展成为一个可直接使用的产品。

更深层的困境

更深层的问题令人感到不安:执行规则的系统与打破规则的系统是同一个。

当你在CLAUDE.md中写上“不要编造数据”时,你是在对着那个可能编造数据的同一个模型发号施令。这就像让被告同时充当审判他的法官。虽然可能会奏效,但你却无法获得保障。

预防性措施(良好的说明、降低温度、根源性校验)可以减少编造的概率,但无法完全消除。而一旦问题发生,没有警报提示你。

我们需要的是由AI外部工具来进行检测的机制:测试真实数据、一种验证数据结构的工具、生产环境中的监视器。这类技术让AI无法再轻易通过解释或忽视来逃避。

在这种技术成熟且易于使用之前,我们的处境和防火墙发展起来之前的网络安全状况类似:我们意识到问题存在,有一些不完备的应对措施,但只能期望“这问题不会出在我身上”。

我现在会怎么做

坦白说,这是我目前在用的解决方案,各有不完美之处:

  1. 像对待陌生人的代码一样审阅生成的代码。 不要因为代码可以编译就假设它是对的。这很耗费精力,但没办法,只能这样。

  2. 问自己:“这是从哪里来的?” 特别是对于API字段、包名以及我无法通过查看代码直接验证的数据。

  3. 手动合同测试。 在认可DTO之前,通过实际调用API来对比和验证。这过程虽然繁琐,但有必要。

  4. 对首次通过的测试持怀疑态度。 如果AI生成代码和测试且一次就通过,这不一定是好信号——反而可能表明虚构的代码被虚构的测试通过了。

  5. 用真实捕获的API响应数据作为样例数据。 而不是让AI生成样例数据。这样一来,如果DTO无法正确解析真实数据,会立刻暴露问题。

这些方法都需要手动操作且速度很慢,依赖个人的自律,无法扩展。但目前这是我能采取的最佳措施。

明天应该有什么

如果有人正在寻找一个真实世界的问题来解决,这就是一个:

一个自动化的、独立于模型之外,且可以集成到CI/CD流程中的生成后验证系统。

这个系统不需要做到完美,但它需要真实存在。它应该是类似linter的工具,用于分析生成的代码,核对可验证的数据来源(如API文档、OpenAPI schema、捕获的真实响应),并标记可能不符合真实情况的内容。

目前,如果你的AI虚构了一个API字段并用一套逻辑包裹,以及一系列协调的测试,那唯一的防御措施就只能靠你自己有火眼金睛。然而在未来,应该有机器能自动完成这些工作。

但现在这种工具依然没有。这才是整个问题最令人担忧的地方。


相关内容: 本文是一个非正式系列的第三篇。第一篇是44封虚构的邮件(AI在未授权的情况下采取行动)。第二篇是MEMORY.md(AI忘记它所学习的内容)。现在,这是关于AI编造的数据并用逻辑协调地通过测试的文章。三个不同的错误,一个共同点:我们对一个不真正理解自己所做事情的系统过于信任。

本文原文为西班牙语,借助AI翻译。