☰
RAG实战:从零构建检索增强生成系统,解决大模型幻觉问题
2026/10/13 17:44:56 网站建设 项目流程

1. 一个老问题:模型“一本正经地胡说八道”

做 AI 应用开发的人,几乎都遇到过这种场景:你问模型一个比较具体的问题,它回答得流畅、自信、结构清晰,看起来毫无破绽——但实际上完全是编的。比如你问某公司某产品什么时候发布,它能给你答出详细日期和发布会地点,哪天确认下来发现根本没这事。

这就是大模型圈子里常说的“幻觉”(Hallucination)。

我最初做智能问答项目时也栽在这个问题上。当时直接用一个通用大模型做客服机器人,用户问产品退换货规则,模型能头头是道地讲出一整套流程,甚至具体到“需要填写某某表单”。但公司压根没这个表单。用户真的照做,体验直接崩掉。

后来我把模型换成微调版本,情况好一些,但依然不稳。微调能教会模型“说话的腔调”,但没法把公司所有动态变化的业务数据灌进它的参数里。产品规则、库存信息、售后政策这些内容,本身就是高频变动的,模型根本做不到持续同步。

那段时间我反复在想一个问题:能不能不强行让模型“记住”所有东西,而是让它在回答之前先去查一下资料?

这个思路最终落地成了 RAG,也就是检索增强生成(Retrieval-Augmented Generation)。说直白点,就是让大模型回答前先“查书”,查到的内容作为依据,再组织语言回答。它的核心作用就是把“闭卷考试”变成“开卷考试”,从机制上把幻觉降下来。

这篇文章我会把整个 RAG 项目的设计思路、落地过程、踩过的坑和优化经验完整拆开讲一遍。如果你是做 AI 应用开发、正在被幻觉问题折磨,或者刚接触 RAG 想找个能参考的实战案例,这篇应该对你有帮助。

2. 回答前先翻书:RAG 的整体设计思路

2.1 为什么不是“继续微调”

很多人的第一反应是:幻觉是因为模型学得不够,那我再喂数据微调不就行了?

这个思路在某类场景下有效,比如让模型学习特定的“文风”或“对话格式”。但换到知识密集型场景,问题就来了。举个例子,某公司内部有几千份业务文档,规则还在不断更新:上个月还能退换的商品,这个月起可能就不能退了。如果你把知识微调进模型参数里,每次规则一变,都得准备数据集、重新训练、评估、上线,整个链路跑下来至少几天。而 RAG 改一条文档、重跑一遍索引,几分钟内知识就生效了。

从成本上看,微调一批参数动辄几万块钱的算力开销,换来的是“模型记住了某句话”。RAG 则是把知识外置,模型只需要学会“怎么使用资料”,知识内容本身可以随时替换。

另外还有一个更本质的问题。微调是在“模仿”,模型学到的是一种概率分布,它无法精确区分“这个规则只适用于某个业务线”还是“这个规则适用于所有用户”。但 RAG 不同——它把上下文直接放进提示词里,生成时模型能真实地“看到”资料原文,回答的依据是具体的段落,而不是模糊的记忆联想。

我当时做技术方案对比时,用了这样一个判断标准:

  • 如果目标是“让模型变成某个领域的人设”——选微调
  • 如果目标是“让模型准确说出某个领域的事实”——选 RAG
  • 两者也可以结合:底层用微调控制表达风格,上层用 RAG 注入事实知识

我的项目最终选择“轻微调 + 重 RAG”的组合。微调部分只花了一个周末训练,用来调整回答语气和格式;知识部分全部走 RAG,业务规则更新只需要替换文档库即可。

2.2 RAG 的核心流程拆解

整个 RAG 流程可以分成几个环节:知识库预处理、嵌入化、检索、增强提示、生成回答。

用“查书”来类比就很好理解。你是一个博学但健忘的研究员,别人问你一个问题,你不会凭记忆瞎说,而是先跑去图书馆查资料。查的时候你不会从头翻到尾,而是先查目录、查索引,找到最相关的那几页,翻开来仔细看,再结合自己的语言组织能力把答案讲出来。

对应到系统实现上:

  • 知识库预处理:把文档切分成小块,每一块就是一个“知识点卡片”
  • 嵌入化:把每个卡片转成数学向量,不同卡片按语义远近排列在“向量空间”里
  • 检索:用户提问时,先把问题也转成向量,去找和问题最接近的卡片
  • 增强提示:把找到的卡片原文拼进提示词,告诉模型“资料如下,请基于资料回答”
  • 生成回答:大模型基于给定资料和用户问题生成最终输出

这里面有两个“为什么”值得展开。

为什么要把文档切块?因为大模型的上下文窗口是有限的,不可能把整本手册都塞进提示词。而且检索追求的是“精准找到关键段落”,而不是“检索一整个章节再让模型自己找重点”。切成小块之后,每块字数可控,检索时命中率更高,也不会浪费上下文空间。

为什么要用“向量”做检索?因为关键词检索只能匹配字面相同的词,语义检索靠向量可以识别“换个说法但意思相同”的句子。比如用户问“这手机防水吗”,文档里写的是“支持IP68级防尘防水”,字面上一个词都对不上,但向量距离上很接近,这种能力对问答体验的提升非常明显。

2.3 一条查询的完整旅程

我把自己项目里一次真实查询的完整链路记录一下,方便你建立直观的体感。

用户问:“笔记本电脑保修期内维修需要带什么?”

这个问题的处理过程如下:

第一步,问题嵌入化。系统调用嵌入模型,把这句话转成一个维度为 768 或更高维的向量,向量中的数值对应这句话在语义空间中的坐标。

第二步,向量检索。系统拿这个向量去向量数据库里做相似度计算,找距离最近的候选块。我们项目中设置的是先召回 Top 20,这 20 块来自不同篇文档,涵盖了“保修政策”“维修流程”“客户须知”等多个分类。

第三步,重排序。这一步是 RAG 的进阶操作。Top 20 里很可能有内容重复或者相关性不够高的块,如果全部塞给模型,会稀释注意力。我们部署了一个交叉编码器模型,把每块“被检索文档”和“用户问题”成对输入模型,逐对打分,从 20 块里筛出最相关的 Top 5。

第四步,构造提示词。系统组装出一段结构:

系统提示部分:设定角色和回答规则 资料部分:贴上筛选出的 Top 5 块原文,来源文档名标注在每段前面 用户问题部分:原样放入

第五步,模型生成。模型阅读资料后,组织出一个自然回答:带保修卡和发票,需提前在官网预约网点,检测后确认问题属于保修范畴即可免费维修。

整个过程端到端耗时大约两秒,其中检索加重排序占 300 毫秒左右,生成占 1.5 秒左右。用户感知就是一问一答,但内部已经完成了“查书再作答”的完整闭环。

3. 落地实操:从文档到向量库的全链路搭建

3.1 文档处理与切块策略

RAG 效果好不好,一半以上取决于文档预处理做没做好。我最早犯过一个错误:拿 PDF 直接切,结果大量段落被拦腰截断,检索出来的内容前言不搭后语。

正确的流程是先做文本清洗,再做结构化识别,最后才切块。

文本清洗是指把文档里的多余符号、乱码、页眉页脚、水印内容去掉。这一步看似简单,但实际非常琐碎。PDF 解析出来的英文单词可能被硬拆成两行、表格结构会变成一堆空格加字母。我是先按文档类型做了分流:PDF 用专门的解析库提取,Word 用文档转换库,HTML 走抓取清洗,统一转成 Markdown 或纯文本格式再进入后续流程。

切块策略上,我最开始用的固定字数切块,设定 512 个字符切成一块。跑了一轮发现问题很典型:有的块正好切开一个意思完整的句子,检索时模型只能看到一半;有的块全是列表项,语义不完整。

后来我改成了“结构化感知切块”,具体做法是:先按文档标题切分,再按段落切分,然后按句子边界修正切块位置。每个块尽量是一个完整语义单元。经验参数参考下面这个表:

文档类型推荐块大小重叠长度说明
产品说明 / 操作手册300-500 字50-100 字段落结构清晰,按小节切即可
政策制度 / 法律文本200-300 字50 字条款独立,小块精度高
技术文档 / 代码注释500-600 字100 字上下文依赖强,需保留衔接
问答对 / 聊天记录每条独立0直接按条切

重叠长度的作用:让相邻块之间保留一部分重合文本,避免关键词被切在边界上导致两边都搜不到。我的经验值是固定字符切块用 10%-20% 的重叠率,语义切块用 5%-10%,重叠太多会加剧索引存储和检索噪音。

3.2 嵌入模型选型与向量索引构建

切好块之后,下一步是把每个文本块转成向量。这一步的“模型选型”直接影响后续检索质量。

我的选型标准是三条:中文语义理解能力足够好、向量维度适中不浪费存储、本地可部署不依赖外部服务。

为什么强调本地部署?因为企业知识库涉及内部资料,把文档内容通过第三方嵌入模型服务传输,本身就存在数据安全风险。我最终选用的是开源的中文嵌入模型,部署在一台带 GPU 的服务器上跑离线批处理。实测下来,它对长句、反问句、业务专有名词都能给出符合直觉的语义向量。

嵌入模型本身不大的话,用 CPU 跑批处理也能接受。比如一个 1 亿参数的嵌入模型,用 CPU 处理一万篇短文档大约需要几十分钟,项目初期完全可以接受。如果文档量大且更新频繁,再上 GPU 也不迟。

向量构建完成后,把向量和原文元数据一起存入向量数据库。我当时的选型考虑过专业向量库和传统数据库加向量插件。项目规模不大,最终选了轻量的、支持向量检索的数据库方案:一条记录保存三类信息,向量数组、原始文本块、来源元数据(文档名、页码、更新日期)。

存储结构大致是:

-- 简化后的向量表设计 CREATE TABLE doc_chunks ( id INTEGER PRIMARY KEY, doc_name TEXT, -- 来源文档名 chunk_content TEXT, -- 原始文本块内容 vector_embedding VECTOR(768), -- 嵌入向量 created_at TIMESTAMP );

建完索引之后,我写了一个本地测试脚本,随机拿问题去检索,人工检查返回结果的相关性。这一步能帮你快速发现“嵌入模型不适配你的领域”或“切块太小导致语义不完整”这类早期问题,不用等系统全部搭完才回头返工。

3.3 检索方案:从单路向量到混合检索

只用向量检索,能解决大部分语义匹配问题,但实测中我发现还有一个盲区:精确关键词。

比如用户问“移动平均线是什么”,文档里写的是“MA 移动平均线指标”。语义向量可以把“移动平均线”匹配到“MA”,但如果用户直接问“MA是什么”,向量检索的结果往往不如关键词检索来得精准。反过来,用户用口语描述问题时,关键词检索又匹配不上。

所以我的最终方案是混合检索(Hybrid Search):同一问题,同时跑路向量检索和路关键词检索(BM25 这种词频算法),然后把两路结果的得分归一化后加权融合。

融合逻辑不复杂。每路检索都返回一组命中文档及其相似度分,我做归一化处理:得分减去该批最小值,除以最大值减最小值,把分数压到 0 到 1 之间,然后按权重相加。两个搜索会找到相同的命中项,则特殊处理,匹配命中则加分。然后取总分排序的 Top N 作为最终候选。

这套方案上线后,一个明显的变化是“精确词命中”和“语义理解”两个场景都能兼顾。用户问“换货要发票吗”,混了关键词检索后,包含“发票”字样的文档被加权召回,准确率比纯向量检索高了几个百分点。

4. 最核心的环节:构造提示词与生成控制

4.1 提示词的结构化模板

RAG 的“生成环节”常见误区是:把检索到的一堆文章整段丢给模型,然后说一句“请回答”。效果通常很差,因为模型不知道“哪段资料对应哪个话题”,也不知道“资料里没有的内容该怎么处理”。

我最终的提示词结构分四层。

第一层是系统角色设定,告诉模型它是什么角色:你是某公司智能客服助手,只能根据提供的内部资料回答用户问题,回答自然友好。

第二层是检索资料区,用明确的标识符包裹:以下是内部资料原文,来源于知识库检索结果。资料与用户问题之间以分隔符标识。

第三层是回答规则,逐条列出约束:仅基于资料内容作答,资料未提及的内容明确说明无法确认。回答须提取资料关键信息,不重复资料全部原文。引用资料时标注来源文档名称。不确定时不推测,避免编造。

第四层是用户问题,原样呈现。

实际模板的简化示意如下:

系统: 你是企业智能客服。严格依据检索资料回答问题。 资料: [资料1] 来自文档《售后政策_v3》:保修期内用户需提供服务发票或购买凭证。 [资料2] 来自文档《售后政策_v3》:人为损坏不在免费保修范围内。 规则: 1. 只根据资料内容回答。 2. 资料中找不到答案时,明确告知用户无法确认。 3. 回答末尾标注依据的资料编号。 用户: 保修期内维修要带什么?

这套结构里,最关键的是“回答规则”那部分。你可别觉得是废话——模型非常容易顺着“资料”的语感走,如果你不强调“不推测”,它很可能在资料没提到的地方自己补充一段合理但错误的内容。

4.2 温度参数和最大生成长度的调节

“查完书再回答”并不等于完全禁用模型的创造力。温度参数控制的是输出概率分布的平滑程度:温度调低,模型倾向于选最高概率的词,输出更确定;温度调高,模型更愿意“发散”,输出更丰富但更不可控。

在 RAG 场景下,我的经验是温度设置在 0 到 0.3 之间。0 的结果最稳定,但回答会略显生硬;0.3 时回答更自然,幻觉风险依然可控。如果超过 0.5,就算给了资料,模型也偶尔开始发挥,某次实测中它甚至编了一个文档里不存在的售后网店地址。不要用高温度搭配 RAG,这是很多新人在“回答生动性”和“准确性”之间找平衡时会踩的坑。

最大生成长度也要控制。如果你的知识库回答通常控制在 150 字以内,那 max tokens 设 500 就足够;设太大反而给了模型“发挥空间”,长回答里更容易夹杂编造细节。配合结束词设计,让模型答完即停,不要继续补充。

一个补充技巧是加入“思维过程”的提示。对复杂问题,可以让模型先简述判断逻辑再给结论。比如规则中加一条:回答前先说一句“根据资料,我的理解是”,有助于模型在内部先梳理一遍检索内容,减少跳跃式输出。

4.3 生成前的前置校验

这个阶段可加可减,但在我实战中价值很高。简单来说,就是在生成之前做两道闸门:

第一道:检索置信度检查。向量检索返回的相似度分数过低时,说明知识库里可能根本没有相关内容。此时不硬着头皮生成,而是启动另一套回复模板:“抱歉,知识库中暂时没有找到这方面的信息,请稍后咨询人工客服。”这比让模型瞎编强一万倍。

第二道:重排序过滤。前面提到的 Top 20 重排序筛出的 Top 5,如果第一名得分依然很低,同样触发避让逻辑。

有人担心这样会让系统显得“笨”,事实上用户在知识库确实没有答案的场景里,最想要的就是一句诚实的“不知道”,而不是一个听起来专业但错误的回复。这在客服场景里尤其重要——错误信息误导用户操作的代价,远比“答不上来”大得多。

5. 实战中的优化过程:效果怎么一步步变好的

5.1 评测集的建立

优化 RAG 效果,最怕的是凭感觉调。今天换个切块参数觉得“好像好了一点”,明天换个提示词又觉得“似乎没变差”,但说不清整体效果是提升还是回退。

我搭了一个小规模评测集,约 120 个问题,覆盖几类核心场景:事实查证类、规则咨询类、操作指引类、无答案类(故意问知识库没有的内容)。

评测维度分了三个:准确率(人工标注正确答案后对比模型输出)、忠实度(答案表述是否基于检索资料而不是凭空发挥)、拒答率(无答案类问题中模型给出“不知道”的比例)。

每周固定跑一次评测,把每次改动前后的结果记录到一张表里。这个习惯帮了大忙——有一次我以为重排序模型效果更好,结果评测数据显示部分场景准确率反而下降了,最终定位到是重排序模型对长文本块不敏感,做了针对性调整后才通过了回归。

5.2 向量检索的几个“隐身”短板

这类问题不跑真实数据很难发现。我遇到过几个有代表性的。

第一,专有名词被切碎。比如某公司的产品名“智汇导航终端”,切块时如果按字符边界硬切,可能变成“智汇”和“导航终端”两块。“智汇”单独检索时召回了很多不相关内容。我最终是给专有名词维护了一个关键词词典,在切块前做一次短语合并,把整词看作不可分割单元,问题解决。

第二,同义改写造成的语义漂移。用户说“退钱”,文档里写“退款”,向量模型能识别大部分。但用户说“把押金退给我”和文档里的“款项原路退回”之间,向量距离就没那么近了。单纯靠模型很难覆盖所有改写变体,这个问题的解法之一是:在索引时额外加入同义词扩展块,把“退款-退钱-原路退回-返还金额”这些表达关联起来,让检索系统更容易在两路碰头。

第三,时间敏感信息会把检索带偏。文档里有旧版规则和新版规则,向量检索只认语义不分新旧,常常把已废弃的旧规则排到前面。我给索引加了一层“有效时间”过滤:当前时间不在有效期内的规则,检索时直接排除,不回。这个逻辑逻辑简单,但实用性极高,做企业知识库时强烈建议加上。

5.3 重排序:用大模型精筛候选段落

如果说向量检索是“海选”,重排序就是“终审”。我在项目中期加入了重排序模型,效果提升很明显。

具体流程:向量检索召回 Top 20 段落后,把用户问题分别和每一段拼接成一句话对,交给一个交叉编码器打分。交叉编码器能对“问题和段落是否相关”做出更精细的判断,因为它在计算时能看到问题与段落的完整交互,而不是像双塔模型那样只比对两个独立向量。

我踩过的一个坑:重排序打分直接排序后,只取 Top 1。有时 Top 1 是“高度相关但信息量极少”的一段,比如一段文档只说“见附录二”,没实际内容。最终我的策略是 Top 5 都进提示词,同时在提示词里让模型参考多个资料输出一个综合回答。这样既利用重排序的精度,又不丢失多段信息互补的好处。

6. 不够满意的部分:检索增强的边界在哪

RAG 把幻觉降下来了,但不能归零。

一种常见残漏是“资料存在但检索不到”。说到底,RAG 的回答质量上限,取决于“检索能不能召回到正确段落”。向量检索本质是近似搜索,模型对语义的理解受训练数据和微调效果影响,遇到领域性极强的说法,召回还是可能出错。某次测试中,用户问“月光石的保养”,文档里写的是“宝石类饰品需避免接触化学品、避免碰撞、洗澡前摘下”,语义距离高度相关,但向量检索硬是把“月光石”匹配偏到了“月光”,返回了一块完全无关的天文知识。

另一种残漏是“资料本身就是错的”。RAG 只能做到“忠实于给定资料”,不能替知识库做真实性审计。如果研发团队上传的文档本身写错了参数,RAG 会忠实地把错参数回答给用户。这时真正需要补的就不是 RAG 能力,而是知识库的产出和审核机制。

看到这里应该能理解,RAG 不是大模型时代的万能药,但它确实以很低的成本解决了一个非常关键的问题:把模型的生成能力与知识的持续更新解耦开。模型只需专注“怎么把话说好”,知识正确与否由知识库负责。这个分工思路,是这套系统给我带来的最大启发。

7. 踩坑与心得:这些细节才是决定体验的地方

7.1 常见问题速查

把过程中遇到的高频问题整理成表,方便你直接对照排查。

现象可能原因解决方案
回答与资料不符温度过高 / 提示词未约束温度降到 0.3 以下,规则中明确“只根据资料回答”
检索结果明显不相关切块过大或过小 / 嵌入模型不适配检查切块边界,换用领域化嵌入模型
知识库更新后回答没变化向量索引未同步更新确认增量更新任务是否执行,索引重建后重试
类似问题时好时坏缺少重排序环节加入交叉编码器精筛,缩小输入范围
特定名词永远检索不到切成碎块 / 词向量未覆盖专有名词词典合并,或添加同义扩展块
资料中找到答案但回答护搪Top 1 相关但信息不足改用 Top 5 进提示词,多段互证
回答总说“不知道”相似度阈值过高调整阈值,均衡准确率与召回率

7.2 几个反常识的经验

第一个反常识:不是资料越多越好。把 Top 20 全部塞给模型,效果通常不如精选 Top 5。信息过载时,大模型会“平均用力”,反而抓不住关键段落。适量的资料加上清晰的结构提示,比堆砌资料更可靠。

第二个反常识:嵌入模型越大不一定越好。大模型嵌入能力更强,但也更慢更贵。一个轻量但经过领域微调的嵌入模型,在小规模企业知识库上的检索效果,并不比通用大模型差多少。真正决定效果的,是文档切块质量和知识库覆盖率,其次才是嵌入模型选型。

第三个反常识:提示词写“不要编造”不如写“只引用资料”。前者是抽象要求,模型不一定能落地;后者是具体指令,模型更容易遵循。把“禁止做某事”转换成“应该怎么做”,是提示词工程里很实用的一条经验。

第四个反常识:评测集比算法优化更重要。没有评测集时,我反复调参但无法判断好坏;建了评测集之后,每次改动都有据可依,反而更快逼近了最佳配置。花一个下午构建评测集,能省接下来两周的返工时间。

7.3 更适合自己项目落地的最小方案

如果你也在做 RAG,可以参考我用下来的最小可行方案:一个轻量开源的向量数据库做存储,一个中文嵌入模型做向量化,一个大模型 API 做生成,再用少量脚本完成切块、清洗、索引构建。整套系统完全跑通,后端加运维,工作量在一到两周左右。

依我的经验,在这个最小方案的基础上,按以下顺序迭代价值最高:先搭评测集、再做混合检索、然后加重排序、最后才是换更大的嵌入模型或做成多级知识库。很多人一上来就追求复杂架构,效果反而兜不住。

我在实际项目里的最终配置是:中文嵌入模型、混合检索、交叉编码器重排序、温度 0.2、Top 5 上下文。这套配置下,测试集中的准确率从初版的约 82% 提升到了 93%,无答案问题的拒答率从 30% 提高到 85%。对业务场景来说,这个水平已经足够支撑上线。

最后再分享一点个人体会:RAG 的“知识库”才是这个系统真正的核心资产。模型、算法、框架都会随时间迭代,但一份结构清晰、覆盖全面、持续更新的知识库,会持续给系统提供稳定价值。我后期几乎所有精力都花在了知识库治理上——给文档分类、标注有效期、补充同义词、清洗过期内容。这些事看起来琐碎、不性感,但对 RAG 系统整体体验的提升,比任何花哨的模型调优都来得直接。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询