纯粹的谎言很容易被识破。你只要追问一句"证据呢",它就站不住脚。
但一个只包含部分事实的陈述,很难被识破。它句句属实,每一个细节都经得起查证——你查了,都对;你信了,结论却是错的。
这篇文章想讲清楚一件事:AI 最危险的一类错误,不是幻觉,而是"部分事实"。 我会用一个差点让我做错决策的真实经历说明它有多隐蔽,然后给出两套应对方法——一套靠人,一套靠"让 AI 给 AI 挑刺",后者我已经做成了一个能直接拿去用的skill。
一个差点让我把计费逻辑迁移错的 spike
几个月前,我负责一个 spike:摸清老系统的计费规则,把它迁移到新系统。我理所当然地打开 AI,问它:"这个业务是怎么计费的?"
AI 很快给出总结:每个新广告都会重新计费。 听起来非常合理——每个广告独立算费,这是很常见的计费模型。而且它没有空口白话,把计费的入口类、实现类都列了出来。我顺着链接一个个看过去,代码真实存在,逻辑和它说的一致。
那一刻我特别踏实:有代码出处,回答靠谱。我甚至已经基于这个结论开始设计迁移方案了。
直到我把总结拿给 BA 验证。BA 看了一眼,非常肯定地说:"不对。不是重新计费,是增量计费——只收取新广告新增功能的费用。"
我有点懵。我让 AI 重新查证。这一次,AI 承认:上一次查询代码库并没有查全,漏掉了一个分支。 而重新总结后暴露的问题,比"漏一个分支"更深一层——它说的"费用",和我说的"费用",根本不是同一个费用。 它总结的是"基础费的计算逻辑",而我要的是"最终向用户收取费用的逻辑"。基础费每次都会重新计算,但在这条路径后面,还有一条计算链条,会减免已经收取的费用。增量计费,就藏在我没让它继续追的那段链条里。
回头看,整个过程最让我后背发凉的一点是:没有任何一步会让我主动发现问题。 AI 的答案验证全过——入口是真的,实现类是真的,逻辑自洽。如果不是 BA 恰好知道业务全貌,我会带着一个错误的理解,把老系统的计费逻辑原样搬进新系统。
为什么"部分事实"比幻觉更危险
我们习惯把 AI 的错误叫作"幻觉"——编造出不存在的东西。但我越来越觉得,有一类更隐蔽的错误值得单列出来:"部分事实"。它的每一句话都真实,却比幻觉更难对付。
关键在于验证的不对称性。
幻觉的内容是"不存在的"。它的验证方式是"查无此物"——你去查,查不到,它就露馅了。所以幻觉的失败是可见的失败:它逼着你怀疑,而验证通常能抓住它。
部分事实恰好相反。它里面的每一个事实点都真实存在——代码查得到,逻辑对得上。你单独验证任何一句,都通过。失败发生在完整性,而不是真实性:它没撒谎,只是没说完。
而完整性,恰恰无法通过"逐句验证"发现。你只能通过"知道全貌"来发现——这正是资深成员和 BA 的作用。
于是两条路径分道扬镳:
我们习惯了问"这是真的吗",却很少问"这是全部吗"。部分事实利用的,正是这个习惯。
所以这里藏着一个反直觉的结论:AI 的答案越可信、越经得起逐句验证,反而越危险——因为验证非但不会戳穿它,还会加深你对它的信任。
图:幻觉死于验证,部分事实却借验证加冕——越可信,越危险
同一个故事里,藏着三层陷阱
回到那个计费的故事。把这次事故拆开,它其实是三层叠在一起:一层"验证过"了,两层"漏"了。而最狡猾的,恰恰是"验证过"的那一层——它让你以为检查通过了,真正的错误才就此溜走。
套用上一节的那两问:正确性回答的是"这是真的吗",完整性回答的是"这是全部吗",歧义性回答的则是"你说的是我说的那个吗"。我们习惯问第一问,很少问第二问,几乎从不问第三问——而第三问偏偏决定了前两问该往哪问。
最有意思的是歧义性如何掩盖了完整性。因为"费用"这个词没对齐,AI 默认理解成"基础费",于是它走到基础费的计算逻辑就停了——它甚至没意识到后面还有一条减免链条。概念一错,搜索的范围就跟着错了;范围错了,完整性自然无从谈起。
你以为是"它少查了一个分支",实际上是"它连该查什么都没搞清"。
这三层叠在一起,才是"部分事实"真正危险的地方:正确性给你安全感,完整性让错误悄悄发生,歧义性让错误发生的方向都偏了。
图:正确性给安全感,完整性让错误悄悄发生,歧义性让方向都偏了
能不能让 AI 自己发现漏了什么?
这三层里,完整性最难防:正确性可以逐句核对,歧义性可以靠追问澄清,唯独完整性,你根本不知道它缺在哪。于是问题自然浮上来:能不能让 AI 自己验证完整性?
我的结论是:不能"保证",但可以"逼近"。
不能保证,是因为 AI 没有"全貌"作为参照系。它无法证明"没有其他分支了"——它不知道它不知道什么。 直接问它"你查全了吗",它多半会答"查全了":它既没有对照物来定义"全",也不会主动暴露自己的搜索盲区。"自证完整"这件事,对 AI 来说在原理上就不成立。
但可以逼近,是因为我反复验证过一件事:反复让 AI 确认,它真的会有新的发现。 计费故事就是证据——当我带着"不对"这个信号让它重新查证时,它自己找出了漏掉的分支,承认上次没查全。质疑,能逼出更多信息。
所以问题从"怎么让 AI 证明自己说全了",变成"怎么设计一套机制,让 AI 不断地重新检查"。这才是可解的。
方法一:信息注入循环
我目前最常用的方法,可以叫信息注入循环:额外提供信息——自己发现的,或者问别人得到的——然后让 AI 再确认一次。
计费故事里就是这样:BA 说"不对",我把这个信号带回去,让 AI 重新查证,它才找到漏掉的分支。信息注入 → 触发重新搜索 → 发现遗漏,这是一个能稳定复现的循环。
但这个方法有个软肋:它需要一个信息来源。 你自己发现的线索,或者某个懂行的人——在这个循环里,它们是"燃料"。而现实中我们经常没有燃料,或者不知道去哪里找燃料。
上下文不完整,答案就不完整;而"上下文完整"这件事,恰恰没有人替我们保证。
方法二:挑刺 agent
方法一的软肋是缺燃料——外部线索或懂行的人,不是随时都有的。于是我想到了一个更结构化的做法:设计一个专门负责"挑刺"的 agent,让它对另一个回答问题的 agent 不断提问,迫使对方反复确认。
这个挑刺 agent 有一个反直觉的特点:它不需要知道更多,甚至不需要懂业务。 它只需要一件事——保持对另一个 agent 的怀疑,然后提出问题。
为什么不需要懂?因为"发现不知道的不知道",靠的从来不是知识,而是怀疑。它不需要知道答案,只需要扮演那个永远说"你再想想"的人——就像审稿人,他不是作者,但他能让作者重新翻卷宗。
这看起来和我前面说的"只能通过知道全貌来发现"矛盾——其实不矛盾:靠人发现,是人的脑子里装着全貌;靠挑刺 agent,是全貌就躺在代码库里,它不需要自己知道,只需要逼 AI 重新去翻,把盲区逼出来。
那它问什么?我把问题收敛成三个方向,称之为三维提问框架:
完整性:你覆盖了所有分支、调用点、边界情况吗?哪些分支你确认过"不存在"?
正确性:你每个说法都和源代码对得上吗?出处是哪一行?
歧义性:你理解的概念和我说的概念是同一个吗?比如"费用"——基础费、促销费,还是总费用?
仔细看,这三个方向就是上一节那张检查表上的三个维度——挑刺 agent 要做的,无非是每次把这张表对着 AI 重新问一遍。
这个框架不是凭空想出来的,它对应着成熟的研究方向:
多 agent 辩论(Multiagent Debate)早就证明:质疑者不需要知道答案,只需要提出不同立场,就能提升回答的事实性——这正是挑刺 agent 的学术依据。
苏格拉底式提问里,"澄清概念"排在六类问题的第一位("你说的 X 指什么?")——歧义性那一维,人类已经问了几千年。
歧义性是需求工程里被研究了几十年的老问题(见 NLP4RE 系统性综述);而"AI 在信息不足时主动澄清"也早有研究(Clarifying Questions)——AI 与其猜,不如问。
说句实话,这三个方向只是起点。等我真正动手把它做成能用的东西时,发现还得再补三个——那是下一节的事。
但挑刺循环有一个适用边界,必须说清楚:它只在"信息可达"时有效。 当没有第三方信息源、而源代码本身已经包含全部信息时,靠"穷举 + 拒绝相信"可以把答案逼近完整;但当信息根本不在代码里——比如业务决策、历史背景——挑刺也问不出来,你必须回到"信息注入",去找那个懂行的人。计费故事里,如果不是 BA 先点了一下方向,AI 的重新查证很可能还是抓瞎。
我把它做成了一个 skill:question-everything
前面这套"挑刺",我把它从想法做成一个可以反复使用的 skill,名字就叫 question-everything:质疑并验证任何 AI 返回的结果。它几乎把我在这篇文章里讲的每一句话,都翻译成了能执行的规则。(代码开源在 question-everything,你可以直接拿去用。)
第一件事,把"怀疑"变成默认姿态。skill 开宗明义:把每一个返回结果都当成"未经证实的声明",默认立场是怀疑而非信任——信任要靠提问和独立验证挣来。这正好回应了前面的结论:让你安心的答案,恰恰最该被怀疑。
第二件事,质疑只针对单个主张,不针对整个结果。每个挑战必须绑定一句具体的话,说清"它为什么可疑",并写死"什么样的证据才算过关"。这逼着挑刺者不空泛地说"我觉得不对",而是说"这句可能不对,因为……;如果……我就信"。然后按影响排序——先挑那个会害你做错决策的。
第三件事,验证循环,且必须是"新的人"。派一个新的同类型 sub-agent,对着主源(代码、文档、数据、日志)逐条验证;绝不用原来的那个 agent 实例——因为原来的 agent 会为自己辩护、会锚定在自己已经写下的输出上。你发现了吗?这就是"信息注入循环"的自动化版:每次验证都强制换一个新实例去翻主源,等于注入一次不受上一个结论污染的独立信息。
第四件事,诚实承认天花板。循环最多三轮,到上限就把"原版答案 vs 验证版答案"都摆到你面前,绝不偷偷替你选一个。这正是"不能保证完整、只能逼近"的落地方式——它从不假装自己证明了完整,它只是把"逼近"这件事变得可重复、可停止。
最有意思的是,当我动手写这个 skill 时,发现"三维"不够用了。写文章时,完整性、正确性、歧义性足够讲清那个故事;可一旦想让它"验证任何 AI 返回的结果",就必须更完备。于是它补上了另外三个维度:
其中"证据"这一维,抓的是"转述":你的主张到底有没有主源,还是只是道听途说。计费故事里 AI 给的出处就是代码,是主源,不是转述——这一维其实是"过"的。但请注意:"证据过了"不等于"覆盖全了"。"引了主源"和"把主源相关的所有路径都查全"是两回事,后者归"完整性"那一维管。skill 追的就是这层区别——连主源都给了,照样可能没查全。
用六维对一条 AI 结论快速质疑一遍,大概长这样——假设 AI 对我说:"这个业务是重新计费的,入口、实现类我都列给你了。"(这正是当初差点让我做错事的那句话):
注意看这张表的结果:正确性、一致性、证据都"全过"——答案自洽、出处真实、而且出处就是主源,不是转述——可真正翻车的,是完整、歧义、假设三维。最隐蔽的地方就在这:它连主源都给了你,照样是错的。 出处的"真",永远替代不了范围的"全"。如果当初有人这么问一轮,我根本不用等 BA 点破。
最后,连它的边界都和我前面说的一模一样:验证必须对着主源做;如果信息根本不在任何主源里,它不会假装验证通过,而是诚实地报一声 UNCERTAIN:缺什么。所谓"信息可达",到 skill 里就是一句可执行的规则。
对团队来说,这套循环还能从"个人习惯"变成"团队默认"——把它当作评审时的检查清单,新人不用重新摸索,团队也不靠运气撞见那个懂行的 BA。怀疑,可以在团队层面变成流程。
图:质疑 → 验证 → 重做,或 3 轮上限呈现两版——怀疑成了可执行的流程
好问题的本质:报告"没查什么"
把这些方法放在一起,我意识到它们有一个共同点:好问题,是要求对方报告"没查什么",而不是"查到了什么"。
别问"你确定吗"——它会被"确定"糊弄过去。
要问"你查了哪些文件?哪些文件你没查?哪些分支你确认过不存在?"
这恰好呼应了故事里那个细节:AI 承认"上一次查询代码库并没有查全"。如果从一开始就要求它报告查询范围,这个盲区就不会是隐性的——它会明晃晃地摆在你面前:这里是没查完的,你自己看着办。
回到计费故事里验证一下:如果当初我问的不是"你说的对吗",而是"你查了哪些类?哪些没查?你确认过'重新计费'这条路之外没有别的分支吗"——AI 很可能当场就把减免链条翻出来,我也不用到 BA 面前才发现自己已经错了一步。
这也是软件测试早就在教的一课:覆盖率报告从来不只报告"测了什么",还要报告"哪些路径没覆盖"。
结论:从信任答案到核对答案
幻觉会让你警惕,部分事实却会让你安心。而安心,才是它最危险的地方。
应对它的方式,不是变得更焦虑,而是把怀疑变成工作流:让 AI 报告它没查什么,让一个专门挑刺的 agent 替你反复追问,让"核对答案"取代"信任答案"。
你可以今天就做起:下次拿到 AI 的答案,先问它一句"你查了哪些、没查哪些"。而我已把这条问法做成了能反复使用的 skill——文里的 question-everything,就是"永远替我们问好问题"这套系统的第一个雏形。
最后留一个问题给你:当 AI 的能力越来越强,我们最需要的技能,可能不再是"问出好问题",而是"设计一个永远替我们问好问题的系统"。这个系统,你打算从哪一步开始搭?当然,就算今天不搭系统,先把"你查了哪些、没查哪些"变成自己的默认问法,你也已经在核对答案了。