大模型为什么会幻觉:从事实性错误到 RAG 缓解方案全解
我在很多场合讲过这个问题。每次讲到一半,底下总有人露出一种表情——就是那种"你说得对,但你没说怎么修"的表情。
这个问题我断断续续研究了快两年。踩过的坑比能数出来的还多。现在回头想,最大的收获不是学会了某个框架或某种技巧,而是终于能把"幻觉"这个模糊的感觉拆成几个可操作的工程问题。
有次周三下午,我一个做后端的朋友发来一张截图。
他在用某个模型查一个开源库的最新 API 参数。模型回得特别流畅,format 整齐、解释到位、连代码示例都给好了。他直接复制进去跑,报错。查了十分钟才发现,那个参数根本不存在——模型自己编的。
他当时的原话是:"卧槽,它要是犹豫一下我还能发现,它他妈的说得太像了。"
我觉得这句话精准地捕捉到了这个问题的本质。
一个人类犯的错误和机器的错误之间,有一个根本区别:人类犯错时通常会伴随信号——犹豫、改口、不自信的语气、前后矛盾。这些信号提醒你该去验证了。
模型没有这些信号。它的流畅度不受真实性的影响。说"巴黎是法国首都"和说"巴黎是意大利首都"时,它所花的算力、所用的句式、所展现的自信,完全一样。
这就是为什么这件事让人不爽。你不知道该信什么。
这就是我最想聊的问题。
不是"模型会犯错"这件事——任何软件都会犯错。真正令人不舒服的是那个瞬间:你被一段看起来无比正确的文字骗过去了。你信了它,然后你自己动手去验证,发现什么都没有。
我们管这叫幻觉。但说实话,这名字不太好。因为它不是在暗示什么已经被研究透了的医学现象,它是一类非常具体的工程问题。
我想把我搞懂这些东西的过程写下来。从"卧槽它怎么会这样"到"好,我知道我能控制什么了"。
从一个让你不爽的瞬间说起
你先想象这个场景:
你在做一个客户问答系统。Demo 跑得很好,老板也很满意。然后你开始测试一些边界 case。
你问:"我们公司去年的退货率是多少?"
你给了它一份月度报表。表里确实有数据,但只到第三季度。
模型回答:"根据报表数据,贵公司去年全年退货率为 4.7%。"
你愣住了。4.7% 是个看起来非常合理的数字。但问题是——你根本没给它第四季度的数据。它自己补了。
这不是撒谎。它根本不知道什么叫撒谎。它只是在做它被训练要做的事:把话接下去。接得很顺,接得很像回事。
然后你意识到,更大的问题来了:你怎么跟你老板解释,为什么一个"知道自己在说什么"的系统,会自己编数字?
先把两种错误分开,不然后面全乱
我刚接触这个问题的时候,脑子里只有一个模糊的概念:"模型会胡说"。
后来我发现,这他妈的是两种完全不同的胡说。
第一种,我叫它事实性幻觉。就是模型说出了现实世界里不存在的事。
比如你问 OpenAI GPT-5 是哪年发布的,它回了一个年份。但你上网一查,根本没有 GPT-5 这回事。它编了一个时间线,还编得挺详细——什么技术突破、什么性能提升,有模有样。
但你只要去验证,就能发现它是假的。
这种事情的核心问题是:输出和真实世界对不上。
第二种,我叫它忠实性幻觉。这个更隐蔽。
它不是非要在客观世界上出错,而是不老实待在你给它设定的边界里。
比如你明确告诉它:"只根据下面这段合同回答问题,不知道就说不知道。"
它看了合同,里面只有两条违约责任。它回给你三条。第三条不是乱编的——那条在别的合同里确实常见。但它不在你给它的这个合同里。
这就是不忠实。
你给它一个文档,它自己加了点东西。你让它只提取原文,它偷偷做了一点推断。你让它说不知道,它非要给个答案。
这种幻觉之隐蔽,在于它经常不是"完全错"的。它往往是"半对"的——文档里确实有相似内容,但模型把它延伸了、泛化了、合理化了。你一眼扫过去觉得还行,但仔细核对就会发现不对。
忠实性幻觉的核心不是"事实对不对",而是:你有没有按我说的来?
为什么这两类必须分开
因为你对它们的处理方法完全不一样。
事实性幻觉,你要做的是:查。给模型一个外部知识源,让它别靠脑子猜。这就是后面要讲的一整套检索增强的逻辑。
忠实性幻觉,你要做的是:管。用 prompt 约束它,用引用要求它,用工作流限制它,用事后验证检查它。
很多团队上了 RAG 以后发现幻觉还在,就是因为搞错了对象。他们以为模型还在胡编事实,实际上去看一下——检索回来的资料明明是对的,但模型没照着用。
这时候继续优化检索,就跟头痛医脚差不多。
有意思的是,这两种幻觉还会互相喂养。一个事实性幻觉(编了一个不存在的数字)如果模型自己信了,它下一步可能基于这个假数字继续做推理。这时候忠实性幻觉跟上来了——它忠实于自己编的那个错误基础,继续往下犯错。环环相扣,越来越离谱。所以识别是哪类幻觉不仅是"分析方法",更是打断这种连锁反应的第一步。
模型到底是怎么干活的
要理解上面这些为什么发生,你得先接受一个有点反直觉的事实:
大模型不是一台真相查询机。它是一台文本续写机。
它训练的时候做的只有一件事:给定前面的一段文字,预测下一个词最可能是什么。
注意这个句式——"最可能",不是"最正确"。
这是一个非常根本的区别。
你可以这样想:一个搜索引擎的工作逻辑是"匹配事实",一个计算器的工作逻辑是"执行规则"。大模型既不是搜索引擎也不是计算器。它的逻辑更接近"如果历史上从来没有出现过月亮的自转周期这个话题,那我对这个词完全没概念;如果历史上出现过,但出现过很多种不同说法,那我给哪一个是概率问题"。
理解了这个,再去看后面那些花里胡哨的缓解方案,你就能一眼分辨出哪些方案是在补这个"概率生成"的先天短板,哪些方案只是在给输出做表面美容。
如果你给它"太阳从___升起",它大概率会填"东方"。不是因为它查过天文学教材,而是因为在它的训练数据里,"太阳"和"东方"这两个词就是经常挨在一起的。
如果你给它"太阳从___升起,根据最新天文观测",它可能还是填"东方"。但如果数据里碰巧有过一篇恶搞文章说"太阳从西边升起",它也可能在某些上下文里填"西边"。
它不知道哪个对。它只知道哪个更像它见过的文本组合。
这件事本身不是缺陷。正是这个能力让它能写诗、能翻译、能续写代码。但当你把它当成一个问答系统来用的时候,就出问题了。
你问它一个问题。在它的世界观里,这不是一个需要查证的问题——这是一段需要被续写的文本。
你给它的 prompt 开头是问题,它就要往下续答案。续出来的东西看起来必须要像答案。但至于像的那个答案是不是真的,不在它的训练目标里。
你可能会问:那为什么不用损失函数惩罚错误的预测呢?
好问题。但"错误"对于训练来说是什么?训练数据里有一个"正确答案",但那个答案只是语料库里的一个 token 序列——它不附带真实性标签。模型学到的目标是"更像训练数据",不是"更正确"。这两者之间的鸿沟,就是幻觉的根本成因。
这就是本源上的错位。
而且还有一个更微妙的点。模型不仅要预测下一个词,它还要学会在"不知道"的时候怎么处理。但训练数据里几乎没有"我不知道"这种输出。互联网上的人在不确定的时候,通常会给出一个听起来确定的答案,或者至少是一个听起来很专业的猜测。模型学会了这个。所以它不会说"我不确定"——不是因为它不诚实,是因为它根本没怎么见过这种表达。
在数据层之上还有一个更隐蔽的问题:训练数据是有偏的。
主流互联网上的内容偏向英文、偏向高收入国家、偏向某些行业的从业者、偏向有时间在网上发声的人。这意味着模型学到的"世界"是一个严重失真的样本。当它面对那些在数据里几乎不存在的人群、语言、场景时,它能调用的记忆要么是一知半解的碎片,要么是完全不相关的模式。
你让它写一份东南亚乡村女性的创业计划书,它可能自动套用了硅谷科技初创的模板。这不是幻觉。这是它脑子里只有那一个模板。
数据本身就有问题
如果你问一个工程师:"训练数据来自互联网,数据量这么大,为什么还会错?"
答案是:数据多,不等于数据真。
你仔细想想互联网上都在产出什么:
各种人把他们不完全懂的东西复述一遍。有人把科普文章缩写成抖音文案。有人在论坛上用很确定的语气说一件他其实只听过一半的事。有百科条目互相抄来抄去,源头是一篇已经过时的论文。
模型吃的就是这些。
这里面至少有几层问题。
第一层:数据里本来就有错。 过时的信息、伪科学、营销话术、不负责任的转述——这些在互联网上到处都是。模型学到的模式里天然就混杂了错误。
第二层:数据分布极其不均匀。 热门话题的数据量可能是冷门领域的几百倍。模型在热点上显得很聪明,但在长尾问题上突然就变了个人。因为它根本没怎么见过那些东西。
第三层:训练数据是冻结的。 预训练完成的那一刻,模型的世界就停在那里了。你要问它明天的天气、最新的政策、上个星期的产品更新,它只能靠猜。如果外部不补给它信息,它猜错是正常的。
第四层:人类语言本身就不是一套精确推理系统。 我们日常说话充满了省略、夸张、暗示、模糊、自相矛盾。模型学会的是模仿这些语言分布,而不是建立一个严谨的知识库。
所以你从一开始就不能默认"模型什么都知道"。这个假设本身就是错的。
但这不意味着你只能接受。只是方向不同——问题不在于"让它知道得更多",而在于"让它知道自己不知道什么"。这是两种完全不同的工程哲学。前者是填充知识空白,后者是标记知识边界。RAG 做的是前者,而后面会讲的流程控制和"允许说不知道"做的实际上是后者。两件事加在一起,才能把幻觉压下来。
模型自己也有结构性问题
就算数据完美了,模型本身的架构也有几层坑。
你可能会想:那为什么不在架构层面加一个"真值检验器"之类的模块?有人确实在尝试。但这件事的难点在于——模型内部对"事实"的表达是高度分布式的,分散在数百万个参数里面,没有一个集中存储的"事实表"可以拿去校验。你没法简单地"查一下模型里有没有这个事实"。你能做的只有前向传播,让它生成,然后外部分析生成结果。
它被训练的方式和它被使用的方式不一样
这个叫暴露偏差。
训练的时候,模型每预测一个词,看到的都是"真实"的前文。比如训练语料里有"巴黎是法国的首都",它学到的是:在看到"巴黎是"之后,历史上出现的是"法国的首都"。
但推理的时候,它看到的不是真实前文了——它看到的是自己刚刚生成出来的东西。
如果它前面已经偏了一次,后面就会在错误的基础上继续"续写"。这就是为什么你经常看到这样的回答:开头还挺正常,中间有点可疑,后面越说越离谱。
它不是突然疯了。它只是在认真地、一步一步地,把之前的偏差滚成了雪球。
这里给你一个直觉:这跟人在紧张时语无伦次很像。你说了句不太对的话,然后为了让这句话听起来合理,你又加了一句。第二句为了支撑第一句,可能更离谱。说着说着,你自己都不知道在说什么了。模型基本就是以这种逻辑在跑的——只是它不紧张。
你要的是一台数据库,它却是台创作引擎
你问它"这个型号的服务器支持多大内存"。
你要的是一个确定的、可查证的参数。
它可以给你答案。但它的原始能力不是记忆参数表——它的原始能力是,看到一个问题的开头,生成一个看起来像答案的结尾。
这两件事有时候恰巧一致,有时候不。不一致的时候,你就看到了幻觉。
这就是能力错位:你在把它当信息检索系统用,但它本来是个概率生成器。
错位本身不是问题——问题是你没意识到这个错位。如果你意识到了,你就会在"需要精确信息"的场景里给它配上外部知识源。如果你没意识到,你就会反复问它"这个参数是多少"然后反复被坑。
它被训练得太想让你高兴了
经过 RLHF 和指令微调的对话模型,被优化成了"好合作"的样子。
更愿意回答,更少拒绝,更像懂了你的意思。
这当然让体验更好了。但这也让模型在某些时候——尤其是你稍微暗示了某个方向的时候——更倾向于顺着你说,而不是坚持事实。
明明应该回答"我不确定",但它觉得这样说会让你失望。于是它硬着头皮给了一套完整的答案。
这是一种奇怪的错位。你都说不清它到底是"听话了"还是"没听话"。
它太在乎句子的局部顺滑了
模型生成文本的时候,注意力天然倾向于局部的上下文。
上一句话提到了一个概念,下一句话就很自然地顺着那个概念接。这段文本的风格像学术论文,下面就一直模仿论文的腔调。
问题是,"局部最顺"和"整体最对"经常不是一回事。
你它给你的答案流畅、自信、工整。你读起来毫无障碍。但也正因为它毫无障碍,你不会停下来检查。
那些真正对的内容和那些编造出来的内容,在阅读的时候给你的体验是几乎一样的。这才是最危险的地方。因为我们通常靠"这东西读起来怪不怪"来判断真假。但模型恰恰在"显得不怪"这件事上做到了极致。
这就是为什么很多幻觉内容读起来感觉特别好——结构工整,术语得当,语气自信。因为它确实在用最好的方式完成一个"续写任务"。
只是它不是事实。
我还有一个小类比想给你。
你玩过那种传话游戏吗?第一个人对着第二个人的耳朵说一句话,第二个人传下去,传到第十个人时可能跟原话已经八竿子打不着了。大模型的生成过程有点像这个——但它不是在十个人之间传,而是在同一个"大脑"里逐步接着自己的话往下写。每一步都只对上一步负责。没有回过头来跟原始事实对账的步骤。所以偏得越早,错得越远。
知道这个之后,你对缓解方案的理解会上一个台阶。因为你能看出来,很多缓解策略其实就是在这个传话链条的不同环节插入"回头对账"的操作——检索就是一种对账,校验也是,引用也是。
那能怎么办呢
先说一个你需要接受的现实:
幻觉是不可能被彻底消灭的。因为它是模型核心工作机制的副作用,不是一个可以被 fix 的 bug。
但可以被显著压低。可以被工程化地控制在一个可接受的范围内。
我知道"不能被彻底消灭"这句话听起来很让人泄气。但换个角度想:这意味着你不用花时间去找那个"完美的解决方案"了。把期待调到一个务实的水平,然后专注于你能控制的部分,效率反而更高。
这个"控制"的思路,大致有三种路子。
路子一:直接改模型脑子里的东西
知识编辑。说白了就是在不完全重训模型的情况下,定向改掉某些特定的知识。
比如某个关于产品的描述过时了,你不想每次都靠外部检索去覆盖,就把它修掉。
这听起来像是打补丁——像一个运维工程师会喜欢的思路。精准、一次性的修复。但它的难点在于神经网络不是数据库。你不能说"把第 327 行改成这个值"。知识是分布式的,分散在大量参数里。编辑一处可能意外地改变了其他相关知识的表达。这就是为什么知识编辑的研究做了这么多年,工程落地依然不算主流。
听起来很美好,但实际门槛很高。改一处可能影响周围的知识分布,成本不低,而且只适合稳定的、低频变化的事实。对于每天都在变的信息——比如最新的 API 文档——这种玩法不太行。
所以日常工程里,真正好用的不是这个,是下面这个。
你可以把知识编辑理解成"给模型做手术"。手术能做,但你要知道什么时候该做——是那种必须动刀的核心知识错误,比如系统的安全策略、合规要求等等。这些改了以后短时间内不会再变,值得一劳永逸。而对于每天都在变的外部信息,别做手术了,给它配个活页笔记本。
路子二:别让它硬猜,先给它资料
检索增强。思路简单到有点可笑:既然你不知道,就别猜。先去查。
就像你考试的时候,如果是闭卷,你不记得的只能蒙。开卷你就翻书。
RAG 就是让模型"开卷考试"。
但这里有一个容易被忽略的点——检索这件事本身有多种做法。
一次性检索是最常见的。用户提问,系统搜一轮,把结果塞给模型,模型回答。快,简单,成本低。但容易漏。
这里有一个很容易被忽略的细节:一次性检索的质量在很大程度上取决于你检索时用的 query 是不是直接从用户原话来的。很多时候用户说的话跟知识库里的表述方式差距很大。直接拿原话去搜,很可能什么都搜不出来。这就是为什么后面要讲 query rewriting。
迭代检索是"边想边查"。先检一轮,发现信息不够,基于缺口再去查第二轮、第三轮。适合那种需要多步推理的复杂问题。但流程重,延迟高。
适合的场景是那种"不查一次不知道要查什么"的问题。比如你问"我们公司的A产品和B产品在合规上有什么差异"——你不可能一次性检索就把两个产品的合规文档一起召回。往往是先召回A,然后发现需要跟B对比,再去召回B。这是正常的信息获取节奏。模型也应该这么做。
事后检索是反过来的——让模型先答,然后针对答案里的事实点去验证。发现对不上的地方,再让模型改。适合高风险场景,比如报告生成、医学术语校对。
事后检索的好处是你不用在生成前就猜测哪些信息有用——你让模型先自由发挥,然后拿外部证据来"审讯"它。这种模式在某些写作任务里特别自然:你先打个草稿,然后拿着草稿去查一下你不太确定的点,改好再交。
我称这种模式为"先写后审"。它在一个维度上特别聪明:你不再需要完美地预测"答案需要哪些证据",因为你可以先拿到答案,再从答案反推需要验证什么。这在生成长文或复杂推理时是一种非常务实的策略。
三种不是互斥的。好的系统经常会组合用。
路子三:把回答过程关进一个工作流
这就要说到 LangGraph 了。
LangChain 帮很多人搞定了"串几个环节"的问题——加载文档、切块、embedding、检索、送入模型——一条链下来很清晰。但一旦流程变复杂,单纯串行的思路就不够了。
比如一个真正严肃的抗幻觉流程可能是这样的:
- 先看你问的是什么——是不是需要检索的那种问题
- 检索一轮
- 检查召回来的东西够不够——质量够不够,信息全不全
- 不够的话,换个角度再检一轮
- 生成初稿
- 检验初稿里的事实点是否都有资料支撑
- 没通过校验的地方,退回重写
- 最终输出
这不是一条线。这是一个带判断、带分支、带回路的东西。是图。
LangGraph 的价值就是让你能把 LLM 应用从"顺序调用"升级成"可控工作流"。对于碾压幻觉来说,它特别适合做迭代检索、事中验证、失败回退、人工审核插入这些事。
如果说 RAG 是给模型补资料,那 LangGraph 就是给模型修跑道。跑道修好了,它想跑偏都难。
RAG 到底是什么
很多人第一次听说 RAG 的时候会觉得这概念很玄。其实不玄。
在讲具体的技术之前,我想先讲一个我自己经历的变化。
我最初搭 RAG 的时候,心态是这样的:把所有文档扔进去,配好流程,然后测试。发现幻觉还是很多,就觉得"这东西没什么用"。
后来我换了一种思路:先理解模型在"没有外部知识"的情况下会错成什么样,然后一点点加 RAG 的能力进去,看每一步把幻觉压下来多少。
这个 mindset 的转变非常关键。RAG 不是一颗药——吃一次就痊愈了。它是一层一层叠加上去的工程手段。你要先知道自己面对的是哪类幻觉,然后有针对性地选层。
现在回到技术本身。RAG 就是三件事:查、拿、写。
查——你有一个问题。拿——你先去知识库里找到相关的资料。写——把资料一起交给模型,让它基于资料写答案。
就这么简单。
我见过很多第一次接触 RAG 的开发者,他们听说这个缩写之后第一反应是去找论文、看原理、读源码。其实不用。RAG 的思想跟人类做研究没区别:你不知道答案,你不会直接给结论,你会先去翻资料。RAG 就是让模型做一样的事。区别在于模型能在一秒钟内翻完几万页。
但这里有一个讽刺的地方:人类做研究的时候知道什么时候不要再翻了——差不多够了就行。模型不会。你不告诉它"够了",它会一直等资料。你塞多了,它又会消化不良。所以 RAG 远不是"塞资料"这么简单,而是"塞合适的资料、塞刚刚好的量、用正确的方式塞"。
为什么它这么重要?因为它解决了几个工程上非常现实的问题。
第一,便宜。
你想让模型知道你的内部产品文档。你可以选择重新训练它,但那个成本——算力、时间、数据准备——不是一般团队承受得起的。你也可以选择微调,但微调对增量信息的处理很笨拙。
RAG 不用动模型。你建好知识库,建好索引,检索一下就行。知识更新也方便——删了旧文档,加新文档,完事。
但我说"便宜"是相对的。RAG 便宜的前提是你已经在用大模型了。如果你从零开始算——买 embedding API 的费用、搭建向量数据库的服务器成本、维护知识库的人力——这些加起来也不小。所以别以为 RAG 是免费的午餐。它是比重新训练便宜得多的替代方案,但它不是零成本。
第二,即时。
模型参数里的知识有截止日期。今天下午更新了产品定价,模型要到下次训练才知道——那不知道是什么时候的事了。
但知识库是可以实时更新的。你上午改了文档,下午 RAG 就能用上。
这种即时性在 toB 场景里特别重要。企业内部的知识——SOP、培训材料、合规要求——更新频率高,时效性要求强。如果系统依赖模型自己的参数来记住这些,每次更新都得等一个训练周期,那就不是"准不准"的问题了,是"过期了就等于错"的问题。
第三,可追溯。
这个很重要。
一个人跟你说"据我所知",你心里是没底的。
但如果他说"根据 2025 年第三季度财务报告第 12 页",你的信任感立刻就变了。
RAG 能给你后一种体验。因为它检索的过程是有记录的——哪些文档被召回、哪些片段被引用——你可以展示出来。用户能看到你的答案不是凭空来的。
这里面有个工程细节值得一提:很多时候,可追溯性不是你做了就能有的,而是你要设计成有的。比如你召回了五个 chunk,但模型生成的答案只引用了其中两个——那剩下三个呢?是不是也影响了答案但没被引用?这种模糊性本身就是忠实性幻觉的来源。好的系统会要求模型对每一句有事实含义的话给出引用标记,没有引用标记的事实声明就不允许出现在最终输出里。
第四,可控制。
纯靠模型自己记的东西,你几乎控制不了。你不知道它在回答某个问题时到底用了哪些"记忆"。
但 RAG 让你拿回了一部分控制权。你决定用哪些文档,决定怎么切块,决定检索几条,决定怎么排序,决定怎么把证据喂进上下文。这些不是模型的黑箱操作——是你作为开发者的设计决策。
但这里有一个很容易掉进去的自大陷阱:手里有了这些控制权,不代表你就能用好它们。一个典型的例子是检索条数。很多人直觉认为"召回越多越好"——结果把上下文窗口塞得满满的,最关键的那条信息反而被稀释了。控制权给你了,但你得在做减法和做加法之间做非常精细的权衡。这是一个不断调试的过程,不是一个一次设好就完了的配置。
但 RAG 不是万能药
我必须把这些冷水也泼了。不是因为我喜欢泼冷水,是因为这些冷水能帮你少走很多弯路。
RAG 很重要,但它不解决所有问题。
一个很常见的错误 timeline 是:
- 第一周:发现模型有幻觉
- 第二周:上 RAG
- 第三周:幻觉还在,开始怀疑 RAG 没用
- 第四周:到处问"有没有更好的技术"
但实际上,RAG 解决了"知识缺失"这一类幻觉——这件事它是真的做到了。剩下的是"它忠实使用检索结果了吗"、"检索本身是对的吗"、"流程有验证吗"这些问题。这些问题需要其他手段来应对。
所以别把 RAG 当成终点站。把它当成中途必过的一站。上了车,继续开。
第一个坑:检索本身就是会出错的。 你问的是A,系统给你召回的是B。如果召回来的证据本来就是错的,模型会在错误的基础上更认真地犯错。这时候不是模型在胡说,是整个链路从根上就歪了。
第二个坑:检索到的东西,模型不一定老实用。 这就是忠实性幻觉。证据来了,摆在它面前了,但它可能因为习惯、因为 prompt 写法、因为上下文太长,没有真的按证据回答。它可能对证据做了二次加工,加了点自己的东西,或者忽略了关键部分。
这个问题比第一个坑更让人抓狂。因为从系统的指标上看,检索是对的——召回的文档内容正确,排名也合理。但模型就是没按证据说。问题出在最后一个环节,但你的调试往往会回溯到前面所有环节去排查。所以我建议:先盯着输出和证据的一致性去优化,等这个稳了,再看检索是不是有问题。从后往前修,更容易定位。
第三个坑:上下文塞太多,证据被淹没了。 你为了保险,召回了几十条文本,全部塞进 prompt。然后最关键的那条——那条真正能回答问题的片段——被埋在了 token 的汪洋大海里。模型可能根本没注意到它。
这跟"丢失在中间"的注意力问题有关。很多研究发现,模型对于被塞在上下文中间位置的信息,注意力会显著下降。开头和结尾的信息更容易被模型利用,中间的信息容易被忽略。所以如果你把最关键的那个 chunk 塞到了上下文中间,而前面后面都是相对次要的内容,模型可能根本就没"读"到它。这里你要学会的不是怎么多召回,而是怎么少而准。
第四个坑:有些任务不是靠检索能解决的。 复杂的多步规划、需要反复调整的决策、涉及多个工具调用的流程——这些不是"查资料然后回答"这么简单。单靠 RAG 搞不定。
这里面有一个微妙的心理陷阱。很多开发者第一次搭好 RAG 的时候,看到它能正确回答一些需要私有知识的问题,就觉得"这个问题解决了"。但实际上,RAG 解决的是"知识缺失"导致的那一类幻觉。对于那些因为模型自己发挥、prompt 不对、流程缺失导致的幻觉,RAG 根本摸不着。所以上完 RAG 之后的第一件事,应该是重新评估你的幻觉来自哪里——而不是直接宣布胜利。
这也是为什么 RAG 经常要配 LangGraph 这样的工作流框架。RAG 负责"给资料",图结构负责"管流程"。
单独用 RAG,就像给一个容易偏题的学生打开了一本百科全书。书是好的,但他可能翻错页、读漏、读完自己加戏。LangGraph 就是你的助教——翻开书之前先问"你要查什么",翻完了问"查到了吗",答完了问"有出处吗"。
这种配合模式其实不新鲜。任何复杂系统最终都会走到"感知-检索-验核-行动"这个循环。RAG 解决了"感知到问题后去哪里找答案"的部分。LangGraph 解决的是"找到答案后如何确保答案被正确处理"的部分。缺哪一个都不稳。
向量数据库是什么,为什么 RAG 离不开它
现在该聊一个比较底层的东西了。
你肯定听过"向量数据库"这个词。很多人第一反应是——又他妈的来了,换了个新概念装高深。
其实它要解决的问题非常实在。
先讲一个猫的类比
假设你有很多猫。你想知道哪些猫比较像。
你可以从很多方面去描述一只猫:体型多大、毛多长、腿长不长、叫声尖不尖、耳朵立不立。
每只猫都可以变成一组数字。比如:
第一只猫:(0.3, 0.8, 0.2, 0.6, 0.9) 第二只猫:(0.4, 0.7, 0.3, 0.5, 0.8)
你看这两组数,整体都很接近——说明这两只猫挺像的。
再换一只:(0.9, 0.1, 0.8, 0.1, 0.2)——这只跟前两只就明显不像。
这就是向量的直觉。
文本也是一样的道理。一段话可以被映射成一组数字——几百维、几千维——语义越接近,这些数字在计算上越接近。
这就是 embedding 做的事:把非结构化内容变成可以被计算的向量。
传统数据库和向量数据库的区别
传统数据库擅长什么?
你问它 id=1001 的记录,它给你。你问 status='paid' 的有哪些,它给你。你问 name 里包含 'phone' 的,它也能给你。
这些全都是"精确匹配"或者"部分匹配"——基于字段值的匹配。
但你现在想问的是:"有哪些文档在讲模型幻觉这件事?"
问题里可能根本没出现"幻觉"两个字。你说的是"模型乱说"、"模型自己编答案"、"输出不靠谱"。
传统数据库不会自动理解这些说的是同一件事。
向量数据库会。因为它不是在比字面——它在比向量。你的问题和文档被映射到同一个空间里,距离近的就是语义相关的。
这不是魔术。只是换了一种比较方式。
举个例子,假设你的知识库里有一篇文档叫"模型输出不可信问题研究",另一篇叫"How to reduce LLM fabrication"。用户用中文问"怎么让大模型别乱说"。字面上,这三个表述几乎没有共同的词。但语义上,它们在说同一件事。向量检索能把它们对在一起,关键词匹配不行。
这就是为什么 RAG 离不开向量数据库。它解决了语义距离的计算问题——而语义距离的计算,就是你"开卷考试"时翻书翻到哪一页的核心逻辑。
怎么比才算"近"
有两种最常见的比较方式。
余弦相似度:它关心的是方向。两个向量指向差不多的地方,就算它们相似。这在文本场景里特别好用,因为你更关注"表达方向"而不是"长度"。一句话说得长点短点,只要说的是同一件事,在余弦空间里就很接近。 欧氏距离:它关心的是绝对距离。就是两个点在空间里隔了多远。
大部分文本语义检索系统用的都是余弦相似度或其变体。
但请注意一个前提:这些相似度度量的有效性建立在你的 embedding 模型的质量之上。如果你的 embedding 模型不能很好地把语义映射为几何关系,那选什么距离度量都没用。所以第一优先永远是选对 embedding 模型,其次才是选度量。
为什么不能用普通数据库硬算
理论上,你可以把所有文档的向量算出来,存进一个表,查询的时候一个一个比。
但当你有几十万、几百万条文档的时候,一个个比会慢到没法用。所以你需要近邻搜索算法和专门的索引结构来加速。
向量数据库真正厉害的地方不是"能存向量",而是"能在大规模集合里高效找到近邻"。这才是它存在的理由。
你如果只管理几百篇文档,用 PostgreSQL 的 pgvector 插件就足够了,甚至用 NumPy 暴力算一下都行。但到了十万、百万级,不靠专门的近邻索引,延迟会直接从毫秒级跳到秒级甚至更差。所以选不选向量数据库,本质上不是个"功能"问题,而是个"规模"问题。
Embedding 模型是干什么的
Embedding 模型就是那个负责"把东西变成向量"的东西。
你可以喂给它一段文本、一张图片、一段音频,它吐出一组数字。
在 RAG 里,最常见的用法是文本 embedding:把一段文档变成向量,存进向量数据库。用户提问的时候也把 query 变成向量,然后去数据库里找最像的。
这个过程的精妙之处在于,它捕捉的是语义,不是字面。
你问"怎么降低模型的幻觉率",它可能给你召回一篇标题里根本没有"幻觉"两个字的文档,但那篇文档就是在讨论"如何让模型输出更可靠"。语义上接近,就召回来了。
这种泛化能力是关键词搜索做不到的。
早期的 Embedding 方法值得了解
在你陷入"embedding 就是调用一个 API"的舒适感之前,了解一点历史会帮助很多。
早期的词嵌入方法——word2vec、GloVe、fastText——虽然不能直接用于现代 RAG,但它们揭示了一件非常重要的事:语义关系是可以被编码成向量运算的。
word2vec 最著名的例子是:king 的向量减去 man 的向量,再加上 woman 的向量,等于 queen 的向量。这不是巧合,这是模型从大规模语料里学到的语义结构。
这个发现的意义不在于它本身多有用,而在于它证明了一件事:意义可以被计算。词语之间的关系可以用空间中的相对位置来表达。
现代 RAG 用的 embedding 模型——不管是 OpenAI 的 text-embedding-3 还是开源的 BGE、M3E——底层继承的都是这个思想,只是从词级别扩展到了句子、段落和整个文档的级别。粒度变大了,能力变强了,但核心不变:把意义变成一组可以被比较的数字。
所以当你在"调 embedding 模型"的时候,你其实是在做一件很具体的事:你选了一个能把文本编码成高维语义向量的工具。这个工具的好坏,不是看它回答问题的能力,而是看它把"语义上相似的内容"映射到"空间上接近的点"这个任务做得有多好。
选错了 embedding 模型,后面的检索、重排、生成都可能跑偏。
RAG 到底怎么搭,才能真的好用
很多人以为 RAG 搭起来就是:文档入库 → embedding → 检索 → 给模型。跑通就算完。
但真正好用的 RAG,远不止这些。每一步都可能决定最终幻觉率的高低。
切块策略比你想的重要
你是按固定 500 个 token 一刀切,还是按段落切?按标题层级切?是不是留 overlap?
切太大,召回来的东西噪声高,浪费 token。切太小,关键信息被拦腰折断,语义不完整。
真正要下功夫的是:根据内容类型来切。FAQ 是一种切法,合同是另一种,代码是一种,报告又是一种。一刀切是最偷懒的方法,也最不好用。
切块策略优化带来的提升,经常比你换一个更贵的模型还明显。
一个很多人都走过的弯路是:一开始随便切,发现效果不好,然后去换模型。从 GPT-3.5 换到 4,好了一点;再换到 Claude,又好了一点。每换一次都花钱、花时间。但最后回头一优化切块策略,可能比换了三次模型的效果提升都大。我自己就走过这个弯路。所以现在在调 RAG 的时候,第一个检查的不是模型,不是 prompt,是 chunk 长什么样。
用户的问题通常不适合直接检索
用户问你"这个接口怎么又报错了",你就拿这句话直接去检索。
能召回什么呢?可能什么都召回不了。因为这句话太口语了,没什么关键词信息量。
所以检索之前你可能需要先做 query rewriting——把"这个接口怎么又报错了"改写成一个更适合检索的形式,比如带上接口名、错误码、出现场景。
这一步做好了,检索质量能提一大截。
纯向量检索不一定总是最好的
有些场景,关键词匹配比语义搜索更准。
比如你问"API v3 的 base url 是什么",这根本不需要语义理解——需要的就是精确匹配到写了 v3 base url 的那一段文档。
所以很多好的系统会用混合检索:BM25 做关键词召回,embedding 做语义召回,两边的结果合并后再重排。
SPARSE + DENSE,比单纯用哪一种都稳。
混合检索还有一个好处:它能覆盖两种完全不同类型的查询失败。向量检索在语义近义表达上很强,但遇到缩写、专有名词、数字等"硬匹配"场景容易漏。BM25 正好相反——它不懂语义,但它不会错过精确的关键词。两者互补,一套逻辑覆盖两个坑。
重排这一步非常关键
第一轮召回出来的 document 的排序质量通常不太行。模型算相似度很快,但精度有限。
所以很多系统会在召回后加一个 rerank 阶段——用更贵但更准的模型把召回结果重新排序。
这一步的价值经常被低估。很多时候不是你完全没召回到相关内容,而是最相关的那条没排在前三。
生成质量不是输在"召没召回来",是输在"最对的没排到前面"。
要求它引用证据
为了防止忠实性幻觉,你可以在生成时明确要求:
- 只基于检索结果回答
- 如果证据不够,就说不知道
- 每条关键结论都要引用来源
- 区分"原文说的"和"你推断的"
这些约束不能 100% 消灭幻觉,但能大幅度挤压它自己发挥的空间。
有一个很实际的 trick:不要只在 prompt 里写一句"请引用来源"。这太模糊了。你要明确地要求它用特定的引用格式,比如"[1] [2]"这样的标记,并且告诉它如果没有引用标记就不允许放进答案。格式越具体,模型越不容易糊弄你。越开放的要求,它越能找到"看起来在引用但其实在脑补"的方式。
事后验证
对高价值的回答——比如要发给客户的报告——你可以在答案生成之后再加一层校验。
把答案里的关键事实抽取出来,和原始证据逐条比对。看看哪些是有支撑的,哪些是多余的,哪些是矛盾的。
没通过的,就送回给模型重新处理。
这一层校验可以有多种做法。最简单的就是把原始证据和生成答案一起扔给另一个模型,问它"这个答案里的每句话都有证据支撑吗"。更高级的做法是用 NLI(自然语言推理)模型对每个事实三元组做真假判断。用哪种取决于你对精度的要求和你愿意为此花多少时间和算力。
按任务分流,不要一条链路走到底
FAQ 查询、合同问答、代码检索、报告生成——它们需要的检索策略、答案格式、严谨程度完全不同。
试图用一套 pipeline 搞定所有,通常的结果是哪个都做不好。
更务实的做法是按任务拆成不同的链路,每条链路专门优化。这就是 LangGraph 里分图的意义所在。
别害怕"拆"。一开始你可能觉得只维护一条链路最简单。但用不了多久你就会发现,每个新需求加进来的时候,那一条共享链路会越变越肿、越调越烂。早点拆,早点轻松。
LangGraph 和 Document 组件:把知识组织成工程对象
如果你真的开始搭 RAG 系统,你会发现一个很具体的问题:
文档来自 PDF、网页、Notion、数据库、Markdown……格式五花八门。每种的加载方式、清理方式、元数据提取方式都不同。
LangChain 的 Document 组件就是把所有这些乱七八糟的来源抽象成一个统一的东西:一份正文、一份 metadata。
正文就是 pageContent——实际的内容。metadata 是附加信息——来源、页码、章节、时间戳、权限。
这个抽象的价值在于,不管你从哪来的内容,经过 Document 组件处理后,后续的切块、embedding、检索、引用都能用一套统一逻辑处理。来源链路也不会断——你检索到某个 chunk,能知道它是从哪个文档的第几页来的。
这种可追溯性是很多高质量 RAG 系统的底牌。用户觉得"这系统挺可信"的感知,往往不是因为它回答得更聪明,而是因为它能告诉你"我为什么会这么说"。
但我必须提醒你一件事:LangChain 的 Document 抽象虽然好,但它并没有帮你解决"怎么处理不同文档格式"这个脏活。PDF 表格的解析、扫描件 OCR、Markdown 里的嵌套列表结构——这些还是要你自己处理的。Document 只是给你一个统一的输出格式。输入端的活,该干还得干。
而 LangGraph 把这个逻辑推到了更复杂的地方。
你会发现,当你真的想把幻觉率压到很低的时候,你需要的已经不是一个 prompt 了。你需要一个流程:判断 → 检索 → 验证 → 回退 → 修改 → 输出。这流程里有很多条件分支、循环回路、外部工具调用。
LangGraph 不能让你写代码更短。但它能让你把复杂的 LLM 应用逻辑表达成可控的图结构——每一步都看得见,每条边都知道往哪走,该回退就回退,该人工介入就人工介入。
关于 LangGraph 还有一个常见的误解。有人觉得它是"重框架"、"太重了不值得学"。但如果你真的在生产环境里跑过一个带多步验证的 RAG 系统,你就会发现:用 if-else 硬写一个复杂流程的维护成本,远远高于在图上清晰地声明状态和转换。不是框架重,是你还没遇到那个复杂度。遇到了就知道为什么需要它。
而且你可能已经注意到一个趋势:LLM 应用正在从"单次调用"快速转向"多步工作流"。Anthropic 的 MCP、OpenAI 的 Assistants API、各种 agent 框架——底层逻辑都是一样的:单靠一次生成不够,需要编排、校验、回溯。LangGraph 是这个趋势里的一个工程化答案。
最后,给你一套自己掂量的框架
我不想像教科书那样给你做个"总结"——把前面讲的再复述一遍没意义。
我更想给你一套问题。下次你面对一个模型的幻觉问题时,可以拿这些问题问自己:
第一问:它在胡说什么?
是编了一个不存在的事实(事实性幻觉),还是没按你给的约束来(忠实性幻觉)?
这个区分决定了你下一步往哪个方向使劲。方向错了,怎么优化都像是在给破墙上贴壁纸。
你可以做一个简单的分类:下次遇到一个模型输出错误,问自己:如果我给另一个人看这个答案和原始证据,他能看出错在哪吗?如果能,那大概率是忠实性幻觉——证据本身是对的,但结论对不上。如果他看不出来,说明问题出在证据本身或者更上游,是事实性幻觉或者检索偏差。这个判断不用工具,但能帮你在几秒钟内决定到底去改 prompt 还是去修检索管道。
第二问:这个信息,它能从自己脑子里掏出来吗?
如果你的问题涉及实时数据、私有知识、最新的外部信息,那答案几乎就是"不能"。
那你就别让它硬猜。上检索,上 RAG,上外部数据源。把"参数记忆"当成最不可靠的最后手段来用。
但这里有一个反直觉的判断条件:有些问题虽然看似"需要最新信息",但其实不需要外部检索——比如"Python 的 list 和 tuple 有什么区别"这种稳定的事实。上 RAG 反而可能引入噪声版本。所以不是默认所有问题都去检索,而是要判断:这个问题属于"稳定事实"还是"动态信息"。前者靠模型自己,后者靠 RAG。这个判断本身就是工作流里的第一步。
第三问:你的知识库真的能用吗?
有知识库和"知识库能用"是两码事。
你的文档干净吗?切块合理吗?metadata 完整吗?embedding 选对了吗?检索方式匹配你的场景吗?重排做了吗?
RAG 成败的核心往往不在模型,在这些知识工程的基础建设上。
我见过一些团队把几万份 PDF 直接扔进向量库,不做清洗,不分结构,一股脑 chunk 掉就上线了。结果当然不好。但他们的第一反应往往是"这个模型不行",而不是"我的知识库太乱"。这是一个非常典型的思维盲区。模型再强,它也只能基于你给它的东西来回答。你把一个乱糟糟的地下室扔给它,别指望它整理出一个图书馆来。
这个类比我想多讲一句。你家里的书架如果完全不分类,全是乱塞的,你找一本书要翻半天。向量数据库有点像给你的书加了一个"语义GPS"——你描述你想找的内容,它帮你定位在哪层楼的哪个拐角。但前提是你的书本身是"可索引"的——至少每本书有个封面能告诉你它大概在讲什么。如果你把整本书撕成碎片再塞进去,那GPS再强也帮不了你。这就是文档清洗和合理切块的意义。
第四问:你给了模型多大的自由空间?
你有没有明确告诉它"没有依据就说不知道"?有没有要求它引用来源?有没有在 prompt 里划清"这是原文"和"这是你的推断"的边界?
你以为它知道你的规则,但如果你没写清楚,它只会在统计意义上猜测你大概想要什么。
写 prompt 的时候有一个心态很有用:把模型当成一个刚接手这个任务的新同事。别假设它"懂你的意图"。你要像给新同事写操作手册一样,把边界条件、不做什么、出错了怎么办都写清楚。写得越啰嗦,它执行得越稳定。
第五问:你的流程够不够紧?
是一问一答就完了,还是有验证、有回退、有人工兜底?
如果这个回答很重要,就不要把全部希望寄托在一次生成上。加校验。加回路。让流程把模型框住。
这里有一个实用的判断标准:如果你的答案出错,代价是什么?如果代价只是"用户多问一次",那简单的一问一答也许够了。但如果代价是"客户可能基于错误信息做决策"或者"报告会被发给老板",那必须上加校验、加回路、甚至加人工审核。流程的复杂度应该跟出错的代价成正比,而不是跟你"勤不勤快"成正比。
最后一问:你敢不敢让你的系统说"不知道"?
这不是技术问题,是产品问题。但它是很多幻觉问题的终局。
一个永远不能沉默的系统,为了达成"必答"这个目标,会用幻觉来填充空白。如果你在产品层面允许它说"我不确定"、"还缺一些信息"、"请核对事实",那幻觉的空间会缩小很多。
一个可靠的系统,必须配有"可靠地承认自己不确定"这个能力。否则它每次遇到自己不知道的东西,就是在赌一把你来不来检查。
赌的次数多了,你总会输的。
这其实也是整个 AI 工程领域正在经历的一个观念转变。前两年大家的关注点都在"让模型说得更好"上——更流畅、更全面、更自信。但今年越来越多的人开始意识到,真正关键的能力其实是"让模型知道什么不该说"。知道沉默是一种能力,不是缺陷。知道不确定性应该被表达,而不是被掩盖。这比任何新技术都更接近问题的本质。
这不是一个技术趋势,这是整个行业在一次次被置信的回答坑了之后学到的教训。自信的成本太低了——对模型而言,自信和一串 token 一样不需要代价。但对收到答案的人来说,纠正一个自信的错误比质疑一句坦诚的"我不太确定"代价大多了。所以设计系统的原则应该是:降低生产不确定内容的风险,同时降低表达不确定性的社会压力。这两件事一样重要。
就像你永远不会信任一个什么都敢打包票的人。最简单的信任标准,往往是看一个人敢不敢对你说:"这个问题我不确定,我先查一下再回你。"你的 AI 系统也应该这样。而做到这一点需要的不是更好的模型,是你作为设计者有没有把这个能力放进系统流程里。
全景关系图:一图看全幻觉的来龙去脉
这篇文章涉及的概念不少——幻觉类型、根因、RAG、向量数据库、Embedding、LangChain Document、LangGraph。每个都挺重要,但单独看容易丢。下面这张图把这些东西串在一起,让你"一图看全"谁管什么、谁依赖谁。
怎么读这张图:
- 上半区是你要诊断的东西——你的幻觉属于事实性还是忠实性?顺着箭头找到根因。
- 中间区是你能落地的工程手段——事实性幻觉靠 RAG 补知识,忠实性幻觉靠 Prompt 约束 + 校验来管。
- 下半区是让这些手段协同工作的东西——LangGraph 编排整个"检索→生成→校验→回退"的流程,不让任何一个环节脱节。
- 虚线箭头代表"依赖但不同层"的关系。比如 Embedding 不直接解决幻觉,但离开它 RAG 就没法做语义检索。
如果你只记得一件事,记住这个:RAG 解决"知不知道",Prompt + 校验解决"老不老实",LangGraph 解决"流不流畅"。三个一起上,幻觉才能被压到可控范围。
FAQ:你可能正在困惑的几个问题
Q: RAG 和微调到底怎么选?
这个问题几乎每个做 LLM 应用的人都会纠结。答案其实不复杂:
- RAG 是给模型配一本"活页笔记本"——知识放外面,随时更新,不用动模型。适合高频变化的私有知识,比如产品文档、SOP、最新的业务数据。
- 微调 是给模型做"肌肉记忆训练"——改变模型本身的参数行为。适合稳定的风格/格式/领域术语,比如让模型学会用你们公司的报告模板说话,或者熟悉你们行业的黑话。
一个很实际的判断标准:如果这个知识下周就可能变,别微调,上 RAG。如果这个知识半年不变且你希望模型自然地用某种风格表达,微调合适。
但注意,这两者不是互斥的。很多正经的生产系统是微调定风格,RAG 补知识。微调让模型学会"怎么说话",RAG 让它知道"该说什么"。单押一边都是傻逼做法——除非你的场景确实只需要一个。
Q: 向量数据库这么多怎么挑?Milvus、Pinecone、Chroma、Qdrant 各有啥区别?
又他妈的到了选型环节。说实话这几个都能用,区别在场景不在功能:
| 数据库 | 一句话 | 适合 |
|---|---|---|
| Milvus | 分布式性能怪兽,大厂级别 | 百万级以上文档,需要分布式部署,有运维团队 |
| Pinecone | 全托管,开箱即用,贵但省心 | 不想管基础设施,预算充足,快速上线 |
| Chroma | 轻量,本地跑,开发体验好 | 原型验证、小规模项目、本地开发 |
| Qdrant | 性能好且有托管版,中间路线 | 想要性能又想要托管,不想绑在 Pinecone 上 |
| pgvector | 你已经在用 PG 了?加个插件就行 | 几百到几万文档,不想引入新组件 |
选型思路很简单:先看规模,再看团队。几千个文档你用 Milvus 就是杀鸡用牛刀,pgvector 或 Chroma 够够的。上了十万级再考虑 Milvus 或 Qdrant。不想管服务器就 Pinecone。
别陷入"哪个数据库最牛逼"的无底洞——对 90% 的团队来说,瓶颈不在数据库性能,在文档质量和检索策略。
Q: Embedding 模型怎么选?BGE、text2vec、OpenAI 的 embedding 到底哪个好?
这个问题的标准答案是"看场景",但我知道你不想听这个。给你一个可以直接用的判断:
- 纯中文场景:BGE(BAAI 的 bge-large-zh)目前是开源里中文语义理解最强的之一,免费。text2vec 系列也不错,轻量跑得快。
- 多语言混合:OpenAI 的 text-embedding-3 表现稳定,但要钱,而且数据要经过 OpenAI 的服务器。
- 垂直领域(医疗、法律、金融):通用 embedding 模型可能不太行,需要在自己的领域数据上做对比评测。别信榜单——拿你的真实 query 跑一轮比看什么评测都靠谱。
一个逆天的事:很多人花三天选模型,花三分钟写 query。实际上query 前置处理(改写、扩展、消歧)对检索质量的提升往往比换模型更大。所以我的建议是:先拿一个还行的模型(BGE 或 text-embedding-3)跑起来,然后把精力花在 query rewriting 和切块策略上。等这些优化到头了再回头折腾模型。
Q: RAG 搞了但幻觉还在怎么办?
卧槽,这太常见了。别怀疑人生,按这个顺序排查:
- 检索对了吗? 打开你的召回日志,看看模型拿到的那几个 chunk 到底是不是在回答用户的问题。如果召回来的东西就已经跑偏了,后面全是白搭。先修检索。
- 切块有问题吗? 信息被拦腰斩断了吗?chunk 太大噪声太多,太小语义不完整。看看你的 chunk 长什么样。
- 模型老实了吗? 检索结果是对的,但模型没照着用——这就是忠实性幻觉。加强引用约束,要求它"没有出处的说法不许出现",用特定引用格式(比如
[1]),别写一句"请基于文档回答"就完事。 - 上下文塞太多了? 你为了保险召回了几十条,结果最关键的那条被埋在中间。模型注意力在中间位置衰减得厉害。控制召回数量,把最重要的放开头或结尾。
- 知识库质量过关吗? 这是最容易被跳过的——你扔进去的东西本身就乱七八糟。PDF 没清洗、格式混乱、metadata 缺失。垃圾进垃圾出,跟模型没关系。
如果上面五条都检查过了还是不行——什么鬼,那可能是你的场景需要的不是简单的 RAG,而是更复杂的多步推理 + 工具调用。这时候要上 LangGraph 做流程编排了。
排查顺序就是按上面的来,从前往后。别上来就怀疑模型不行,逆天的是,大多数时候是管道的前面环节出了问题。
到你读到这里的这一刻,你可能已经有了一个感觉:解决幻觉这件事,本质上不是在优化一个模型,而是在搭建一套系统。模型只是其中一个环节,而且不是最关键的环节。真正关关键的是你给它配了什么资料(知识库),你怎么让它用这些资料(检索、重排、引用约束),你怎么验证它有没有乱用(事后校验),以及你允不允许它说自己不知道(产品设计)。
这些都指向一个结论:大模型幻觉是系统性偏差,需要系统性地对抗。单点方案(换一个更好的 prompt、换一个更好的模型、上一个 RAG)能解决一部分问题,但如果你的问题涉及多个层面,这些单点方案就会像打地鼠一样——按下一个,又冒一个。真正能让你安心的,是把数据质量、检索管道、生成约束、事后校验、流程编排这几个环节串成一个能互相兜底的闭环。
最后一句很土但很真的话:
别指望一次搞定。别再找银弹了,没有银弹。但如果你每次对幻觉的容忍度降一点、每次发现的问题修一个、每次系统说"我不知道"比上一次多一次,你就走在正确的路上。而且这个方向是可持续的,不会突然崩。工程上真正好的东西,不是看起来酷的那个,是你用着越来越安心的那个。
就去试试吧。从改你的 chunk 策略开始也行,从给你的 prompt 加一条"不确定就说不知道"开始也行。不管起点多小,只要你开始区分"它在胡说什么"和"我怎么不让它说",你已经比绝大多数还在喊"模型不行"的人领先太多了。
当然,如果你看到这里脑子里已经在想"那我是不是还要学向量数据库、学 LangChain、学 LangGraph..."——我会说:是的,但别一口气学。需要什么就学什么。搭个小东西出来跑一跑,感受一下"上了 RAG 以后幻觉真的少了"和"嗯这里还是有问题"之间的差别。这种体感比看十篇文章都值。
动手吧。幻觉不会等你,但你现在知道怎么对付它了。
读者来信
暂无来信,期待你的分享。