资讯动态

RAG效果差?八成问题出在数据和检索层:五层排查指南

发布时间:2026/9/14 5:39:41 来源:尧图企业网站定制
做RAG项目的朋友应该都有过这种体验明明模型选的是大厂旗舰款prompt调了三五轮知识库也传了几百个文档结果一问到细节问题模型要么一本正经地“编答案”要么回复一句“根据我找到的资料暂时无法回答”。这种时候大多数人第一反应是换更强的模型、继续调prompt但在我自己完整做过几个生产级RAG项目之后发现一个很扎心的规律检索效果差十次里有八次问题出在数据和检索层而不是生成层。项目标题里那句话说得非常准——“八成问题在数据和检索层”。这背后的逻辑其实很简单RAG系统的上限由召回决定下限由生成模型兜底。如果数据没有进去、切得不对、向量化之后语义漂了、召回时该搜的没搜出来那再强的LLM也只能基于错误或不完整的上下文强行作答。这篇文章我不会讲空泛的概念而是把我在实际项目中反复用的一套五层排查方法完整拆开从数据接入、文本切分、向量化、检索召回到重排生成每一层到底看什么、怎么查、怎么修全部写成可落地的操作指南。1. RAG效果差先别急着换模型建立五层排查坐标系很多团队在RAG效果不理想时上来就陷入两个误区一是疯狂换大模型从7B换到13B再换到70B结果发现改观有限二是不停改prompt把提示词写成了小作文召回的内容不对prompt写得再花哨也是白搭。我自己踩过这个坑之后总结了一套固定的排查顺序类似看病时的“分诊”流程——先确定病灶在哪一层再对症下药。1.1 为什么“数据层”和“检索层”是重灾区RAG的完整链路可以拆成五个层次数据接入层、文本切分层、向量化层、检索召回层、重排生成层。前四层都属于“数据和检索”的范畴只有最后一层才轮到LLM发挥作用。如果前四层里有任何一层出了偏差最后LLM拿到的上下文就是错的或残缺的它再怎么聪明也无济于事。举个例子我把一份几十页的PDF设备手册塞进知识库PDF里大部分是扫描图片文字层根本不存在。如果解析环节没做OCR那这部分内容在知识库里就是“隐形”的——用户问设备报警代码是什么意思系统检索不到只能硬编。这一类问题换再大的模型也解决不了。再比如文本切分如果一篇技术文档被切成了固定512个字符的块正好把一个表格从中间切断或者把一个操作步骤的上下文关系打断那后续检索到的内容天然就是残缺的。这两类问题都发生在检索之前的“数据准备”阶段但它们对最终效果的影响是决定性的。1.2 建立基线先跑通最小闭环再逐层优化我建议任何RAG项目在动手调优之前先做一件事用一份高质量的、格式规整的测试文档跑通整个流程建立一条基线。这条基线不需要追求极致效果只需要保证“数据正确、切分合理、检索能召回、回答可用”。有了基线之后五层排查才有意义。否则你今天调了embedding模型明天改了切分参数后天又换了rerank策略所有变量同时变化你根本不知道哪个改动起了作用。我在项目中会固定一个评测集大概20到30个问题覆盖不同类型事实型、对比型、流程型、否定型每次只改一层跑完评测集看指标变化。这个习惯帮我避开了很多“盲调”的坑。2. 第一层数据接入与解析知识库地基全在这一步很多人以为数据接入就是把Word和PDF往知识库一传就完事等发现检索不到才回头查却发现源头就已经烂了。数据接入层的核心任务有三个把内容完整读出来、把噪声清干净、把结构信息留在文档里。这一层做不好后面几层再努力也是白费。2.1 文档解析扫描件、表格和双栏排版是三大坑先说文档解析。不同格式的文档有着完全不同的解析难度我按“坑的程度”排个序Word/HTML/JSON结构相对完整解析工具成熟主要问题是提取时保留语义结构。PDF文字版能直接提取文字但要留意页眉页脚、表格结构、双栏排版是否被正确还原。PDF扫描版/图片必须OCR且中英文混排、公式、表格的OCR准确率需要逐项验证。网页/微信公众号文章需要清洗大量广告、导航、推荐模块只保留正文。我比较常用的解析工具组合是PDF文字版用pdfplumber或PyMuPDFfitz扫描件用PaddleOCR或Tesseract。PaddleOCR对中文表格和公式的支持比Tesseract好不少但部署重一些如果只是简单扫描文字Tesseract也够用。表格是个特殊存在。直接把表格转成纯文本喂给RAG检索时极易丢失行列对应关系。处理方式有两种一是用表格解析工具把结构转成Markdown或HTML格式保留在切分块里二是对复杂表格做“表格摘要”用一句话描述这张表讲了什么检索时优先召回摘要。在实际项目中我倾向于两者结合表格原文保留同时额外生成一段规范化描述。2.2 数据清洗页眉页脚、水印和乱码必须干掉清洗阶段常见的噪声源包括页眉页脚、页码、水印、章前页、目录、参考文献、超链接、脚本标签、网页导航栏。这些噪声一旦进入切分块检索时会产生大量“看起来相关但实际没用”的结果。以PDF的页眉页脚为例很多设备手册每页底部都印着“第X页共Y页”和公司名称。如果不做清洗这些词会出现在每一个切分块里导致按关键词检索时这些块全部命中排序时干扰极大。清洗策略不复杂解析后做一轮正则匹配把重复出现的页眉页脚模式去掉网页内容用trafilatura或Readability做正文提取比直接抓全部文本干净太多。这里有一个容易忽略的点清洗规则要区分文档类型做配置不能一套规则走天下。技术手册需要保留章节编号如“3.2.1”合同需要保留条款序号问答FAQ需要保留每一问的完整结构。清洗的本质是“去掉该去掉的保留该保留的”力度过了反而伤害语义。2.3 元数据给每个切分块打上“身份证”数据接入阶段还有一个容易被忽略的高价值动作元数据采集。元数据指的是文档的来源、作者、日期、章节路径、文档类型、权限等级等信息。这些信息在后续检索、过滤、引文溯源时非常有用。比如在做企业知识库时某些文档仅限特定部门阅读那么在检索阶段就要通过元数据做权限过滤否则任何用户都能从知识库里拿到敏感内容。再比如检索结果展示了“来源章节”用户能直接判断这段内容出自哪里可信度大大提升。实现方式很直接在切分时把元数据一并写入向量库的字段中检索时作为filter条件使用。这类设计越早做越好。我见过不少项目向量库里光秃秃地存了embedding和原文本后面要加权限过滤、要加来源展示才发现还得回源重新建库代价非常高。3. 第二层文本切分策略决定了检索命中率的底层逻辑文本切分是整个RAG链路里“参数简单但影响巨大”的一环。很多人直接用LangChain默认的RecursiveCharacterTextSplitterchunk_size设个固定值就再也不管了。但实际上切分策略必须跟文档类型和检索目标强绑定没有任何一种参数能通吃所有场景。3.1 固定长度切分 vs 语义切分 vs 结构切分目前主流的切分方式大概分三种固定长度切分按字符数或token数直接切优点是简单、性能好缺点是容易切断语义完整的段落表格和代码块常被拦腰截断。递归字符切分通过分级分隔符先按段落再按句子逐层切尽量保持小块完整LangChain和LlamaIndex默认都走这个思路。结构感知切分根据文档本身的层级标题Markdown的#、PDF的书签、HTML的heading标签把内容切成“按章节组织”的块。这种方式对长文档效果最好但需要解析阶段保留结构信息。我的体会是80%的文档用“结构感知递归兜底”的组合策略就够用。先尝试按标题结构切如果标题信息缺失或层级不规范再退回到递归字符切分。切出来的块如果超过上限比如1000字符再用递归方式继续切。3.2 chunk size、overlap到底怎么调别再用默认值chunk size和overlap这两个参数直接影响检索的“颗粒度”。chunk size太大比如一个块2000个字符一个问题召回的上下文可能混入大量无关内容chunk size太小比如200个字符语义表达不完整embedding效果也会大打折扣。我踩坑之后总结出来的经验值是中文场景下chunk_size取400到800字符overlap取50到150字符算是一个比较稳妥的起点。为什么是400到800因为这个长度大约能覆盖2到4个自然段落既要表达相对完整的语义又不至于太长导致向量语义“被稀释”。overlap的作用是缓解切分边界切断句子的影响。想象一段话横跨两个块第一个块只有半句话第二个块有后半句——没有overlap的话检索时召回的块可能只有半截内容语义残缺。加上overlap后相邻块会有部分重复内容确保句子完整性。调这两个参数时不要只靠感觉。我建议做一轮小规模网格搜索选10到20个代表性问题分别用不同chunk_size300/500/800/1200和overlap50/100/150组合跑检索看召回命中率。实测下来这两个参数对召回率的影响经常比换一个embedding模型还要大。3.3 面向不同文档类型的切分方案选型在实际项目里不能一本手册打天下不同文档类型应该用不同切分方式FAQ问答对按“一问一答”为一个块不要切碎。如果一条QA太长可以把“问题”放块开头后面跟“答案”这样检索时问题和答案天然绑定。技术手册/说明书按章节标题切分保持章节完整性这样用户问“第三章的安装步骤”时能精确命中。合同/法律条文按条款编号切分保留条款号便于引用和溯源。论文/研究报告按“摘要、引言、每个大节、结论”的结构切参考文献单独成块避免干扰正文检索。表格密集型文档表格单独提取为一个块或做摘要不要跟正文混在一起。4. 第三层向量化与Embedding选型语义匹配的基石前面两层解决“数据进得对不对”到了向量化层核心问题变成了“机器能不能理解这些文本的语义”。不少项目在这层犯的错误是用了不合适的embedding模型或者对用户的查询不做任何处理导致机器的“理解”和人的意图完全不在一个频道上。4.1 通用Embedding和领域Embedding怎么选市面上有很多embedding模型泛用型如text-embedding-3-small、bge-large-zh、m3e等。通用模型的好处是开箱即用对常见语义理解不错但放到垂直领域比如医疗、法律、工业设备维护通用模型经常抓不住领域术语之间的微妙关系。这里有个实操建议先拿一批你所在领域的典型问题用通用模型跑一轮检索看失败case集中在哪。如果失败原因多是“同义词、缩写、术语别名”导致的匹配不上说明embedding模型在领域语义上不够敏感这时候可以尝试在领域数据上做微调或者直接用领域微调版模型。我实测过中文场景下bge-large-zh-v1.5在大多数场景里表现比较稳定尤其对中文长文本的语义理解优于第一代模型。但如果项目涉及大量英文、代码、数学公式就需要另外测试专门的模型。不要盲信榜单分数第一个维度永远是“在你自己的评测集上召回命中率提升多少”。4.2 查询改写用户的问题太口语化检索结果自然跑偏很多RAG项目有一个隐蔽的检索瓶颈用户问法和知识库文档的表达方式差异太大。用户在搜索框输“怎么退钱”而知识库里的原文是“退款政策与流程”——两个文本的字面重叠度很低纯向量检索很容易漏掉。解决这类问题的方法叫“查询改写”Query Rewriting通常有两种路径基于规则维护一个同义词词典查询进来之后做扩展匹配。比如“退钱”扩展为“退款、退费、退款政策”。这个方案简单可控但维护成本高只适合词表有限的场景。基于LLM改写在进入检索之前先用一个轻量级prompt让LLM把用户query改写为知识库风格的书面表达甚至生成多个候选查询多查询扩展。比如“怎么退钱”被改写成“退款流程是什么”“用户退款申请步骤”等分别检索后再合并结果。从我的实践来看多查询扩展Multi-Query是提升召回率性价比最高的一招。实现也不复杂把用户query丢给LLM让它生成3到5个同义改写每个改写都走一遍检索最后对结果去重合并、再排序。代价是多花一些LLM调用和检索时间但召回稳定性的提升非常明显。4.3 向量化会失效的场景数字、代码和否定语义向量检索有一个特点对语义层面的“相似”敏感但对字面层面的“精确”不够敏感。比如用户查询“容量为500ml的杯子”文档里写的是“500mL”算法层面分词和向量化可能把它们视为不同实体导致召回丢失。再比如代码片段、订单号、型号编号这类精确匹配场景纯向量检索经常拉胯。处理这种问题的通用做法是混合检索在下一章展开。但向量化层本身也要意识到不要把向量检索当成“唯一的检索方式”也不要指望embedding模型能解决一切匹配问题。精确匹配和关键词匹配在某些场景下比语义匹配更可靠合理的架构应该是两者并存、互为兜底。5. 第四层检索召回层决定最终答案质量的分水岭如果数据接入、切分、向量化都做得差不多了检索效果还是不行那问题大概率出在召回策略上。这一层是最考验工程经验的部分也是热门话题“多路召回”“混合检索”“RAG框架参数配置”集中出现的环节。5.1 纯向量检索的瓶颈为什么“语义相似”不等于“正确答案”纯向量检索的直觉是把用户query和文档块都转化成向量然后按向量距离找最相似的块。听起来很自然但它有一个天生的缺陷embedding模型捕捉的是“整体语义相似度”而不是“任务相关性”。比如用户问“A设备和B设备哪个更便宜”文档里有一段讲A设备的价格另一段讲B设备的价格两段单独跟query算相似度时可能都只匹配上了一半的信息。还有一种情况是query里包含多个限定条件向量检索容易顾此失彼。这时候单独依赖向量检索会非常脆弱。我在实际项目中几乎从不只用向量检索而是采用“向量检索关键词检索BM25多路召回”的策略。BM25擅长精确匹配词项向量检索擅长语义泛化两者互补性很强。5.2 多路召回与混合检索的落地配置多路召回说得直白一点不要把所有鸡蛋放在一个篮子里。用多种不同的检索方式分别召回一批候选文档最后合并、去重、统一打分。常见组合有向量召回 BM25关键词召回最经典组合语义和字面两条腿走路。粗排召回 精排重排第一轮用向量BM25各取50条合并后用rerank模型精排取top5。多路向量召回如果预算和性能允许可以同时用通用embedding模型和领域embedding模型各跑一路甚至对query做改写后分别召回。知识图谱辅助召回涉及规范化实体如“武汉大学”“发明专利”时可以用GraphRAG或Ontology RAG先做实体匹配和关系扩展再把相关实体对应的文档块加入候选集。工程上做混合检索很多向量数据库原生支持。以Milvus为例可以同时建向量索引和标量索引检索时把向量相似度和BM25分数做加权融合。像langchain4jMilvus这种组合在Java技术栈里也比较常见配置好两个检索源最后用Reciprocal Rank Fusion或加权求和合并排序即可。我在一个企业知识库项目里用过多路召回后效果提升非常明显原来是纯向量召回用户问一个包含两个产品型号对比的问题时经常只召回其中一个型号的文档加上BM25关键词召回后两个型号的相关文档都能进候选集最终答案的完整度大幅改善。5.3 检索参数调优topK、相似度阈值、索引参数逐一说明检索层的参数如果设置不当结果也会非常不稳定。以下几个参数是我每次排查都会检查的重点topK传给LLM的文档块数量。并不是越多越好topK太小时容易漏掉关键信息topK太大时会把大量无关内容塞进上下文干扰模型判断。我的经验是先粗召回50条精排后取top5左右传给LLM。如果问答需要综合多篇文档的信息可以适度调到8到10但超过15条后效果通常不升反降。相似度阈值很多向量数据库允许设定最小相似度分数低于阈值的文档不召回。这个阈值需要基于实际分数分布来定。我遇到过某项目里所有文档相似度都在0.7以上但很多并不相关阈值设得太低导致大量噪声进入候选集把阈值一步步上调精确率立刻改善。HNSW索引参数如果用的是HNSW索引M每个节点的最大连接数和efConstruction建索引时的搜索范围影响索引质量和查询速度。efSearch则控制查询时的搜索广度值越大召回越准但越慢。不是每个项目都值得深调这些参数但如果你发现检索延迟高、或者召回结果不稳定先看看索引参数是否合理。另外还有一点容易被忽视别把RAG的检索参数做成全局一套。知识库里如果既有短文档又有长文档短期可以靠切分兜底长期建议按文档类型挂不同的检索配置比如FAQ库用BM25高阈值技术手册库用向量低阈值否则“顾此失彼”的问题会反复出现。5.4 图文检索和跨模态检索的趋势最近“图文检索”这个概念很火本质是把图片、表格、图表也纳入可检索范围。纯文本RAG面对“用户想看某型号设备的分解图”这类需求时完全没辙因为知识库里只有文字没有图片信息。目前的落地方案有几条路径一是对图片做OCR或Caption生成把图片内容转成文本后喂进向量库二是用多模态embedding模型把图片和文本映射到同一个向量空间实现真正意义上的图文混合检索。前者实现简单、成本低适合大多数业务场景后者技术门槛高一些但检索质量更自然。如果你手头有大量图表类知识资产建议尽早把图文检索纳入改造计划。6. 第五层重排与生成给最终答案把好最后一道关前四层做得好只能说“候选内容靠谱”但能不能把靠谱的内容转化成准确的答案还要看重排和生成这最后一层。这层有两个核心任务把最相关的文档排到最前面以及避免LLM被不完整或冲突的上下文带偏。6.1 为什么需要Rerank粗排“差不多”精排“差很多”向量检索和BM25召回的排序本质上都是“粗略相关度排序”。它们的目标是“把这个文档召回来”而不是“这个文档是这批候选里最该被采用的那份”。所以召回后通常需要一个精排模型对候选文档打分重排。常用方案是用专门的rerank模型比如bge-reranker-v2-m3、Cohere Rerank等。这些模型会把query和候选文档逐对送入一个交叉编码器做深度匹配打分比双塔式的embedding准确率高不少。代价是计算量更大所以用法通常是先用便宜的粗排方式召回50条再用rerank模型精排出top5再交给LLM。在Dify这类低代码RAG平台里这个流程已经被封装成“召回→重排序”两个节点你只需要配置一个rerank模型API。但如果你自己搭链路建议务必留出这一环。我自己测试过一个问答集加入rerank后答案的引用准确率能从70%左右拉升到85%以上代价只是额外几十毫秒的延迟非常划算。6.2 结果过滤与去重避免重复内容淹没上下文只要做过多路召回的人都会遇到一个问题不同路由召回的文档可能高度重复有的甚至一模一样。如果直接把所有候选结果塞进上下文LLM会被重复内容干扰还白白浪费上下文窗口。因此合并结果后要做三步后处理按文档块ID去重向量召回和BM25召回可能命中同一个块去重后只保留一次。按相似度或rerank分数过滤设定一个最低分低于这个分的候选直接丢弃。按来源文档多样性排序如果top5里4块都来自同一篇文档而另一篇相关文档一块都没进建议在保证相关性的前提下让结果来源尽量分散避免单篇文档信息偏差过度影响答案。6.3 上下文注入与提示词组织检索结果如何“喂”给LLM最后一步是把选中的文档块组织成LLM能有效利用的上下文。这一步的细节不少但核心原则是让LLM明确知道哪些是检索到的资料哪些需要基于资料作答哪些情况下必须承认不知道。我在实际项目中常用的prompt结构大致是系统指令你是一个严谨的助手只能基于提供的参考资料回答如果资料中没有相关内容请明确说明“资料中未找到相关信息”不要编造。资料区每条资料前加上编号和来源比如[1] 来源XX手册第3章让LLM在回答时可以引用。用户问题最后放用户原始query。把来源和编号带上很重要这样LLM在回答时可以引用“根据资料[2]”用户能快速回查原文。如果前后两条资料存在矛盾指令里也要明确要求LLM指出矛盾、而不是强行选择一个。6.4 建立端到端的评测闭环到这里五层排查的每一层都讲完了。但我必须强调一个工程习惯每一层优化都要有对应的评测手段否则就是在盲人摸象。评测不一定要做得很重一个简单的脚本维护一份测试集就够了——包含问题、预期答案类型、涉及的知识库文档编号、期望召回文档编号等。每次改动代码或参数跑一遍测试集对比召回率和答案正确率。我建议至少记录三个指标召回命中率正确答案对应的文档是否出现在topK召回结果里。答案准确率LLM根据召回内容生成的答案是否与预期一致。可溯源率答案能否正确引用知识库文档不出现无中生有。只有建立了这套评测闭环五层排查才不是一次性的“消防队”而是一个可持续迭代的优化体系。7. 常见问题排查速查表与实战复盘前面五层讲了很多理论和方法这一章把最常见的问题和排查路径整理成速查表方便你定位病灶。下面几个场景是我在真实项目里反复遇到的可以直接对照使用。7.1 高频问题与排查路径速查问题一检索结果完全无关可能原因文档解析失败PDF白写了、扫描件没OCR、向量库索引建错字段没对上、相似度阈值设得太高或太低、query改写过度偏离原意。 排查路径先拿一段原文直接到向量库里搜看能否召回自己再到实际query逐层对比。问题二相关文档能召回但答案还是不对可能原因切分把关键上下文切断了topK太小只召回了部分信息多个相关文档互相矛盾LLM没被要求指出冲突回答引用的核心信息没在上下文里。 排查路径把召回结果打印出来人工读一遍看关键信息是否完整、是否被其他信息淹没。问题三不同问题效果差异极大可能原因知识库文档质量参差不齐部分文档没有做清洗或切分适配query类型差异大事实型、对比型、流程型一套检索配置解决不了所有类型。 排查路径把失败问题按类型分组看某一类问题是否系统性失败再针对该类型调整检索策略或文档处理逻辑。问题四检索速度慢可能原因向量索引参数不合适efSearch太小则慢HNSW的M太大则慢候选集太大、每层都在全量计算没有做标量字段预过滤。 排查路径看向量数据库的查询日志定位耗时来自索引搜索还是后处理排序。问题五知识库数据更新后检索结果不变可能原因新增文档没有重新向量化后半途失败向量库索引没有增量更新缓存层失效逻辑有问题。 排查路径验证新文档是否入库确认embedding和索引更新状态检查缓存策略。7.2 实际项目案例从“答非所问”到“精准引用”最后分享一个我亲身经历的案例。某个企业内部制度问答系统知识库里有几百份Word和PDF文档包含员工手册、报销制度、差旅标准等。项目上线第一周用户反馈大量问题“答非所问”比如问“出差住宿报销上限是多少”系统答非所问地开始介绍出差审批流程。我在排查时先做了召回结果检查发现关键问题有三个第一源文档里“住宿标准”相关内容分布在多个章节被切分模块切成了不同块检索时只命中了开头“出差审批流程”相关的块第二部分PDF是扫描版解析阶段没有做OCR导致“报销上限”这类文字根本没有进入知识库第三用户query是会话式表达直接送去向量化后与文档书面用语匹配度不高。对应的修复措施也很直接第一对制度类文档改为按章节结构感知切分并在切分块里保留章节路径第二补跑OCR流程把所有扫描PDF转成可检索文本第三在检索前增加一个query改写步骤把口语变成书面表达后再走向量BM25多路召回第四加入rerank模型做精排并为答案增加“来源章节”引用。修复后的效果立竿见影同一批测试问题的召回命中率从62%提升到91%用户反馈的“答非所问”问题基本消失答案开始能正确引用“员工手册-第5章-差旅报销标准”。这个案例并不是什么独门秘技整个过程就是把五层排查走了一遍——问题出在数据解析、文本切分、查询理解和检索策略多个层面而不是模型能力不足。写在最后的一个小建议我自己做RAG项目到现在的体会是不要迷信“换个更贵的模型、加更多上下文”这种偷懒解法。RAG是一个系统工程数据决定上限检索决定下限生成只是最后一棒。真正值得投入精力的地方永远是先把数据和检索打磨扎实。如果你手里正有一个“检索效果差”的项目我的建议是不要急着大改代码先按这五层逐项做一次排查把每一层的输出都可视化出来看一遍。你可能会发现问题和你最初猜的完全不一样——而看清楚问题通常就已经解决了一半。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价