做 RAGRetrieval-Augmented Generation检索增强生成系统这段时间我最大的感受是很多人把 RAG 想得太简单了——以为接一个大语言模型LLM再配个向量数据库把文档往里一灌就能得到一个好用的问答助手。实际跑起来才发现效果完全不是那么回事问细一点检索不到问泛一点答非所问文档一多还容易串台。这篇文章不聊概念只聊实操。我会从数据准备、索引构建、检索策略、生成环节和评测闭环五个方向把我在实际项目里验证过、踩过坑的 RAG 性能优化方法完整拆开讲一遍。内容偏工程向适合正在做 RAG 落地、或已经开始调优但效果不理想的开发者看完可以直接拿去对照自己的系统找问题。1. 先看清 RAG 的性能瓶颈卡在哪个环节很多团队一上来就调模型、改提示词折腾半天效果还是不行根本原因是没有定位清楚瓶颈到底在哪。RAG 是一条流水线任何一个环节出问题最终都表现为回答质量差。所以第一步不是优化而是诊断。1.1 四段流水线里谁的短板在拖后腿RAG 主链路可以粗略分成四段数据预处理与切片、索引构建、检索召回、生成回答。每段出问题的表现完全不同我列了一个对照表方便你对号入座环节典型故障表现内在原因数据预处理回答内容看起来通顺但信息是错的原文在切片时被截断语义不完整索引构建相似问题检索结果不稳定embedding 模型与语料领域不匹配检索召回明明库里有答案却召回不到检索策略单一只用了向量召回生成回答检索内容正确但大模型没用上提示词不够明确上下文组织混乱判断方式其实很简单直接在检索层只输出中间结果不看最终回答。如果你发现检索回来的 top 5 片段里根本没有正确答案那问题在上游如果检索结果里有答案但最终回答没有引用或者用错那问题在下游。先做这一步能省掉后面 80% 的无用调参时间。1.2 用一个模拟项目案例判断瓶颈优先级我用之前接手的一个某企业内部文档问答项目举例。当时的症状是用户问报销流程中发票丢失怎么办系统的回答却是在讲发票粘贴规范。粗看是模型理解错实际把检索结果单独打出来后发现召回片段里根本没有丢失相关内容全是在讲粘贴。问题出在两个地方一是原始 PDF 里丢失和粘贴在同一个大段落固定长度切片时被切散了二是检索时只用了向量召回导致文本里带有丢失字样的完整段落被截成多段后语义向量相似度被稀释。最后我们同时改了切片策略和检索策略效果才稳定下来。所以优化 RAG 性能的顺序应该是先保数据质量再优化检索链路最后才调生成参数。2. 数据准备与索引构建决定 RAG 性能天花板的源头工程很多人忽略一个事实RAG 的上限不取决于模型有多聪明而取决于喂进知识库的数据有多干净。模型再强检索结果是一堆语义不完整的碎块它也编不出正确答案。所以数据阶段花的每一分钟都会在下游成倍回报。2.1 文档解析不是把 PDF 转文本这么简单我见过太多项目直接用开源的 PDF 解析库把文档转成纯文本就开始切块。结果文档里的表格丢了结构多栏排版的正文顺序错乱页眉页脚混进正文——这些垃圾数据进到索引里检索质量直接崩。正确的做法分三步第一步对文档做版面分析区分标题、正文、表格、页眉页脚表格尽量转成 Markdown 格式保留结构而不是把单元格拆成一串没头没尾的文字第二步清洗非正文内容包括重复的页脚、引用标注、无关注释第三步识别文档的层级结构把父标题和子标题信息提取出来后续切片时可以作为元数据使用。这一步处理完你的语料质量就已经超过一半的同类项目了。2.2 分块策略怎么选chunk size 到底该设多大切片是 RAG 数据工程里最关键也最容易纠结的一环。切片太小单个片段承载的信息量不足检索时语义不连贯切片太大片段里包含太多无关信息向量相似度被噪声稀释而且超出大模型上下文窗口后还要二次截断。我的建议是不要盲目追求固定 token 数先按文档结构切再按 token 数兜底。具体做法是优先看文档里有没有标题段落把每个小节作为一个候选块如果某个小节过长再按 300-500 字左右二次拆分并保留上一级标题作为前缀信息。关于 chunk size 和 overlap我常用的一个组合是单块约 400-600 个 token重叠区间 80-120 个 token。这个组合在中文文档场景下兼顾了语义完整性和召回精度。如果语料是技术手册、合同条款这类逻辑严密的内容单块可以适度加大如果语料是社交媒体、问答记录这类碎片化内容单块要更小、重叠要更多。切片完可以做一个简单巡检随机挑几十个切片人工过一遍看看有没有出现一句话被拦腰截断重要数字被切没了的情况。这类问题靠调参调不出来必须从数据层解决。2.3 元数据与索引参数的配置思路切片完成之后还要给每个切片附加元数据。比如来源文档标题、一级目录、二级目录、文档类型、更新时间等。这些信息有两个用途一是检索阶段可以通过过滤条件缩小范围比如只搜某个版本的说明文档而不是在整个库里漫无目的地找二是拼进生成提示词里帮助大模型确认信息的来源背景降低编造概率。索引构建层面向量索引的参数直接影响检索速度和召回效果。主流方案用的是 HNSW分层小世界图索引其中有两个参数值得认真调M 控制每个节点的连接数值越大召回越准但索引体积和内存占用越高一般建议 16-64efConstruction 控制建索引时的动态候选集大小值越大构建越慢但质量越高一般建议 100-200 起步语料量大时适当调低。如果你的系统对内存敏感还可以考虑把向量量化成 int8 存储精度损失不大但内存能省接近 75%。我个人的体会是索引阶段不要过度优化。只要元数据完整、切片策略靠谱、向量维度选择合理索引参数的调整空间带来的收益远不如检索策略大。把时间留给下一段。3. 检索阶段的提效从单路向量检索到多路召回与重排序如果说数据工程决定了 RAG 的天花板那检索策略决定了你离这个天花板有多近。相当一部分 RAG 性能差的案例问题根源不是数据差而是检索策略太单薄——只靠一个向量检索走天下。3.1 为什么纯向量检索常常翻车命名实体与精确匹配向量检索的本质是语义相似度匹配它擅长处理换个说法但意思相同的查询对应的是模糊匹配能力。但很多业务场景需要的是精确匹配产品编号、订单号、合同条款编号、人名地名、特定型号。这些场景下向量检索的表现反而不如最基础的关键词匹配。举例来说用户问PS-2024-009 号项目的验收标准如果知识库里出现了PS-2024-009这个字符串但 embedding 模型没有把编号和正文内容充分对齐向量相似度可能排不进前 10而传统的关键词倒排索引能立刻命中。所以在做检索优化时切忌只优化语义侧而忽略字面侧。这是纯向量检索方案在实践中最大的盲区也是很多团队加了向量数据库反而更难用的根本原因。3.2 混合检索的权重设计和结果融合混合检索就是把向量召回和关键词召回结合起来分别拿回一批候选片段再合并排序。合并时不要直接对相似度分数取平均因为两种检索的分数分布不在一个量级。常用的做法有两种第一种是 RRFReciprocal Rank Fusion不关心具体分数只看每条候选在各自排序中的位次按照 1/(krank) 计算融合分k 通常取 60。这种方式对分数分布不敏感实现简单效果稳定是我比较推荐的首选方案。第二种是加权归一化先分别对向量相似度和关键词得分做 Min-Max 归一化再按权重相加。关键词权重初始可以设在 0.2-0.4 之间向量权重设在 0.6-0.8 之间然后根据实际检索效果微调。如果语料里专业术语和编号很多关键词权重可以往上调如果用户问法多变以自然语言表达为主向量权重可以往上调。调整的基准只有一个看检索结果的准确率和召回率是否在真实 query 上变好了。3.3 重排序花少量时间换回答质量混合检索只是完成第一轮粗召回。既然粗召回阶段要考虑召回率就难免混进来一些不太相关的片段。你可以在第二轮引入重排序rerank模型对粗召回的结果逐条计算和 query 的相关性分数然后按这个分数重新排序。重排序模型的效果通常比单纯依赖向量相似度好很多因为它把 query 和 passage 放在同一上下文里进行深度语义交互理解更精确。实际项目中我一般会在粗召回阶段取 20-50 条候选用重排序模型筛选出 top 3-5 条喂给大模型。重排序会带来额外的推理延迟每几百条候选增加几十毫秒到几百毫秒不等但换取的是回答质量肉眼可见的提升。考虑到用户对响应时间的感知这个取舍通常非常划算。要注意的是重排序模型的训练领域和你的业务领域也存在匹配问题。通用重排序模型在网上公开语料上效果很好但放到垂直领域如法律、医疗、制造业相关性和准确性会明显下降。如果业务预算允许在垂直语料上做微调收益会非常大预算有限时先选用开源的大参数重排序模型效果通常也好于小参数版本。3.4 查询改写与扩展让用户的话变成检索语言用户的 query 往往不是好的检索语言。有人问得很口语化有人问得很模糊还有人问得特别长。直接拿原始 query 去做向量检索检索效果常常被表达噪声拖累。一个非常有效但经常被忽略的优化手段是在检索之前先用大模型把 query 做一次改写。改写的大致思路是让大模型判断用户的意图输出 2-3 条更规范、更利于检索的查询语句。比如用户问我们公司打印机坏了报销走什么流程改写后的查询可以是打印机损坏报销流程办公设备故障维修报销标准。多条改写后的 query 分别做检索把结果合并再去重能显著提升召回率。这个做法的代价是一次额外的大模型调用但对回答质量的提升通常远大于这点延迟。这个阶段还有一个容易犯的错误过度依赖问题改写后一定能检索到正确答案这个假设。改写的目的是把用户 query 翻译成知识库里更可能存在的表述方式而不是凭空补全缺失信息。如果知识库里本身没有相关内容改写再多次也无济于事所以务必配合数据巡检一起做。4. 生成阶段的二次把关上下文组装与推理参数调优检索结果出来之后真正决定用户体验的是最后一步怎么把检索片段组织成提示词怎么引导大模型产出准确、克制、可溯源的回答。这个阶段被很多人当成随便写写就行但实际上同样的检索结果配不同的提示词输出质量能差出好几个档次。4.1 提示词结构把检索结果摊开给模型RAG 场景下提示词的核心目标是约束大模型让它在给定片段内作答而不是自由发挥。我常用的提示词骨架长这样你是一个基于给定资料回答问题的助手。 请严格根据以下资料回答问题不得使用资料以外的知识。 资料 context {检索片段1标注来源文档A} {检索片段2标注来源文档B} /context 问题{用户问题} 要求 1. 如果资料中有相关信息请基于资料直接回答并在每个关键结论后标注来源编号 2. 如果资料中没有相关信息请明确回答未在现有资料中找到答案不要自行推测 3. 使用简洁、客观、连贯的中文作答不要重复资料中的无关内容。这里的核心在于标注来源和要求 2这两点。标注来源能让模型在推理时把内容和对应文档绑在一起降低张冠李戴的概率不得使用资料以外的知识则是对抗幻觉最直接的一道指令。我见过很多团队不加后面这条模型的发挥自由度一高回答就开始飘。4.2 控制上下文窗口给模型喂多少、喂什么、怎么喂大模型对上下文的理解并不是喂得越多越好。检索返回的 top 3-5 个片段加起来可能已经占到上下文的一半如果文档里还有大量无关细节模型容易被噪声带偏。所以在组装上下文时我建议做两个减法第一按相关性和信息密度对片段做排序把和 query 相关度最高的排在前面。大模型对前部信息的关注度通常高于尾部重要的内容要放在开头。第二对片段做长度截断每个片段控制在 200-400 字以内过长就把完全无关的修饰性内容去掉只保留关键信息。另外一个细节是片段之间的边界标记。如果两个片段来自不同文档要在中间显式隔开让模型意识到这是不同来源的内容。否则模型可能把两个文档的信息混在一起产生一种看起来很有道理但实际是拼接事实的答案。这个问题在知识库文档数量较多时尤其常见。4.3 生成参数与幻觉的权衡温度、top_p、最大长度生成环节的参数设置原则上是为准确度牺牲一定灵活性。RAG 场景里有标准答案不需要模型发挥创意。temperature 建议设置在 0.1-0.3 之间太高了模型容易在字句间自由发挥幻觉概率明显上升top_p 建议设置在 0.8-0.9 左右配合低温度使用。这里的核心原因是RAG 需要的回答应该以给定片段为事实依据而不是追求语言多样性和表达丰富性。max_tokens 的设定也值得注意。很多人只看最终答案是否够长却忽略了一个问题max_tokens 太长时模型在已经完成回答后还有剩余空间容易被诱导继续补几句——这些补出来的内容经常是不必要的推测。我通常是基于检索片段的数量和问题类型预估一个合理范围比如答案通常 100-300 字的问题就把 max_tokens 设在 400-500留出余量但不至于无限扩展。这一步做完你的 RAG 在单条链路层面的优化基本到头了。但链路调优永远不该只靠感觉下一步需要把优化过程变成数据驱动的迭代闭环。5. 评测驱动迭代把 RAG 优化从玄学变成工程做 RAG 优化最忌讳的一件事是拍脑袋改、看感觉定。今天把温度调低了明天把切片改大了全凭主观判断出了问题也不知道是哪个改动引起的。我自己的项目里评测体系的建立对效果提升的帮助甚至超过了任何一次单点调优。5.1 建立评测集和自动化评测指标评测的第一步是准备一批带标准答案的测试问题。这些问题不能只挑简单的要从实际用户问题里采样覆盖简易查询、复杂推理、多文档对比、无效提问知识库中无答案等类型数量至少 50-100 条起步越多越好。每条问题记录它的预期答案类型和检索来源范围便于后续分析失败原因。第二步是用自动化指标衡量回答质量。我目前最常用的一套指标体系包括三个维度指标含义判断方式上下文相关性检索回来的片段是否与问题直接相关检查 top-k 片段中相关片段的比例答案忠实度模型回答是否严格基于检索片段没有自行编造检查回答中的关键论断是否能在片段中找到依据答案相关性模型回答是否完整覆盖用户问题对照标准答案检查要点覆盖率自动化评测可以通过调用大模型打分来实现——让一个评价模型对照问题和标准答案给回答质量打分。虽然不如人类专家精细但胜在可重复、低成本、能持续跑回归测试。我习惯每次改动后跑一遍完整的评测集把三项指标的数值记录下来改动效果好不好一眼就能判断。5.2 线上指标与体验观测评测集是离线对标线上真实用户的行为数据才是最终裁判。接入系统后至少要看这三个数字用户提问后是否触发二次追问、是否在反馈入口点击答案没用、同一个问题是否被反复改写重问。这些都是回答质量差的间接信号。我在实际项目里遇到过一种典型情况离线评测集上三项指标都接近满分但线上用户反馈平平。排查才发现用户会问大量带上下文的口语化问题比如那这个呢后面怎么弄单看 query 根本不知道在问什么。离线评测集里没有覆盖这类问题所以分数好看不代表真实体验好。后来我在前端做了会话历史关联把前一轮的问答内容拼进当前 query 再做检索这才把线上满意度提上来。所以离线评测集必须持续补充线上真实 query否则评测会失真。5.3 缓存、异步与成本控制最后一个容易被忽视的方向是成本和延迟。RAG 系统的开销大头通常不在向量检索而在大模型推理。回答质量优化到位后用户会反复提相似的问题每次反问都重新跑一遍全链路是非常浪费的。一个非常实用的优化方法是在最外层加一层缓存对用户 query 做归一化后与大模型改写后的标准 query 拼接作为缓存键命中缓存就直接返回之前的回答。缓存层还可以配合语义去重把相似的问题归并为一个缓存条目——这里的归并阈值要仔细调太激进会把不同问题错误合并。命中率稳定在 30% 以上时整体成本能省出一大截响应速度也会有明显改善。此外检索链路可以考虑异步化。粗召回、重排序、生成这三个环节之间没有强依赖可以分批并行执行一部分。比如重排序只需要等粗召回结果不需要等生成多个改写后的 query 也可以并发检索。工程上把这些链路拆好RAG 的端到端延迟能从 3-5 秒降进 1-2 秒这个提升对业务满意度的影响非常直接。最后再分享一点个人体会RAG 优化没有银弹数据干净、检索多路、生成克制、评测闭环这四件事是一套组合拳。如果你现在只做了一半比如只调了提示词、只换了 embedding 模型效果上不去是正常的——把这条链路完整走一遍你会看到质的改变。先别急着上更多新功能把现有四个环节的短板补完系统自然会给你惊喜。