
1. 大模型“一本正经胡说八道”的背后RAG到底治什么病我最早接触到 RAG检索增强生成Retrieval-Augmented Generation这个概念是在给一个内部客服问答项目做大模型落地评估的时候。当时我们拿通用大模型直接对接用户问题Demo 阶段效果看着还行一问到产品退换货政策这种需要查最新规则的问题模型就开始自由发挥了。它不会说“我不知道”而是非常自信地告诉你“根据相关规定7天无理由退换货适用于所有商品”。问题是我们压根没有那条政策。这就是大模型最典型也最要命的痛点幻觉。很多刚入门的朋友会把幻觉理解为“模型变傻了”其实不是。大模型的本质是一个概率语言引擎它生成下一个词的依据是训练时学到的统计规律。只要某个知识点在训练语料里出现过、或者和语料中的某些片段长得像它就倾向于把这个知识“润色”后输出哪怕这个知识已经过时、被撤销、或者压根不存在。这在开放域闲聊里没什么问题但在企业知识库、产品文档、法律条款、医疗建议这些对准确性要求极高的场景里几乎是一票否决的问题。RAG 解决的就是这个矛盾。它的思路非常朴素既然你训练语料里没有我这份最新的内部文档那我就先把这份文档检索出来把相关内容拼进你的输入里逼着模型“基于我给的资料回答”而不是基于它自己那套过时的记忆回答。用大白话说RAG 就是给大模型配了一个“开卷考试”的外挂。模型不再是闭卷默写而是先翻书再答题翻到哪一页由检索系统决定答得好不好由大模型生成能力兜底。对于程序员来说RAG 的另一层价值在于它的门槛。它不需要你重新训练模型不需要几千张 GPU 卡也不需要你懂复杂的强化学习和 LoRA 微调流程。你只要有一套可用的向量检索环境加上对 prompt 的基本理解就能在几天内搭出一个效果还不错的问答系统。这也是为什么我强烈建议还没接触过 RAG 的朋友先把它搞明白——它是大模型从“玩具”变成“生产力工具”的关键一步。当然RAG 也不是万能的。它解决的是“知识缺失”和“知识陈旧”的问题如果你想让模型学会一种全新的说话风格、学会做某种特定格式的报表那 RAG 帮不了你那是微调或者领域训练的事。这个边界要先划清楚不然项目后面很容易跑偏。2. 一套可复现的最小RAG流程从文档切分到答案生成2.1 先把 RAG 的完整链路在脑子里画出来很多人一上来就急着调库、跑代码结果跑通了也不知道每一步在干什么出了问题更不知道从哪里排查。我建议先花十分钟把整个流程走一遍如果你能用自己的话把下面这个链路讲清楚代码层面的东西其实都是细枝末节。RAG 的完整流程可以拆成三个阶段索引构建Indexing、检索Retrieval、生成Generation。索引构建阶段把文档切分成小块每一块用 Embedding 模型转成一个向量然后把向量连同原文一起存进向量数据库。这一步是离线做的相当于给所有文档做了一套“坐标定位”。检索阶段用户发起一个问题把这个问题也用同一个 Embedding 模型转成向量然后去向量库里找最相似的那几个向量片段。找到的内容叫“上下文”是被拼接给大模型的参考资料。生成阶段把用户原始问题加上检索到的上下文片段一起组装成一个 prompt发送给大模型大模型根据上下文内容组织答案。这一步的关键是 prompt 设计——你要明确告诉模型“只根据下列资料回答不要使用你自己的知识”。这三个阶段任何一个出问题最终效果都会打折。文档切得不好检索就召回不对检索召回的片段不对大模型再聪明也答不对prompt 里没有限定规则模型就可能无视上下文自己发挥。所以不要只看某一个环节RAG 是个全链路问题。2.2 文档切分决定上层建筑的地基工程切分Chunking是 RAG 里最容易被低估的环节。很多初学者会用最简单的“按固定长度切”比如每 500 个字符切一块。这种方案跑通 Demo 没问题但遇到真实业务文档就很容易翻车。我踩过最典型的一个坑一份产品操作手册里有一个“退货政策说明”的章节里面有表格、有备注、有引用用固定长度切完后一张完整的表格被腰斩成三块检索时只召回其中的某一行模型根据那一行生成答案结果把“仅限未拆封商品可退”理解成了“所有商品可退”。这里我补充一个判断切分好坏的实用标准切出来的每一块是否在语义上自洽一个块应该是一个完整的信息单元可以是一段话、一个小节、一个表格、一个列表。固定的字符数只是一个兜底手段优先级最高的一定是“按语义边界切”。实际操作中我建议按下述优先级来选择切分策略如果原文档有标题结构Markdown、HTML、PDF 书签先按章节切再把每章内部的段落做二次切分。如果没有标题结构优先按段落切段与段之间用空行识别。段落过长超过模型上下文窗口或者 Embedding 模型最大长度时再按句子边界切。一定不要把一个句子拦腰切断因为那样会直接破坏语义完整性。切分的长度也要考虑 Embedding 模型的输入上限。现在常用的开源 Embedding 模型输入长度一般是 512 或 2048 个 token中文文本大约对应 500 到 2000 字左右。超过上限的部分会被截断等于白切。我常用的做法是设置一个合理范围比如“最少 200 字最多 800 字”同时允许和上一个块之间保留 50 字左右的 overlap重叠避免关键信息恰好落在边界处被丢掉。2.3 Embedding 模型选型与向量库选择的经验Embedding 模型是 RAG 里负责“语义理解”的组件。它的任务是把一段文字变成一个固定维度的向量让“意思相近的文本在向量空间里距离更近”。选 Embedding 模型时我见过不少朋友直接照搬某个项目的推荐这容易出问题——不同领域、不同语言的文本分布差异很大同一个模型在通用语料上表现好不代表在你的垂直领域里也好。给你一套可执行的选型思路先在公开评测集上筛选两三个候选模型比如针对中文、英文分别做检索召回率对比再用你自己的业务数据建一个小测试集约 50 条问答对就够了分别跑一遍 RAG 全流程看最终答案质量。这里特别提醒不能只看检索准确率还要看真实问题的答案正确率。我之前遇到过检索命中率 90% 但答案质量一塌糊涂的情况原因是召回的片段排序位置不对模型虽然看到了正确信息但被其他噪声干扰了。向量数据库的选型相对简单。小规模项目几万条文本片段以内直接用一个轻量方案就够了比如在 Python 里用带向量索引的轻量库数据量到了百万级再考虑上分布式的向量服务。很多云厂商都有托管的向量数据库产品不过如果是内部研发环境我更倾向于用开源组件自建方便调试、也方便迁移。从成本角度考虑初期不建议在向量数据库上花太多精力先用最小的方案把全链路跑通等数据量增长自然会有迁移的动力。2.4 检索阶段的两步关键操作检索阶段默认的动作是把用户问题转成向量后做相似度搜索。但这里有个隐藏问题用户的提问方式往往和文档的表述方式差异很大。比如文档里写的是“商品自签收之日起7日内可申请无理由退货”用户的问法可能是“我昨天刚收到货现在能退吗”。这个问法就算 Embedding 模型能力很强也不一定能和文档里的那句话完美对齐。解决这个问题的常见手段是先把用户问题改写成一个更适合检索的查询语句再去做向量搜索。这个改写动作可以交给大模型来做。比如用户问“刚收的货能退吗”改写系统把这句话转成“商品在签收后是否可以申请无理由退货以及退货时限要求”这样再去和文档片段做向量匹配命中率会明显上升。这个技术叫HyDEHypothetical Document Embeddings原理是先用大模型生成一个假设性的答案文档再拿这个假设答案去检索因为假设答案和真实文档的语料风格更接近。TopK返回多少条片段的取值也值得单独说。我见过有人上来就设 TopK10结果模型生成答案时被夹在十段可能互有冲突的信息里产生了更多错觉。对于绝大多数问答场景3 到 5 个片段其实就足够了。检索质量高的情况下不需要堆砌大量上下文检索质量不高的情况下堆再多也只会增加干扰。我常用的策略是 TopK5然后在 prompt 里要求模型“当且仅当给定资料包含足够信息时才回答否则明确说明”。这样即便某个片段是噪声模型也能够主动跳过。3. 构建中最容易翻车的三个环节与实际排查思路3.1 检索几乎召不回有效内容先别怪 Embedding 模型我在帮某开发者的内部知识库项目调优时遇到过一个非常典型的案例索引构建时把 PDF 文档直接交给了某个解析库没有做任何预处理检索阶段不管问什么都召不回和问题相关的片段召回来的全是乱码。他的第一反应是 Embedding 模型选得不行想要换一个更大的模型。我让他把向量库里某几条内容打印出来看一眼发现里面全是 PDF 页眉页脚、章节编号的碎片和重叠的排版文字根本没法看。这时候换再强的 Embedding 模型也是白费。这个坑的根源在于向量检索只负责“语义相似度匹配”它不负责理解“这段文字有没有意义”。一个全是乱码的文本片段和你的问题在向量空间里可能恰好离得很近但它没有任何可用的信息。所以排查检索质量的时候第一步不是换模型、调参数而是检查索引里存的原文长什么样。我这次排查的链路如下随机抽 10 条索引里的文本片段肉眼过一遍确认文本质量正常。发现大量片段源自 PDF 页眉页脚的重复内容。回到文档解析环节增加版式过滤剔除页眉页脚、目录、空白页等噪声。重新生成索引检索准确率明显回升。很多文档处理库都有“解析后重排”的能力不只是把 PDF 当图片识别而是保留标题层级、列表、表格结构。RAG 构建中文档解析的重要性至少不亚于 Embedding 模型的选型。如果这一步做得糙后面所有环节都跟着吃亏。3.2 召回了正确片段但生成答案还是错的另一种情况更让人困惑检索结果明明是对的——相关片段里已经把答案写得很清楚了——但大模型生成出来的答案还是错的。我排查过的一个项目就出现这种情况用户问“月度报表的提交截止时间是几号”文档片段里清清楚楚写着“每月第 3 个工作日之前提交”大模型给出的答案却是“每月最后一个工作日之前提交”。这个问题出在哪我一次一次地检查了 prompt发现其中并没有明确要求模型只能依赖给定资料回答提示语写的是“以下是一些可能相关的背景资料”。对大模型来说这句话的约束力约等于零。它依然会优先调用自己的内部记忆——“提交报表的截止日期通常在月末”——而不是基于给定的上下文回答。这种问题的排查思路把完整的 prompt 打印出来先看检索到的上下文是否真的拼进了 prompt。检查 prompt 里的角色设定和约束指令是不是足够明确。检查给定的上下文是否被其他重复的、无关的内容挤占。修复方式是在 prompt 里加入强约束比如你是一名企业知识库问答助手。请严格根据下面提供的资料回答问题。 如果你无法从资料中找到答案请直接回复“根据现有资料无法回答”。 不要使用你自己的知识不要对资料内容做引申推测。加了这句之后同样的检索结果答案正确率马上就不一样了。顺带一提prompt 的这个约束在中文环境下尤其重要因为中文大模型在开放回答时的“自由发挥”倾向表现得比英文更明显。3.3 多轮对话之后检索上下文被“污染”RAG 项目做到后面基本都要支持多轮对话。用户先问“上个月的销售数据是多少”再问“那这个月呢”如果系统直接把“那这个月呢”拿来当检索 query检索出来的片段大概率是错的。这不是模型的问题而是检索 query 的设计有缺陷。正确做法是在进入检索阶段之前先把多轮对话的省略指代问题做一次“指代消解”。可以先用大模型把对话历史压缩成一个当前问题比如“从上个月的数据来看趋势怎么样”改写为“本月销售数据与上月相比的变化趋势是什么”再去执行检索。我踩过的坑是在做指代消解时把整段历史全部塞给改写模型导致模型迷失在上下文里改写结果还不如直接用原始问题。后来我限制只保留最近两轮用户问句效果反而更好。原因是对话改写任务的目标是“提取当前意图”而不是“总结整段对话”。输入太多反而引入噪声。另外改写后的 query 要拿来做检索所以它必须是一个信息完整的独立句子不能有“那这个呢”“上面提到的那种呢”之类的指代词残留在里面。4. 从“能用”到“好用”RAG工程化的几个优化方向4.1 Rerank 二次排序把最相关的内容顶到最前面向量检索的问题是它的相似度度量是“整体的语义相似”不是“针对当前问题的答案匹配”。举一个容易理解的例子用户问“什么样的鞋子适合雨天穿”文档里有一大段都在讲鞋子的保养方法只在最后顺带提了一句“防水面料适合雨天”。整段的向量和用户问题可能已经很接近了但真正有用的信息只有一句话。初次检索向量相似度搜索会把这段文字整体排进候选集但它的排序位置可能不够靠前导致生成时被更靠前但相关性较弱的内容抢占了上下文位置。要解决这个问题就引入Rerank重排序这一层。Rerank 的思路是向量检索阶段先放宽条件召回 20 到 50 个候选片段然后用一个专用的交叉编码器模型cross-encoder把“用户问题只有这一个片段”逐对计算匹配得分再按得分重新排序只保留 Top 3 到 Top 5 进 prompt。交叉编码器的计算量比向量相似度大得多但精度也高得多。好处是它不再依赖 Embedding 模型的单向语义压缩而是真正看到了问题和片段之间的交互。工程上的惯用做法是向量检索负责“广撒网”Rerank 负责“精挑细选”。两者配合之后RAG 的答案质量会有一个明显档次的提升尤其是用户的提问方式和文档表述风格差异大的场景效果显著。代价是要多一次模型推理响应时长会增加几十毫秒到几百毫秒但对于大多数知识库问答场景来说完全可以接受。4.2 上下文压缩不要一股脑把 TopK 全塞给大模型很多人会进一个误区为了让模型“看得更多”把 TopK 调到 8、10甚至把整个章节都拼进 prompt。这对于长上下文模型来说表面上看没毛病但实际效果不但没有变好反而会出现“细节淹没”问题——正确答案确实在上下文中但模型的注意力被大量无关细节分散掉了。更好的方向是在进入 prompt 之前先做一次上下文压缩Context Compression。这一步的目的不是检索——因为检索已经做完了——而是筛掉和当前问题无关的句子只保留候选片段中和答案直接相关的部分。这种做法在文档很长、内容有较多铺垫时的效果特别好。有一类工具专门做这件事内部实现是把候选片段再喂给一个轻量模型让它提取“和问题相关的句子”然后只把提取出的结果拼进 prompt。我实践中更推荐一种轻量做法先让 Rerank 把 Top 5 的结果按得分排序然后在 prompt 里给每个片段标注来源编号并指示模型优先依据编号靠前的片段回答。这个方案不需要引入额外的模型也能达到清晰有效的效果。大模型通常会对上下文开头的部分更关注把最优片段放在 prompt 开始的地方是一种低成本且有效的策略。4.3 混合检索与评估闭环向量检索不是唯一的检索方式。在生产环境里我通常建议保留一个后备方案关键词检索BM25。原因是向量 Embedding 对专有名词、缩写、型号名称的匹配能力有限。用户搜索“XK-200 型传感器故障代码 E-32”时关键词匹配往往能直接把包含这段型号的记录捞出来而向量检索可能因为语义空间里没有这个型号的“概念位置”而漏掉。混合检索的典型做法是同时跑向量检索和关键词检索然后对两路结果做融合排序最后再用 Rerank 统一精排。这个工程实现不难但对效果稳定性的提升却非常明显。比如我维护的一个工业设备故障排查助手处理型号故障码这类精确匹配问题时混合检索的准确率比纯向量检索高出将近 30 个百分点。如果你的业务文档里包含大量名称、编码、编号类信息混合检索基本是必选项。最后说说评估。RAG 系统不能只靠“感觉效果不错”来迭代我个人建议无论项目规模大小都建一个小的评估集包含以下内容5 到 10 条标准问答、几条需要引用多文档的复杂问题、几条故意超出知识库范围的无关问题用于测试模型是否会被迫编造答案。每次改动任何一个环节之后用这个评估集跑一遍对比答案的准确率和不编造率。有条件的还可以加上自动评估指标但我是建议先人工核对一轮性价比最高。没有评估闭环的 RAG 优化基本等于闭眼开车。5. 用了一个月之后的大实话与小建议5.1 什么时候不要硬上 RAGRAG 确实能解决很多问题但它不是所有场景的最优解。在这个话题上聊点相对冷静的个人体会。如果你的需求和知识文档几乎不相关你需要的其实是模型自身的能力或者微调RAG 帮不上忙。举例说你想要一个模型把所有技术方案的输出格式统一成固定模板、固定语气、固定汇报逻辑那更适合做指令微调。RAG 适合的是“知识在变、规则在更新、并且有现成全量文档”的场景——政策问答、产品手册、操作指南、故障排查、新人培训辅助诸如此类。另外如果直接让用户从一份几千行的文档里搜索内容就能高效解决问题那么做一个检索系统就足够了不必硬套 RAG。我记得有一次在某个讨论组里看到有朋友问“我只需要让用户能在文档里搜到对应章节为什么还要接大模型”这个问题的答案是如果目标仅仅是“找到内容”那传统检索就够了RAG 的增量价值在于“找到内容之后用自然语言把答案组织好、解释清楚”在跨章节信息整合的场景下尤其明显。想清楚这一点你才能决定要不要接生成这个环节。5.2 几个每次都会用到的实用小技巧最后分享几个我每次搭 RAG 都会顺手加上、且验证过确实有用的细节第一切分参数不要一次定死。我一般会让切分结果“先能改写再定死”。什么意思新接一批文档时先不直接建生产索引而是随机抽几个段落用代码生成 Embedding 后和几个测试 query 跑一下相似度看看召回的内容是不是真的和测试 query 语义相关。预期不对就调切分策略直到召回肉眼可见合理了再全量建索引。第二Prompt 里的分隔符要稳定。我习惯用反引号包住每一条检索到的片段并在片段开头加上来源编号比如“资料1反引号内容”。这样模型能清晰分辨哪部分是说理、哪部分是引用。调度得当的话模型还会在答案里复述编号对应的来源让问答案的人可以对原文核对这是企业场景里很看重的一项能力。第三不要一次性把历史对话全部塞进上下文。每轮对话都要考虑问题改写和上下文长度控制。我习惯的做法是用一个“对话清理”步骤把最近一轮独立问题提取出来检索完生成答案后只把当前这一轮的问答拼回历史中其他历史内容不再进入检索。第四日志要记录全链路。线上运行 RAG 的时候我强烈建议把用户问题、改写后的 query、检索出的 TopK 片段编号、Rerank 分数、最终的 prompt、生成结果这一整条链路记下来至少保留一段时间。任何一次线上答案翻车只要翻一下日志马上就能知道是检索召回不对、是 Rerank 排序不对、还是生成阶段出现了幻觉。没有这套日志做 RAG 线上运维出了事故就像大海捞针。5.3 后续还可以往哪里扩展RAG 的项目边界一般会往两个方向扩展一个是“多路知识源”比如把企业内部文档、权限控制、在线标注知识库同时接入检索让系统能在一个统一入口处理不同来源的知识查询配合相应的版本管理和定时增量更新。另一个方向是“Agent 化”把 RAG 升级成带工具调用的问答系统比如用户问“这个型号最近有没有售后升级公告”时不只是检索静态文档还能主动触发查询接口去拿最新状态再结合检索知识组织回答。这两个方向都是在 RAG 主线上的自然演进——先保证检索回得准、生成答得对再逐步把动态数据接入、权限过滤、多步推理这些能力加进去。我亲眼见过不少项目在第一步就急着想做完所有能力结果检索效果一塌糊涂就盲目堆功能后面返工成本很高。凡事先把基础链路的质量打磨稳定再考虑扩展方向维护起来的难度会完全不一样。RAG 构建是个入门容易、做深很难的领域。好在它的每一层优化都有明确的思路去排查和验证对技术背景不同的人也比较友好。希望这篇整理能帮你少走一些弯路把精力花在真正影响效果的地方。