做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或PyMuPDF(fitz),扫描件用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默认的RecursiveCharacterTextSplitter,chunk_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_size(300/500/800/1200)和overlap(50/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分数做加权融合。像langchain4j+Milvus这种组合在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是一个系统工程,数据决定上限,检索决定下限,生成只是最后一棒。真正值得投入精力的地方,永远是先把数据和检索打磨扎实。
如果你手里正有一个“检索效果差”的项目,我的建议是不要急着大改代码,先按这五层逐项做一次排查,把每一层的输出都可视化出来看一遍。你可能会发现,问题和你最初猜的完全不一样——而看清楚问题,通常就已经解决了一半。