先交代个背景我做 RAG 相关项目四五年了从最早的“拿向量库拼个 demo”到后来给多个业务线做生产级知识问答。前 12 章更多在讲“怎么把 RAG 跑起来”而真正的麻烦从来不在搭骨架而在调优——你明明把文档灌进去了、接口也通了可一问就露馅要么答非所问要么拿着答案胡说要么检索结果跟上下文完全对不上。第 13 章就把这件事摊开聊RAG 系统调优到底在调什么、按什么顺序调、用什么工具调、怎么用数据证明它真的变好了。适合正在做知识库问答、客服助手、智能文档检索的研发同学也适合那种“demo 能跑但不敢上线”的团队。这一章的思路很直白把一条 RAG 链路拆成数据、检索、生成、评测四段每一段分别讲可落地的调优手段。顺手还会把几个被问烂的词拉出来划清边界——RAG 和 MCP 到底什么关系、Agentic RAG 和普通 RAG 有什么区别、Ontology RAG 该不该碰。内容以 langchain / langchain4j / semantic kernel 这类常见框架为背景但核心方法论不绑定任何框架你听完直接拿到自己项目里套。1. 调优之前先想清楚RAG 系统到底在调什么1.1 摆正调优视角别一上来就换模型很多人调 RAG 的第一反应是“再换个更强的 LLM”。这句话不能说错但大多数线上问题根本轮不到模型背锅。一条 RAG 链路至少有七个环节数据清洗、切块、Embedding、向量存储、检索召回、重排序、提示词组织最后才是生成。任何一个环节糊了下游都会跟着崩。打个比方这像你去图书馆找资料写论文不是“写”这个动作出毛病而是“查”这一步出的毛病图书馆里压根没有那本书数据缺失、书被乱放在错误的楼层切块与索引不对、你只看了书目没翻正文检索策略太弱、抄了一段没抄出处生成时没有引用上下文。这些问题在链路上叫“数据侧问题”“检索侧问题”唯独不是“模型侧问题”。所以调优第一步不是动手而是给问题归因。我的经验是先把链路里每一个环节的“可观测点”列出来。比如数据入库后抽样看 20 个 chunk肉眼扫一遍内容是否完整、有没有乱码、表格是否被截断。检索出来的 top5 结果人工判断相关还是不相关。把检索结果原样拼进提示词但让模型“不回答、只判断这些上下文是否够用”。最后才看生成质量。这套归因做完一半的问题已经暴露了。剩下那些确实是模型导致的问题再讨论换模型也不迟。1.2 瓶颈先测量再动手别凭感觉调参。RAG 调优没有评测集就等于盲人摸象。我建议任何项目开工第一件事就是先做一份 50~100 条的高质量问答对覆盖业务里最常见的问题类型。不需要太复杂但必须记录标准答案、出自哪份文档、问题属于哪一类事实型、比较型、流程型、多跳型。有了这套基线先原样跑一遍统计出三个最基础的指标检索命中率正确答案对应的文档片段有没有出现在 top5 / top10 里。忠实度回答里有多少内容是有上下文依据的有多少是模型自己脑补的。答案相关度最终回答是不是用户想问的那个东西。这三个指标不是学术概念是线上事故的预报器。我在实际项目里见过很多团队折腾一周调整了各种参数最后回头一看评测集的命中率反而降了 5 个百分点。没有基线你的“优化”就是随机游走。1.3 RAG、Agentic RAG、MCP 先划清边界搜索热词里高频出现的“rag和mcp区别”其实是问错了对象这俩根本不是同一层级的东西。RAG检索增强生成是一种“把外部知识喂给大模型”的模式解决的是模型不知道、记不住、易胡诌的问题。MCPModel Context Protocol是一种标准化接口协议解决的是“模型怎么调用外部工具/服务”的问题——比如查天气、查数据库、操作业务系统。RAG 甚至可以做成 MCP 里提供的一个服务端能力。Agentic RAG 则在两者之上加了一层“自主决策”模型不再只是“固定先检索再生成”而是自己决定要不要检索、检索几次、查不到要不要换一种查法、要不要把多轮查询拆开。Ontology RAG 更进一步把知识组织成带关系图谱的形式适合回答“A 和 B 有什么关系”这种单靠向量相似度很难命中问题。这里我要给一个非常实际的观点别为了追热点把架构搞复杂。80% 的知识问答场景普通 RAG 一层查询改写就可以打得很稳只有当业务确实需要多步推理、需要访问多个业务系统、需要应对复杂查询意图时才考虑引入 Agentic 循环和 MCP 组合。2. 数据侧调优切块、清洗与表格入库2.1 文档清洗垃圾进不了知识库也出不了好答案RAG 调优最容易见效、也最容易被忽略的一个环节就是数据清洗。很多人从内网拖下来一堆 PDF、Word、Excel直接切块灌进向量库然后抱怨检索结果差。我抽样看过不少这类数据问题触目惊心PDF 里带页眉页脚被切进 chunk 后变成“第 3 页”“XXX 公司密件”表格被解析成乱序文字列表项被拦腰截断。基础的清洗至少要做这几步统一文本格式去掉 PDF 残留的换行符、多余空格、OCR 噪声。识别页眉页脚、目录、水印能去就去去不掉就标记成 metadata检索时不参与内容匹配。对扫描件先过 OCR但 OCR 结果务必人工抽检识别错一个字可能让整个 chunk 失真。对 Excel / CSV 类文件不要直接按行切割先转成结构文本再看。这里分享一个我自己的习惯清洗之后把前 100 个 chunk 打印出来从头读一遍。这不光是验证解析效果更是建立“这个 chunk 看起来像不像一段能回答问题的话”的直觉。2.2 切块策略从“越短越准”到“语义切分”切块可能是调参最敏感的一环。固定窗口切分最简单直接chunk_size512、overlap50但代价是出现半句话问题以及多段高度相关的信息被拆到相邻两个 chunk 里导致检索时两个片段都只命中一半。实际项目里我一般按“内容结构”决定切分方式切分方式适用场景优点风险固定窗口 overlap长文本、无明显结构的新闻/报告实现简单、参数好调容易切断语义、检索噪声大按段落/标题切分产品文档、帮助手册、Markdown 文档颗粒度合理、结构完整段落太长时还得二次切语义切分会议纪要、对话记录、逻辑跳跃多的文本按语义边界切关联度更好计算成本高边界判断有误差小 chunk 大 context需要精确定位原文片段检索准、上下文丰富流程复杂要维护两级索引我比较推荐的通用方案是“段落切分 区块内补全”以 Markdown 标题层级或空行为主边界切分如果单段超过 800 字再按句子二次切并保留前文摘要作为补充上下文。这套方式在常见框架里都有现成实现比如 LangChain 的MarkdownHeaderTextSplitterLlamaIndex 也有对应 splitter。注意切块参数没有标准答案业务字频、回答颗粒度都会影响最优参数。建议做一组 A/B固定chunk_size384/512/768每组跑同样的评测集看命中率和忠实度的变化通常半小时就能找到相对较优档位。2.3 表格和结构化数据入库系列产品表怎么处理搜索热词里有“系列产品表格怎么存入rag知识库”这是个好问题。表格类数据直接按行切块检索时上下文会支离破碎直接按整表切块又会超过上下文窗口、浪费 token。我见过最常见也最不可取的做法是把 Excel 转成 CSV 后按行塞进向量库然后让模型“看图猜答案”效果自然惨不忍睹。根据数据用途不同我会分三种方案处理方案 A按行为语义转成自然语言。比如“仓库编号 WH-001产品名 LK-300库存 120单价 899 元”然后把这一行以一条“记录文本”的形式作为一个 chunk存的时候保留“这条记录属于哪个表的哪个字段”作为 metadata。适合点查类问题比如“某某产品还有多少库存”。方案 B以“表头 多行片段”组合切块。比如每个 chunk 带上表头再带 10 到 20 行数据然后用标题或描述性字段做索引。适合需要对比多个产品的场景。方案 C结构化查询不硬走向量检索。当前面两种方案都喂不准时直接上 Text-to-SQL 或 Query Rewrite 数据库查询。比如问“价格低于 500 的产品有哪些”向量检索和文本生成都容易翻车而结构化查询一次就能算对。这类场景如果硬塞进 RAG 知识库换来的是幻觉和算力浪费。我做了一个实践原则表格数据先问“用户要的是数据还是要的是信息”。要数据走结构化查询要信息走语义化 chunk两者都有就把 RAG 和 SQL 并行跑再让模型从两个结果里综合。这样比把表格硬塞进一个流程里稳得多。3. 检索链路调优召回、重排与过滤3.1 向量召回不是万能的混合检索才稳只要做过 RAG 的线上项目都会遇到同一个尴尬向量检索对“意思相近但用词不同”的问题很敏感但对“精确名称、型号、编号”这类强标识符信息反而记不住。比如用户问“仓库 WH-011 的库存”如果 Embedding 模型没把 WH-011 和文档里的 WH-011 对齐向量相似度可能排到十名开外。我前几年接手一个 ERP 知识库项目检索质量一直上不去后来做了个改动把 BM25 关键词检索和向量检索做混合用 RRFReciprocal Rank Fusion合并排序。BM25 擅长精确匹配术语编号向量检索擅长同义改写和语义匹配两者互补后检索命中率直接涨了一截。用 LangChain 实现一个混合检索器很直接from langchain.retrievers import BM25Retriever, EnsembleRetriever from langchain.vectorstores import Chroma bm25_retriever BM25Retriever.from_texts(texts, k10) vector_retriever Chroma(embedding_functionembedding_model).as_retriever(search_kwargs{k: 10}) ensemble EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.4, 0.6], ) docs ensemble.invoke(仓库存量 WH-011 多少)权重比例建议按数据形态试如果数据里型号、编号多BM25 权重给到 0.5 甚至更高如果文档偏自然语言向量侧可以再压一压。这个步骤简单收益却很稳定。3.2 重排小模型解决大问题向量检索先召回 top50 到 top100再用重排模型精排取 top5这是线上 RAG 的标配。重排的价值在于把“语义相似但可能不必要”的候选过滤掉留下真正能支撑答案的片段。跨编码器Cross-Encoder重排效果公认最好因为它把 query 和 doc 直接拼接在一起打分能捕捉更细的交互语义。我用 bge-reranker 类模型做重排后评测集上的命中率经常能从 60% 提到 80%。一个我不断强调的细节是重排只看相关度还不够还要看上下文完整性。有些候选片段高度相关但只有一句半截话单独支撑不起回答。这时可以把“片段是否含完整语义”作为重排的后置规则或者直接用小 chunk 检索 大 chunk 拼接解决。重排阈值也别拍脑袋设。比如 top5 里第 5 名的相关度得分如果和 top1 差出一大截宁可只给 LLM 前 3 个片段也不要喂噪声。否则模型会被不相关内容带偏反而出现“检索对了答案却也错了”的奇异情况。3.3 元数据过滤与权限隔离很多团队忽略 metadata 的用法。数据入库时给每个 chunk 打上文档来源、部门、品类、时间范围、权限级别等标签检索时先按权限过滤再召回既能提升准确率也是合规必需。比如同样的产品文档销售部门想看价格策略研发部门只能看技术规格如果不做 metadata 过滤模型可能把不该披露的内容答出来。在业务复杂一点的项目里我还会给 chunk 增加“知识类型”标签比如“规格参数”“操作流程”“FAQ”“价格政策”然后在检索时根据用户意图倾向去过滤。这个做法的迭代空间很大很多所谓“检索不准”的问题其实只是忘了利用你本来就有元数据。4. 生成链路调优提示词设计、上下文与多轮对话4.1 上下文工程不是把检索结果一股脑丢给 LLM检索做得再好生成阶段如果把一堆片段不加整理地塞给模型照样会水土不服。上下文组装有三个原则去噪、按相关度排序、标明出处。去噪指重排后只保留高相关片段排序指从最相关到次相关排列让模型优先看到核心答案标明出处则是把每个片段的文件名、章节号、原文位置写进提示词方便模型引用。我自己常用的提示词结构是请基于下面的“参考资料”回答问题。参考资料可能包含不相关内容请只使用与问题直接相关的部分。 如果参考资料不足以回答问题请直接回答“资料不足无法确定”不要自行编造。 参考资料 [1] 来源产品手册-第3章-仓库管理 内容…… [2] 来源库存规范-V2.1-第7条 内容……这样一个提示词结构看着简单但能把很多模型的“幻觉”压下去不少。模型在明确被告知“资料不足就当不知道”之后编答案的概率会明显降低——前提是你真的给了它够用的资料。4.2 多轮对话怎么设计查询改写与指代消解RAG 接多轮对话最大的坑是“指代丢失”。用户先问“A 产品什么时候发货”接着问“它的价格呢”如果第二个问题原样拿去检索模型完全不知道“它”指的是 A 产品。常见的解法是让大模型先做一次查询改写把当前问题结合历史对话转成独立的、完整的查询语句再用改写后的查询做检索。Python 伪代码大概长这样rewrite_prompt 根据历史对话和当前问题生成一个更适合检索库的独立问题。 要求不遗漏关键实体不改变原意。 历史对话{history} 当前问题{question} 独立问题 rewritten llm.invoke(rewrite_prompt.format(historyhistory, questionquestion)) docs retriever.invoke(rewritten)除了改写还要控制历史窗口。把七八轮之前的闲聊内容也塞进 prompt只会白白消耗 token 和干扰模型。我一般只保留最近 3~5 轮并且做一个“历史摘要”字段如果对话太长就先摘要再拼接。这个设计在很多框架里都有现成模块但默认参数不一定合你业务用自己评测集调一遍更有底。4.3 RAG 和 Skill、Agentic 怎么结合再说一个热词“skill怎么和rag结合起来”。在 Semantic Kernel、LangChain4j 这类框架里Skill / Tool / Function 其实就是一个“能力单元”。RAG 完全可以作为一个技能暴露给模型调用比如“search_knowledge_base(query)”模型在回答时主动决定“这个问题需要查知识库”。这样做的好处是当用户问“今天天气如何”这种不需要知识库的问题时模型不会白白检索一遍。更复杂一点可以把 RAG 和其他 Skill 组合成 Agentic 流程。比如一个客服场景先通过 RAG 查产品资料再调用订单查询技能查用户订单状态最后综合两部分内容回答。这就是 Agentic RAG 的典型形态。MCP 的作用则是把这些 Skill 标准化封装成统一协议的 Server让不同 Agent 都能复用。所以区别可以记成一句话RAG 是知识手段MCP 是工具协议Agentic RAG 是调度的思想三者不是竞争关系而是可以叠在一起用。5. 评测体系与调优回归5.1 从“肉眼感觉”到量化指标没有评测体系的调优约等于靠运气上线。我在前面的章节提过 RAGAS 这类评测思路实际使用下来最值得盯的四个指标是指标关注的问题计算方式参考上下文命中率/召回率正确片段是否被检索到答案对应的 ground truth chunk 是否在 topN忠实度回答是否忠于检索上下文LLM 逐句判断是否有上下文支撑答案相关度回答是否正中问题语义相似度或 LLM 打分端到端延迟用户体验是否可接受检索 重排 生成耗时实际操作中我先用检索命中率兜底因为如果答案片段压根没被检索出来生成阶段再怎么调都没用。命中率达标之后再去优化忠实度和答案相关度。这套顺序能帮你少走很多弯路。5.2 5 分钟搭建一个简易评测集评测集不用一步到位。我通常的做法从真实用户日志里抽 50~100 条高频问题。人工给每条问题找答案来源文档并标出正确答案的原文片段。把每条问题记录成{question: ..., context: 标准答案片段, doc_source: xxx.pdf}。用脚本批量跑 RAG 流程输出检索结果和生成答案。人工或 LLM 打分生成一份对比表。一个几十条问题的小评测集半小时就能建好。它最大的价值不是准确反映线上效果而是给你一个“回退坐标”任何一次调整如果评测集分数下降超过 2~3%就说明这次调整有风险可以及时回滚。5.3 回归与基准库调优的护城河有些团队在评测集上狂调把分数刷得很漂亮一上线立刻翻车。原因通常是评测集过拟合或者评测集没有覆盖长尾问题。我建议把评测集拆成两层一个是“核心回归集”几十条高频必须全对另一个是“冒烟集”覆盖各种边界情况、多轮对话、表格问题定期混入新出现的问题类型。每次调整代码或参数就重新跑一遍核心回归集连续跑上一周你就能看出“看似变好”的调优是不是真的稳定。这个习惯救过我很多次尤其当团队同时有好几个人在改不同环节时回归集就是最后的守门员。6. 常见问题与排查技巧实录6.1 三个典型 Bad Case 复盘Case 1检索到了但模型答错。有一次用户问“商品退换货期限”检索结果里明明有“自签收之日起 7 天内可无理由退换”但模型却回答“15 天”。排查发现同一份文档里有“会员可享 15 天退换”的另一段模型基于更靠后的上下文回答了。解决方法是重排后按相关度排序并明确告诉模型“优先采用最匹配的片段片段之间有冲突时标注版本范围”。同时把退换货政策做成结构化的“政策时效表”在生成阶段加入版本提示。Case 2回答含糊像在套话。用户问“A 和 B 套餐哪个划算”系统答案却是复述两段原文没有对比。问题出在检索阶段把两段内容分散在不同 chunk重排 top1 只选了其中一段。我尝试对这种“比较型问题”做意图识别单独走“多文档混合 chunk 对比模板”的流程效果立刻清晰很多。Case 3表格数据答不到点子上。“今年 3 月各区域销售额排名”这类问题向量检索很难命中。最后我放弃了硬塞向量库的方案改成 Text-to-SQL 直接查数再把查询结果交给模型总结。RAG 链路不是只能有一种形态遇到结构化数据结构化路数才更可靠。6.2 资源受限的调优路线不是每家团队都有充足 GPU。对“本地知识库”“个人免费版”这类需求可以用开源 embedding 模型、本地小型 LLM、轻量向量库如 Chroma、sqlite-vec搭建一套能跑的 RAG构建成本很低。但要注意模型变小时幻觉未必减少反而可能增加所以“小模型 高质量检索 严格提示词”就格外重要。开发阶段先用免费或低价的托管 API 打通链路评估业务效果之后再把推理迁移到本地是我比较推荐的路线。这样你把成本集中花在“值得本地化”的模型上而不是一上来就硬啃全套自建。6.3 框架选型速查最后整理一张工具选型速查表算是我做调优时的默认偏好框架适合人群特点调优友好度LangChain / LangGraphPython 生态灵活 DIY组件齐全可控性高高但版本变化快LlamaIndexPython 生态偏数据索引对文档切分、索引引擎封装很深高特别适合索引调优LangChain4jJava/Spring 生态适合已有 Java 技术栈的团队中高但社区物料少Semantic Kernel.NET / Python 生态Skill/Plugin 体系适合企业应用中需要自己搭检索逻辑我个人没有“最好框架”的执念。项目用了多年 Java 就上 LangChain4jPython 团队就 LangChain 或 LlamaIndex。真正决定上线质量的永远是数据质量、检索策略和评测闭环这“老三样”框架只是壳。最后分享一个我踩过 N 次坑之后的经验RAG 系统调优调的不是某一个“魔法参数”而是一个可迭代的闭环——数据侧做清洗和结构化检索侧做混合召回和重排生成侧做上下文工程和多轮改写评测侧做回归集守门。每次只动一个环节改完立刻跑基准分数不降再上线。按这个顺序来你大概率能在两周内把一套 “勉强能跑” 的 RAG调成 “用户愿意用” 的 RAG。