AI 明明"读"了我上传的文档,为什么还会答错:RAG 讲人话
把一份几十页的说明书丢给 AI 助手,问"卡纸怎么办"它答得又快又准还标页码,换个问题它却说"文档里没提到",而答案明明就在倒数第二页。这两种表现背后是同一套机制:模型根本没有"读"你的文档,它是现查的。把 RAG 拆成切、找、喂三步讲清楚,再把"答错"拆成两道关:没找到,和找到了没用好。这两道关的修法完全不同,分清了才不会白换模型。顺带说说窗口大了为什么还要 RAG,以及 Claude Code 为什么干脆不用向量库、让模型自己 grep。
很多人都碰到过这样的场景:把一份几十页的设备说明书丢给 AI 助手,问”卡纸了怎么办”,它答得又快又准,还标了页码。过几天换个问题,“这台机器保修多久”,它一本正经地说”文档里没有提到保修信息”。翻开说明书,倒数第二页一张表里明明白白写着”质保期 24 个月”。
第一反应通常是:它到底读没读我的文档?之前写《大模型为什么会”忘事”》时讲过,模型一次能读进去的东西有限,几十页的文档并不总能整本塞进去。那它是怎么处理我上传的文档的?答案是:它没读,它是查的。这套查法叫 RAG,Retrieval-Augmented Generation,检索增强生成。搞清楚它怎么查,上面一准一错两次回答就都能解释了。
不是读,是查
RAG 做的事拆开只有三步:切、找、喂。

切。文档上传的那一刻,它被切成几百段,每段几百字,首尾通常留一点重叠,免得一句话被拦腰斩断。说明书里”卡纸处理”那一节可能变成两三段,保修那张表单独一段。每一段切出来时都带着一个标签:我来自第几页。
找。这是最关键的一步。每一段文字被送进一个叫 embedding 的小模型,变成一串几百到上千个数字。这串数字的妙处在于:意思相近的文字,数字也相近。“打印机卡纸怎么办”和”纸张卡在机器里的处理步骤”,字面几乎没有重合,但在这个数字空间里它们挨得很近;“卡纸”和”下季度预算”则离得很远。你的问题也被变成这样一串数字,然后系统在几百段里找出离它最近的几段,通常三到十段。

喂。找出来的这几段,连同你的问题,拼成一段提示词一起送给大模型。模型实际看到的东西长这样:“根据以下资料回答问题:[第 12 段][第 13 段][第 40 段]。问题:卡纸了怎么办?“整本说明书从头到尾没有进过模型,进去的只有这几段小抄。它答得对,是因为送到它面前的那几段里正好有答案;它能标出页码,是因为每段身上贴着第一步留下的标签。
这就是”检索增强生成”这个名字的字面意思:先检索,再生成。模型本身没有变聪明,也没有学会你的文档,它只是每次答题前被塞了几张小抄。
为什么还会答错:两道关
理解了三步,“答错”就可以拆成两种完全不同的错。
第一道关:没找到。模型压根没见到答案所在的那一段。开头那个保修问题多半就死在这里。可能的原因很多:
- 切坏了。表格被切成两段,“质保期”在上一段末尾,“24 个月”在下一段开头,哪一段单独看都不像在讲保修。
- 问法和写法对不上。用户问”保修多久”,文档写的是”质保期”。embedding 能兜住一部分同义表达,但不是万能的,行业黑话、缩写、型号编码这类东西经常兜不住。
- 答案分散在多处。“哪个季度增长最快”需要四个季度的数字,只捞回来两段,模型看到的就是残缺的。
- 名额不够。只取最近的五段,答案在第六段。
第一道关的失败有一个典型症状:模型回答”文档里没有提到”。它没有撒谎,它拿到的那几段里确实没有。但用户会以为它读了全文。
第二道关:找到了,没用好。答案所在的段落确实送进去了,模型还是答偏了。
- 段落之间打架。一份文档改过好几版,旧的说 12 个月,新的说 24 个月,两段都被找回来,模型挑了旧的。
- 埋在中间被忽略。2023 年有一篇论文叫 Lost in the Middle,发现模型对提示词开头和结尾的内容记得牢,对中间的内容明显走神。找回十段,关键的那段排第五,就容易被漏掉。
- 模型更信自己。它训练时见过的通用说法和你文档里的说法冲突,它有时会不自觉地按自己的记忆答,把小抄晾在一边。
- 小抄太多反而是噪音。为了保险把二十段全塞进去,注意力被稀释,每一段都看不仔细。

这两道关的区分在实践里非常有用。同一个”答错了”,如果卡在第一道关,换个更大的模型没用,因为它压根没看到答案,该修的是切法和检索;如果卡在第二道关,换模型、改提示词、加一个排序器把最相关的段落挪到最前面,才是对症的。出了问题先翻检索日志,看它到底捞回了哪几段、答案在不在里面,往往一眼就能分出是哪道关。
窗口都能装几十万 token 了,为什么还要切
一个自然的疑问:现在的上下文窗口动辄几十万 token,几十页说明书完全放得下,还费劲切什么?三个理由。
第一,钱。一份几十页的文档是几万 token,每问一个问题都要重读一遍。上一篇讲的前缀缓存能省一部分,但那是打折,不是免单。
第二,量。真正有用的场景很少是一份文档,而是一个知识库:几千份工单、几百份合同、整个 wiki。这时候窗口再大也装不下,必须先筛。
第三,就是前面说的 Lost in the Middle。塞得越多,模型越走神。只喂最相关的几段,反而比全喂更准。
所以现实里两种做法是混着用的:资料不多就直接整个塞进去,量大就先筛再喂。RAG 没有过时,它是从”唯一的选择”变成了”工具箱里的一件”。
现在的变化:让模型自己决定查什么
上面讲的是最经典的 RAG:查一次,答一次。这两年一个明显的变化是把”查”的主动权交给了模型自己。
最直观的例子是我每天在用的 Claude Code。它要在一个几万个文件的代码库里找东西,用的不是向量检索,而是像人一样:先 grep 一个关键词,看看结果,不对就换个词再搜,找到文件再打开读。它的作者说过,早期版本试过向量库那一套,后来发现让模型自己搜反而更准,还省掉了维护索引、索引过期、代码外泄这一堆麻烦。
这种做法的本质,是把”找资料”从一个固定的流程变成了模型的一个动作。它可以查完发现不对再查,可以先查目录再查正文,可以把一个复杂问题拆成三个小问题分头去查。大家给它起了新名字,Agentic RAG,或者更时髦一点的,上下文工程。名字换了,核心还是那句话:模型答得好不好,取决于你在它答题前往它面前放了什么。
我的看法
RAG 最常被误解的一点,是以为它让模型”学会”了你的资料。没有。它只是把”找资料”这件事从模型身上卸下来,交给一个搜索系统,然后把搜到的东西临时摆在模型眼前。所以一个 RAG 应用的上限不是模型定的,是检索定的。找不对,再强的模型也是巧妇难为无米之炊。
下次 AI 助手说”文档里没有提到”的时候,先别信,也别急着说它笨。翻翻它到底看到了哪几段。多半不是它笨,是它没看见。
评论