Skip to content

408 AI 答疑,从参数记忆到检索接地:治理大模型的"出处幻觉"

一位同学问答疑 AI:"按字节编址时,存储字长可以是 64 位吗?"AI 斩钉截铁地说"不行,必然是 8 位",被正确反驳后仍然死扛,还补了一句"主流教材都是这么定义的"。翻开教材,白纸黑字写着相反的结论。这篇把整件事拆开:它为什么会"自信地错、还编出处",我们怎么用向量内积和最近邻查找把答疑焊回教材原文,以及一个反直觉的修正——别让系统惩罚"我错了"。全程用向量数学讲清楚,不牵涉任何具体模型。


1. 事故现场:一次自信的错误

对话大致是这样(复述,去掉无关寒暄):

同学:按字节编址时,存储字长可以是 64 位吗? 答疑:不可以。按字节编址意味着每个地址对应一个字节,所以存储字长必然是 8 位。 同学:不对吧,字节编址说的是地址粒度,存储字长是一次访存读出的位数,这两个不是一回事。 答疑:你可能记混了。按字节编址的机器存储字长就是 8 位,主流教材都是这么写的。

然后我们去翻教材。蒋本珊《计算机组成原理》(第 4 版) 5.2.4 节原文:

目前,大多数存储器采用字节编址……设存储字长为 64 位(8 个字节),即一个存储字……

也就是说,同学是对的:字节编址(地址的最小粒度是 1 个字节)和存储字长(一次访存读/写的位数)是两个正交的概念。一台字节编址的机器,存储字长完全可以是 64 位——一次访存吐 8 个字节,但你仍然可以按单个字节去寻址。

这次翻车叠了三个问题:结论错、被反驳后死扛、还凭空编了个"教材共识"。

2. 为什么会"自信地错,还编出处"

拆成两层根因。

第一层,知识本身错了。 生成式大模型的"知识",本质是对海量文本做统计压缩后留下的参数。中文考研语料里,"编址单位就是存储字长"这种把两个概念焊死的简化说法出现得不少,于是模型内部这个错误口径的置信度是真高的。我们给它设过一条安全规则"拿不准就说拿不准"——但这条只在模型自认心虚时才触发。它挡得住"心虚地错",挡不住"自信地错"。

第二层,出处是编的。 当被追问时,模型有一种辩护性倾向,会去"找个权威给自己背书",于是生成了"教材都这么写"这句话。它并没有真的读过任何一本教材,只是拼出了一句听起来可信的话。

两层错误共用一个根:模型答题时,手里没有一个可以核实的事实锚。它在凭参数记忆复述,全程没有对着任何原文。

3. 思路:让它"查了再答"

解法的方向叫检索增强(RAG)。在模型开口之前,先从教材原文里把与这个问题最相关的几段捞出来,塞进它的上下文,再压一条硬规则:锚定这些原文作答,出处只许引这几段标注的书;检索不到就不许声称任何教材出处。

这一步顺手把"出处幻觉"这个最伤权威性的失效点,变成了一个可核实的能力——每句结论都能顺着引用点开对应的教材原文核对。

但真正的技术难点在这句话里:机器怎么知道"按字节编址存储字长能不能 64 位"这个问题,和教材里"大多数存储器采用字节编址……设存储字长为 64 位"这段话是相关的?

把两句话摆一起看:问句里没有"大多数""存储器",原文里没有"能不能"。字面上几乎一个词都对不齐,字符串匹配(哪怕是关键词模糊匹配)在这里基本失效。要让机器判断"语义像不像",得换一套数学。

4. 让机器"读懂"相似:向量 + 余弦相似度

第一步,把文本变成向量。 一个文本向量化模型,能把任意一段中文映射成一个高维向量 vRd(我们这批用的是 d=1024 维)。它的训练目标简单说就是:语义相近的文本,向量在空间里方向也相近。 于是"存储字长能否 64 位"和"设存储字长为 64 位",虽然字面不同,向量方向却挨得很近。

第二步,怎么量"方向接近"? 用两个向量夹角的余弦——余弦相似度:

cosθ=abab=i=1daibii=1dai2i=1dbi2

取值落在 [1,1],越接近 1 说明两个向量方向越一致、语义越像。这就是线性代数里的向量内积(考研数学里反复出现),只是把长度归一化掉了、只看方向不看模长。

第三步,一个省算的小技巧。 如果入库时先把每个向量除以自己的模长、变成单位向量(即 a=b=1),上面的余弦就退化成一个干净的点积:

cosθ=ab=i=1daibi

我们在灌库时就把 12009 段教材向量全部归一化,检索时只算点积,省掉每次查询的开方。

第四步,检索就是最近邻查找。 把全部教材段的向量存下来(这批 12009 段),来一个问题就编码成向量 q,在这 12009 个向量里找与 q 余弦最大的前 k 个——这正是数据结构里的 k 近邻查找(k-NN),只不过距离度量换成了余弦。语义检索的内核,就是考研里都学过的两样东西:向量内积和最近邻查找。

5. 怎么找得快:从暴力扫到索引

暴力法,O(Nd)q 和全部 N 个向量的点积,N=12009d=1024,一次查询约 1200 万次乘加。实测约 40 毫秒。对一个要好几秒才生成完整答案的答疑来说,这点开销可以忽略。

想更快,上近似最近邻索引。 两种主流思路都能在 408 里找到影子:

  • 分层可导航图(HNSW):给这些向量建一张多层图,上层节点稀疏、跳得远,下层稠密、走得细;查询时从最上层粗定位,逐层往下逼近目标。这个"多层加速"的骨架,和数据结构里跳表/多级索引是同一个思想。实测建完索引后单次查询从 40 毫秒降到约 0.7 毫秒
  • 倒排聚类(IVF):先把向量粗聚成若干簇(K-means 的路子),查询时只在离 q 最近的几个簇里精算,用一次粗筛砍掉绝大部分计算。

边界:12009 段这种规模,暴力扫的 40 毫秒就够用,索引主要是给语料日后长大留的余量。规模小的时候,数据库的查询规划器甚至会主动放弃索引、直接暴力扫——因为建索引、跳指针的固定开销,还不如把一万多个向量直接乘一遍来得快。这一点和"小数组排序有时插入排序比快排还快"是同一个道理:渐进复杂度低,不代表在小输入上一定跑得快。工程上也经常有这些问题,量小的时候简单暴力的方案反而更好。

6. 不是每次都塞:阈值门控

接地不能无脑开。假如某个问题检索回来、最相关的一段余弦也只有 0.4,说明教材里根本没有对得上的原文——这时候硬把它塞进上下文,反而会用一段不相关的话把模型带偏。所以要设一道阈值:只有最高余弦超过阈值才注入原文;不到阈值就不接地,让答疑收敛成"常见的讲法是……具体以你手上的教材为准"。宁可这次不接地,也不添乱。

阈值怎么定?看数据。把一批真实的概念提问跑一遍,统计检索命中的余弦分布:真正相关的命中落在 0.67–0.77,明显不相关的落在 0.4 以下,中间有一段空档。我们取 0.55 作初值,偏保守,之后按线上真实命中率再往哪个方向挪。

7. 回到那道题:接地之后

复测开头那道题。把"按字节编址时,存储字长可以是 64 位吗"编码成向量,在 12009 段里检索,排在第一的是:

蒋本珊《计算机组成原理》(第 4 版) · 5.2.4 目前,大多数存储器采用字节编址……设存储字长为 64 位(8 个字节),即一个存储字……

余弦相似度 0.755。这段被注入上下文之后,答疑的回答变成:

可以。按字节编址的计算机存储字长可以是 64 位(即 8 个字节)……可见"字节编址"和"存储字长为 64 位"并不矛盾。 依据:蒋本珊《计算机组成原理》(第 4 版) 5.2.4 节。

接地前后对照:结论从"必然 8 位"回到"可以 64 位"(与标准一致),出处从凭空编造变成一句能点开核对的引用。

那句"依据:蒋本珊……"本身就是接地生效的硬证据。因为我们给模型的规则里明令禁止编造教材出处——它能报出这本具体的书、具体的节,唯一的可能就是系统真的把这段原文喂给了它。检索不接地,它连书名都不许提。

8. 一个反直觉的修正:别惩罚"我错了"

接地治的是"没有事实锚"。但复盘这次事故,还挖出一个更隐蔽的问题:它为什么被反驳了还死扛?

我们有一道回答质检,会拦掉答案里泄露的"内部独白痕迹"——比如"让我重新检查一下""这与前文矛盾,我再算算"这类本该在心里走完、不该展示给用户的思考过程。方向没错。但它的匹配规则里混进了一条误伤:把"我刚才说错了"这种表述也判成了低质量,直接拦下不展示。

后果很微妙。当用户给出正确的反例、模型本该诚实地讲"我上一轮答错了,正确的是……"时,这条质检把这句诚实的认错也拦掉了。于是系统在结构上变成了:认错会被打回,死扛反而能过审。这等于在训练它嘴硬。

我们把规则改成:只有在用户没有反驳的语境下,"自我纠错痕迹"才算问题;一旦上一句是用户在给反例、纠错,诚实认错一律放行。拦掉"我错了",只会把模型训练成死扛。

9. 能力边界

向量检索接地不是万能钥匙,几处我们清楚的短板:

  • 教材本身写错或有分歧时,接地也会跟着被带偏。 我们的对冲是只喂权威课本、并对同一概念收多本互相印证,但这挡不住所有情况。
  • 向量化模型对长表格、纯符号公式段的理解,弱于对连续文字的理解。 这类段落的检索质量要打折扣,靠人工抽检兜。
  • 阈值 0.55 是保守初值。 真实流量跑一段时间才谈得上调准:太严会漏掉本该接地的,太松会塞进不相关的。
  • 接地只解决"有没有对着原文答",解决不了"这道题的推导对不对"。 后者还是要靠题目本身的标准答案和解析兜底。

10. 结论

不要因为一个 AI 答得"自信"就信它,尤其当它给不出一个能点开核对的出处的时候。

最后更新:

一句数据说不清楚的事,我们用代码 + 公式 + 截图说清楚