ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RAG从Demo到好用:六个关键分水岭

2026/10/7 13:50:47 拓冰建站 浏览量
RAG从Demo到好用:六个关键分水岭 “RAG烂大街了吗”过去一年我几乎每周都能刷到“手把手搭RAG知识库”的教程。LangChain、LlamaIndex、Dify确实把上传文档、切块、向量化、检索、拼接、提问这条流水线磨得越来越顺滑好像跟着点几步一个像模像样的知识库问答就出来了。但真把一套带着真实业务问题的RAG系统丢到用户手里很多人会发现Demo里能答出来的问题放到生产环境十个能翻车六七个。我不觉得这是奇怪的事“烂大街”的从来只是那条流水线本身真正的分水岭在于六个极少有人认真想过的决策点。这篇博文不讲那些“装个库跑通示例”的东西只想聊聊我在企业知识库、客服问答、内部工具这些场景里被数据反复教育后总结出的六处关键分岔路适合已经会用框架搭出检索问答、但正卡在“能跑”到“好用”之间的人。写出来的都是实操、参数和事故现场可以抄作业的部分我尽量标清楚。分水岭一句话说明最容易被忽视的点1. 文档切分决定检索质量的天花板切分不是“能切就行”2. 召回策略决定上限的粗筛环节只靠向量搜索不是万能3. 重排决定最终命中精度召回后直接拼给大模型太粗暴4. 索引与元数据决定系统可用性没有信息架构等于垃圾场寻宝5. 查询理解决定“问对了没有”用户问A系统检索C是常态6. 评估闭环决定优化方向没有评估调优就是玄学1. 第一处分水岭文档切分决定检索质量的天花板1.1 切分不是“能切就行”很多人搭RAG的第一个动作就是把PDF丢进去默认用框架的固定长度切块然后就去调提示词了。这是最典型的误区。切分策略几乎决定了后续所有环节的上限召回、重排、上下文拼接全都在吃切分的红利或苦果。切太碎语义上下文丢失模型看到的是“断头句”切太大一个块里塞了多个主题向量化后语义被稀释检索出来的内容看似相关实际上大量无关噪声混在里面。我在实际项目中见过最典型的一次事故一份50页的产品手册按固定250字符直接切结果所有包含“退款政策”描述的块都被散落在不同章节之间。用户问“退款多久到账”召回的结果里有产品介绍、有发货说明就是没有最核心的退款时效段落。后来改成按章节标题切再把“上一级标题当前段落摘要正文”做成三层块结构同样的问题检索命中率立刻上来了。这不是Embedding模型不行是切分把语义结构破坏了。切分参数也不是拍脑袋定的要看你用的Embedding模型的上下文窗口。以常见的text-embedding-3-large为例最大输入是8191个token中文场景下一个字符大致对应一个token按500字符一块加50字符重叠实际消耗约550 token留给后续重排和拼接足够的余量。如果用的bge-m3窗口也是8192同样可以按这个思路。关键原则就一条不要让单块长度逼近模型的极限否则后续拼接检索结果时很容易超长截断把最该有的信息丢掉。1.2 主流切分策略怎么选选切分策略先看文档本身的形态。固定长度切分只适合纯文本、没有结构、格式松散的内容比如聊天记录稍好一点的是递归字符切分按段落、标点、空格逐级降维切尽量保证断点落在语义边界再往上走是结构感知切分按Markdown标题、PDF大纲、表格边界来切最后是语义切分通过embedding相似度找语义转折点成本高但在长文、多主题混排的文档里效果显著。切分策略适合场景需要注意的问题固定长度日志、聊天记录、无结构文本容易切断语义完整的句子递归字符切分网页文章、一般文档断点靠标点仍可能跨主题结构感知切分Markdown、Word、PDF章节依赖源文档结构完整语义切分长文、主题跳跃明显的内容计算成本高调参难度大这里给一份我常用的递归切分参数可以直接抄from langchain_text_splitters import RecursiveCharacterTextSplitter splitter RecursiveCharacterTextSplitter( chunk_size500, # 中文场景约等于500个token左右 chunk_overlap50, # 重叠太少会丢边界信息太多会增加冗余 separators[\n\n, \n, 。, , , , , ], is_separator_regexFalse, )重叠值不是越大越好。我见过有人把overlap设成200结果相邻两个块几乎一半内容重复检索时不断返回同样的段落白白浪费上下文窗口。overlap的意义只是保住断点附近的上下文50到100之间足够不需要更强。1.3 图片、表格与本地拆解工具“RAG知识库能不能存图片”这个问题我没少被问到。答案很直接向量库存的永远是向量和文本图片本身不会直接进库。你要么用OCR把图片文字抽成文本再入库要么用图像描述模型生成图片的语义描述再当成文本块处理。后者适合需要保留图片“视觉信息”的场景比如产品外观、流程图、界面截图。还有一种做法是用CLIP这类多模态嵌入模型直接对图片生成向量但要注意它与纯文本Embedding的向量体系不对齐混合检索时很容易出现“检索到图像但匹配不准确”的尴尬。表格的处理也常被忽略。很多表格在PDF里看似规整抽出来就是一坨乱码。我建议对表格单独做“表头行内容单元格”的结构化切分或者干脆把表格转成Markdown格式再入库效果比硬切强得多。如果你在Mac上想跑本地拆解工具又不想把敏感文档传云可以试试unstructured、marker、minerU这类开源方案。实测下来marker对PDF版面识别的效果不错minerU在复杂表格和公式场景更稳。Dify这类框架自带文档拆解能力适合快速验证但自定义能力有限正式项目我一般会先用独立拆解工具处理完再把干净文本喂进知识库而不是完全依赖框架默认流水线。2. 第二处分水岭召回策略只靠向量搜索一开口就是外行2.1 为什么必须做多路召回很多RAG项目上线后效果差第一个背锅的往往是Embedding模型。但仔细看日志问题根本不出在“语义相似度算不准”而是“精确词命中”在向量检索里被严重弱化了。问“怎么退差价”如果你的知识库里恰好有一句“用户可申请差额退款”向量检索可能把它召回来但如果同时有一篇价值极高的、包含完整退差价流程的FAQ因为切分后关键词分散、主题不集中向量检索的得分反而可能不如那句泛泛的描述。这种场景下BM25这类稀疏检索反而能靠词频和精确匹配把最该出现的文档捞出来。我做过一次很直观的实验同一批客服知识库只用向量检索top5命中率大概62%加入BM25再做RRF融合top5命中率到了81%。注意我没有换Embedding模型没有改切分仅仅是把召回从单路变成多路效果差距立刻拉开。这就是多路召回的价值它解决的不是“哪个模型更好”而是“不同检索信号的互补问题”。向量检索擅长语义相似、近义表达、模糊描述稀疏检索擅长精确名词、型号、代码、操作步骤。真实的业务查询往往两种信号同时存在你用一个向量模型去扛所有情况本质上是在逼它做它不擅长的事。2.2 RRF融合落地多路召回做好之后下一个问题是怎么把不同路的结果合并排序。别一上来就想调权重那是个无底洞。更稳妥的方式是用RRFReciprocal Rank Fusion公式简单结果稳定[ score(d) \sum \frac{1}{k rank(d)} ]k通常取60这个值是业界反复验证过的经验常数。每路检索只关心自己的排名不需要归一化不同模型之间的得分尺度实现也非常简单def rrf_fuse(rankings: list[list[str]], k: int 60) - dict[str, float]: scores {} for rank_list in rankings: for rank, doc_id in enumerate(rank_list): scores[doc_id] scores.get(doc_id, 0.0) 1.0 / (k rank 1) return sorted(scores.items(), keylambda x: x[1], reverseTrue)我一般会用两个召回路一条是ES的BM25一条是Milvus或Qdrant里的向量检索两边各取top20再做RRF融合取top10给到重排层。这里有个细节两条路的候选集不要完全一样比如BM25针对“精确型号操作动词”优化向量检索针对“口语化描述语义近义”优化这样融合后的互补性才强。如果两边召回的内容高度重叠融合的意义就不大。2.3 召回问题排查手册召回效果差的时候先别急着换模型按下面几个方向排查第一查询词是否太短单独一个“退款”去检索任何系统都很难给出精准结果这时候要结合查询理解去改写第二切分是否有断句问题检索命中的文本块是否本身就是语义残缺的第三Embedding模型与文档语言的匹配度中文场景下一个在英文语料上预训练为主的模型对中文长尾表达往往不敏感第四候选集是不是太小top5和top20在召回阶段差异巨大先放宽候选把过滤动作交给重排。排查时有个笨办法但非常有效准备50条真实用户问题人工标注每条问题“期望命中的文档ID”每次调整后全量跑一遍看“top5命中率”和“top10命中率”。别嫌土我就是靠这个基准集区分出很多次“到底是切分问题还是模型问题”。3. 第三处分水岭重排召回是粗筛重排才是精挑细选3.1 为什么检索完不能直接丢给大模型召回阶段追求的是“别漏”所以候选集往往很大、很杂。如果把这堆东西直接拼进提示词丢给大模型你会发现它经常被不相关信息带偏甚至会从噪声块里“强行总结”。原因很简单大模型并不会自动给每个检索块标权重它默认所有上下文都值得关注于是与问题无关的块同时挤占了注意力和生成空间。解决方式就是召回和重排分离。召回阶段用一个高效的bi-encoder模型快速筛出候选重排阶段用cross-encoder模型对查询和每个候选逐对打分选出真正相关的内容。bi-encoder把查询和文档分别编码再算相似度速度快适合海量候选cross-encoder直接把查询和文档拼在一起过一遍模型能捕捉更细的交互信息精度高但慢。用生活例子类比就是先海投简历广撒网HR花十几秒筛掉明显不合适的最后再由业务负责人一对一细聊做最终判断。整个思路就是“先保住召回率再精修精确率”。3.2 重排模型选型与接入重排模型我比较常用的是BAAI的bge-reranker系列特别是bge-reranker-v2-m3多语言支持好效果稳定。如果你不想折腾开源模型也可以接商业API比如Cohere的Rerank但要注意数据隐私和成本。本地化部署的场景我优先推荐bge-reranker-v2-m3体积和显存占用都在可接受范围。接入方式很简单把召回阶段取到的top20候选配合原始查询批量计算交叉编码得分再按得分重排取top5。调用的伪代码大概是这个样子from FlagEmbedding import FlagReranker reranker FlagReranker(BAAI/bge-reranker-v2-m3, use_fp16True) pairs [(query, doc) for doc in candidates] scores reranker.compute_score(pairs, normalizeTrue) reranked sorted(zip(candidates, scores), keylambda x: x[1], reverseTrue) top5 [doc for doc, _ in reranked[:5]]一个比较典型的实测结果召回阶段top20里真正有用的可能只有五六个直接拼给大模型命中率在六成左右经重排后取top5命中率可以到八成以上。这个提升不是玄学而是因为过滤掉了绝大多数低相关度的噪声块给大模型留出了更干净的上下文。3.3 重排之后还可以叠加业务规则重排模型只看“语义相关性”但生产系统里“该不该给用户看”还取决于很多业务约束。比如新闻类内容用户想了解最新政策那就应该给时间衰减因子加权越新的内容排名越靠前代码问题官方文档的权威性远高于个人博客涉及权限的文档即便语义再相关无权限用户也不能看到。这些规则可以放在重排之后做一次业务层的再排序也可以做成一个过滤层。我在一个企业项目里就踩过坑用户问2024版退款政策系统却因为向量相似度把2023版的老政策排在第一位新旧混着答结果被用户投诉。加一个“年份必须匹配”的规则过滤后问题直接消失。所以重排不是终点业务规则才是生产系统的常态。也别把规则搞得太复杂一两层关键的、可解释的规则足以解决大多数问题。4. 第四处分水岭索引与元数据没有信息架构的RAG就是在垃圾场寻宝4.1 元数据精确过滤、权限控制、溯源防混淆如果知识库里只有“文本块向量”两样东西那它本质上就是一个没有目录的图书馆。你问它什么它全库检索看似智能实际上是把所有文档混在一起碰运气。元数据就是给每个文本块贴上结构化的标签让检索从“全库捞”变成“精准定位”。我常用的元数据字段包括来源、作者、发布时间、文档类型、标题层级、所属业务线、权限级别、原文页码。元数据的作用不只是过滤。有了它RAG的回答才能做到真正的溯源。用户看到“退款政策”你得能告诉他这个结论出自哪份文档、哪个章节、发布时间是什么。没有元数据一切都是黑箱出错了也完全没法复核。权限控制也一样文档按密级建好索引后检索时直接拼上“权限级别用户级别”的过滤条件比在提示词里让模型“注意保密”靠谱一万倍。我强烈建议在切分和向量化的阶段就把元数据同步写入向量库。Dify这类框架虽然也支持元数据过滤但字段的灵活度和自定义能力有限复杂业务场景我通常会在自己的索引层里做。4.2 多知识库布局与wiki和RAG的结合一个知识库里什么文档都塞叫“踩踏式建设”看起来方便实则检索时互相干扰。更合理的做法是按业务域、权限域或文档类型拆分知识库。比如客服部门建一个“售前咨询库”一个“售后政策库”再按渠道拆分线上和线下。查询时先做意图路由判断用户问题属于哪个域再定向检索。如果拿不准可以并行检索多个库再做融合但这要求后端有统一的排序策略别把NRR写死在单库维度。这里顺带聊一下wiki和RAG的关系。很多人纠结“有了RAG是不是就不需要wiki了”我理解恰恰相反。wiki是人工维护的权威真源结构清晰、长期稳定RAG擅长在大量非结构化文档中捞回碎片信息。成熟的系统通常是这样配合的wiki作为高可信数据源接入RAG回答优先引用wiki条目RAG召回触发了文档变更再反向提醒维护者更新wiki。靠RAG去替代一套精心维护的知识体系那是把上下文检索当成了知识管理本身容易翻车。4.3 RAG知识库、KG知识库和结构知识库怎么选很多人一听说“知识图谱”“Graph RAG”就上头想把自己的RAG升级成“更高级”的形态。我的观点是先分清场景再选型别为炫技买单。文本RAG知识库适合非结构化的、以语义检索为主的问答结构知识库也就是关系型数据库里那些有明确字段和约束的记录适合“查某个订单状态”“看最近一笔交易”这种精确查询KG知识库适合解决多跳、聚合和关系推理问题比如“A部门下哪个岗位招的人最多”“哪些客户同时购买了B和C产品”。类型最佳场景主要成本文本RAG知识库海量非结构化文档、开放问答切分和召回质量结构知识库精确查询、报表统计需要结构化抽取与字段对齐KG知识库多跳推理、复杂关系分析构建图谱成本极高笔者的建议是用一个渐进路径先搭文本RAG把召回、重排、评估调顺如果发现大量查询需要多跳推理再考虑引入知识图谱。直接一上来就建图谱的项目大概率会卡在实体抽取和关系维护上半年后变成一个没人维护的骨架。Graph RAG是好东西但不是所有问题都值得用图去解。5. 第五处分水岭查询理解用户问的是A你检索的却是C5.1 为什么必须做Query改写用户不会像搜索引擎专家一样组织查询词。他们习惯说口语、用代词、夹带无意义语气词还经常在一个问题里藏着两个意图。你用这种“脏问题”直接去检索召回结果自然飘。比如“这个怎么退”“那个什么时候到账”模型根本不知道“这个”“那个”指代什么再比如“你们和另一家比怎么样”问题里没有具体对比维度检索出来的当然是一堆不相关内容。Query改写的核心任务就是把这些“自然语言口水话”翻译成“适合检索的查询语言”。说白了检索的不是用户说的话而是用户真正想问的东西。这个动作在上下文检索里做得不够好后面所有环节都会跟着错。很多团队把精力浪费在调提示词上其实问题出在查询理解环节用户意图根本没被正确解码。5.2 改写与路由的可工程化方案Query改写不一定要上多复杂的模型规则加LLM的组合在很多场景下已经够用。规则层负责处理那些高频的、明确的模式比如把“这个”替换成对话历史里的上一轮主题词把“退换货规则”同义扩展成“退货流程”“退款标准”LLM层则用来处理更复杂的、需要上下文推理的改写任务。这里给一份我常用的点击就能改的提示词模板你是一个查询改写助手。给定用户的原始问题与历史对话改写成一个独立的、适合检索的单轮查询。 要求 1. 不要丢失业务专有名词如产品名、型号、部门名 2. 将口语缩写补全例如“退差”改写成“退还差价” 3. 还原代词例如“这个”改写为具体指代的事物 4. 保持原问题的所有限制条件如时间、地区、版本 5. 只输出改写后的查询文本不要解释。除了改写意图路由也非常关键。不是所有问题都该走全文检索。有些问题是精确查询比如“订单号12345的状态”应当直连结构数据有些问题适合做摘要比如“这个季度的整体销售额是多少”应当走分析工具有些问题需要多跳推理再考虑进知识图谱。把RAG当作唯一答案来源是架构上的偷懒。更进阶的玩法是把它做成RAG智能体让Agent自己决定“什么时候去查文档、查哪个库、要不要调用外部工具”查询理解就从一个固定模块变成了系统决策的一部分。5.3 兜底策略宁可不说不要胡说Query改写做得再好总有检索不到答案的时候。很多系统在“没有答案”的情况下依然强制大模型生成一段“看似合理”的回复这就是幻觉的最大来源。我见过最离谱的输出用户问的是一场活动是否延期知识库里根本没有这场活动的任何信息模型却编造了一个日期。行业里有一句话叫“检索不到的答案别硬答”但真正能落地这个原则的系统很少。我的做法是给检索结果设置一个置信度阈值低于阈值时直接返回“没有找到相关信息”并引导用户换一种问法而不是硬给一段编造的回复。这需要在召回和重排阶段把得分分布统计出来找到合理的临界点。哪怕阈值设得保守一点多几次“不知道”用户也不会太反感但一次“乱说”信任就崩塌了。兜底策略在RAG里的重要性我认为怎么强调都不过分。6. 第六处分水岭评估与闭环你连好坏都不知道调优就是玄学6.1 先建一套Golden Set做RAG优化最怕的就是“凭感觉”。这里调大一下温度那里换个模型效果好坏全靠肉眼和几条测试问题。这不是调优是碰运气。我的经验是动手做任何优化之前必须先建一套Golden Set评测集。Golden Set的构成是从真实用户问题里抽样而不是自己编几条脑筋急转弯。数量不用太多50到200条就足够覆盖主要意图。每条问题要标注三个信息期望召回的文档ID、答案必须包含的关键点、以及允许范围内的参考答案。标注过程很枯燥但它是整个系统后续所有优化的标尺。没有这个标尺任何一次“效果变好了”都是主观幻觉。6.2 RAGAS指标与简易评测评估维度上我推荐直接套RAGAS那套体系。它把RAG评估拆成几个可量化的指标faithfulness衡量“生成的答案是否忠实于检索到的上下文”answer_relevancy衡量“答案是否贴合用户问题”context_precision衡量“检索到的上下文有多精”context_recall衡量“检索到的上下文是否覆盖了关键信息”。这四个指标配合起来能让你快速定位问题出在“检索”还是“生成”。简易评估可以直接用RAGAS库跑配置好了之后大概是这样from ragas import evaluate from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall result evaluate( dataset, metrics[faithfulness, answer_relevancy, context_precision, context_recall], )跑完各指标会出一个分数。低于0.7的指标需要专项排查context_recall低问题出在召回或切分context_precision低问题大概率在重排faithfulness低要检查上下文是否被错误信息污染answer_relevancy低先怀疑查询理解和改写环节。我没见过哪个团队是拿这些指标做完全自动化的更常见的做法是每周跑一次评测集人工复核差异项再决定下一轮优化的重点。6.3 从Demo到生产的优化路径最后给你一条我实际走下来的优化路径参考它不花哨但每一步都有明确目标先搭一个最简版本文档切分用递归字符切分检索用向量加BM25无重排在这里建立基线把Golden Set跑出分数。别急着优化先把基线跑稳。基线能跑之后第一步加召回融合和重排这一步通常能把context_precision和top5命中率拉高一大截。第二步做元数据过滤和权限控制把“答案来源混乱”的问题清掉。第三步上查询改写和意图路由解决口语化和多意图问题。第四步接入置信度兜底与“无答案”策略先保证不胡说。第五步搭评估闭环和线上反馈采集让系统越来越准。整套流程做下来你会明显感觉到RAG真正难的地方不是好不好搭而是敢不敢用一个评测体系去审视自己搭出来的东西。我个人的体会是别把时间花在追逐更贵的大模型和新框架上RAG项目最大的杠杆永远是把“检索质量”和“评估体系”这两件基本功做扎实。毕竟用户不会因为你用了最前沿的模型而原谅一次不相关的回答但他们会因为每次回答都能溯源、能解释而真正信任你的系统。