☰
RAG不止于流水线:六个最易忽略的分水岭决定知识库效果
2026/10/31 4:25:04 网站建设 项目流程

这几年聊RAG,基本绕不开一个词:烂大街。随便搜一下,教程、框架、demo满天飞,GitHub上接一个langchain,配一个向量库,加载几个PDF,就能跑出一个“智能问答机器人”,然后大家就觉得自己已经会RAG了。说实话,我一度也这么干过:文档一切、向量一存、top-k一取、大模型一答,完事。跑通的那一刻确实很爽,可一旦放到真实业务里,效果立刻原形毕露——要么答非所问,要么一本正经地胡说八道,要么明明知识库里有的东西它就是检索不到。问题出在哪?就出在太多人把“能跑通的流水线”当成了RAG本身。

流水线确实烂大街了,它只是RAG最外面那层壳。真正的分水岭,是切入知识库之前、检索过程中、生成之后这六个关键环节有没有做扎实。这篇文章不打算再从头教你怎么搭一个demo,而是想把这六个最容易被忽略、又最能拉开效果差距的地方逐一拆开讲清楚。每一处我都会结合自己踩过的坑、实测过的方案来讲,尽量给到可以直接上手的思路和判断依据。无论你是刚接触RAG的新手,还是已经在做企业级知识库的工程师,这篇文章应该都能帮你找到下一步优化的方向。

1. 第一处:切分策略,别再用固定长度“切豆腐”

很多教程里默认的切分方式就是按固定token长度切,比如每500个字符一段,overlap设50。这个方案最大的好处是省事、通用,但它也是我见过最坑人的默认配置。因为它完全不理解文档结构,经常把本来完整的一句话、一个表格、一条法律条款从中间切断,导致检索的时候召回到的块内容不完整,生成的时候大模型拿到的上下文是“残废”的。

1.1 结构感知切分与语义块

我自己实际测试过一个场景:一份采购合同,里面有“违约责任”这一节,整个条款是一个完整的段落逻辑,前半段写甲方义务,后半段写违约后果。固定长度切分后,前半段和后半段被分到不同的块里,用户问“甲方违约要赔多少钱”,向量检索召回了后半段,但前半段已经不在上下文里,模型只能根据残缺信息猜,结果自然不靠谱。

正确的做法是结构感知切分:先识别文档的Markdown标题、PDF章节、Word样式、法律条款编号等结构特征,再在结构边界上切分。以Markdown文档为例,可以按标题层级切,把一级标题和二级标题作为块的逻辑边界,保证每个块是一个相对完整的语义单元。我用过langchain的MarkdownHeaderTextSplitter,也自己写过一段简单的逻辑:按“## ”和“### ”先定位,然后在标题区间内再处理过长段落。处理下来的结果,比固定切分在合同类文档上的召回质量提升了不止一个档次。

1.2 中文文档切分的三个细节

中文文档切分比英文更讲究,因为中文没有天然的空格分词,标点使用习惯也不一样。我总结出三个细节,都是实际踩坑踩出来的。第一,切分点尽量选在句号、问号、感叹号这类完整句子的边界,而不是硬在第500个字符处截断;第二,如果句子的确超长,可以在逗号、分号处做二次软切,同时保留上一段末尾的部分内容作为上下文衔接;第三,overlap不是越大越好,我通常建议控制在单块长度的10%~15%,太大会导致同一段内容反复出现,浪费向量库容量,也会影响检索的区分度。

有个更进阶的办法叫“父子切分”。把文档切成长度较大的“父块”和长度较小的“子块”,检索时命中的是子块,但最终发给大模型的是子块所在的父块全文。这样做的好处是:检索粒度细,定位准确;生成粒度大,上下文完整。比如一份操作手册里,“设备启动”和“故障代码表”是同一章节下的内容,用户搜“Err-23是什么”,子块命中故障表那一行,父块把整个表格和前后说明一起交给模型,回答质量就明显好很多。

表格内容在固定切分下基本是重灾区。PDF里转出来的表格,如果按文本流切,很容易把表头和表体拆散,检索时模型看不到表头,根本不知道那串数字是什么意思。我的处理习惯是:先用工具把PDF表格抽成Markdown表格,再把它作为一个独立的块整体入库。对于特别长的表格,再做横向拆分,每一行或每几行都保留表头和对应的列名,这样检索到任意一段,模型都能看懂结构。

2. 第二处:检索,别再迷信“向量搜索万岁”

纯向量检索是另一个被神话到不行的东西。它在“语义相似”的召回上确实很强,但在精确匹配、数字范围、专有名词这些场景下表现非常不稳定。比如用户问“2024年Q3营收”,如果知识库里有一篇财报的语义描述“第三季度收入”,向量检索能召回;但如果是“A8-Max这个型号的保修期”,向量检索可能召回一堆类似型号、类似保修相关的内容,唯独没有A8-Max那条。向量相似度不等于事实正确,这是很多RAG系统翻车的根源。

2.1 混合检索:BM25与向量的双路召回

我现在的做法基本都是混合检索,BM25负责精确匹配,向量负责语义召回,两者并行跑,最后做融合。BM25擅长处理关键词、型号、人名、编号这种“原文里出现了就是出现了”的检索;向量es embedding负责处理“意思相近但字面不同”的检索。把两路结果合并之后,召回质量比单路向量稳定得多。

融合算法最常用的是Reciprocal Rank Fusion(RRF),公式很简单,对每路结果按排名给分,score = sum(1 / (k + rank)),其中k通常取60。这个公式不依赖具体分数,只依赖排名,所以两个检索系统的分数尺度即使不一样也能一起融合。我实测下来的效果:混合检索之后Recall@5普遍比纯向量高10到15个百分点,尤其在专业术语密集的知识库中提升更明显。

2.2 Rerank这一步,能救回不少误召回的块

召回归召回,召回质量再高也避免不了噪音,这个时候就要上rerank。召回阶段我用的是双塔向量模型(embedding),速度快、可以大规模检索;rerank阶段我改用交叉编码器,把query和每个候选文档拼接起来,逐对计算相关度打分。交叉编码器的精度比双塔高,但速度慢,所以一般只对召回结果的前20~50个做重排,取到top 3~5给大模型。

我在本地环境里用的rerank模型是bge-reranker-base,完全没有调用线上的付费接口,效果在中文场景下基本够用。跑过几个内部测试集,重排后的命中率明显高于直接取向量top-k的结果。有一点要注意:rerank很吃对query的理解,如果query本身写得含糊,rerank也救不回来,所以下一步要重视查询改写。

还有一个细节,别只做一次“检索—生成”就完事。有一种做法是拿到初次结果后,继续用检索到的内容去触发下一轮检索,比如提取出文档里的专有名词、产品型号,把它们作为新的检索词再搜一轮。这也是Agentic RAG里最基础的一种模式,对于深度问答和跨文档问题,效果比一次性检索好很多。

3. 第三处:知识库不只是“文本库”,图片和表格怎么进

说到RAG知识库,很多人的第一反应是“纯文本”,PDF转文字,Word转文字,然后切分、向量化。但真实的业务资料里,信息最密集的部分恰恰是表格、图片、截图、产品说明书里的配图。标题里那句“RAG知识库能存储图片吗”是很多人刚接触时的第一反应,我的答案是:能,但绝对不是直接把图片本身往向量库里一塞那么简单。图片像素不能直接和文本query计算语义,必须做一层转换。

3.1 图片处理的两条路径:OCR与视觉语言模型

第一条路径是OCR。适合文字型截图、扫描件、票据、PPT截图这类“图里主要是字”的内容。先用OCR模型把文字抽出来,再把文字当普通文本切分入库。常用工具有PaddleOCR、Tesseract、以及一些商业OCR服务。我本地搭的环境用的是PaddleOCR,中文版面分类和文字识别效果比较好,且在CPU上也能跑,虽然慢一点但能用。

第二条路径是视觉语言模型。适合信息密度高的产品图、流程图、架构图、带颜色标记的图表。用VLM走一遍“图生文”流程,让模型看图并描述成一段结构化文本,再入库。比如一张系统架构图,模型会把“客户端通过网关连接服务A和服务B,数据流向……”这类信息转成文本,检索阶段就能把它当成普通文本块命中。我在本地环境用的方案是接入支持视觉的量化模型,比如Qwen-VL系列的最小号版本,或者Ollama上能拉的视觉模型。效果虽不如大参数量在线API,但胜在数据不出内网。

3.2 表格入库前,先转成Markdown并校验

表格比图片更常见,也更麻烦。直接用pdfplumber或camelot把表格抽成结构化数据,再转成Markdown表格,这样保留表头、列名和行数据之间的对应关系。我遇到过一个问题:PDF里的表格跨页后,表头在下一页不重复,直接抽取会变成“有数据没表头”的残缺表。解决方法是抽取时手动判断,遇到跨页表格时把上一页的表头拼回来,或者干脆把整张表作为一个块处理,虽然块变大了,但保证了完整性。

图片和表格最终入库时,一定要把原始文件路径或来源信息写进metadata。这样前端回答问题时可以附上“原始图片/表格链接”,用户能看到大模型引用的原始证据。纯文本回答再准,都不如让用户点开原图自己确认一眼来得可信,这也是减少幻觉投诉的最直接手段之一。

4. 第四处:知识更新,别再天天全量重建

很多RAG项目demo跑通之后就再没管过知识库更新,或者一更新就把整个库删了重建。全量重建听着简单,但真实业务里代价非常大:一套百万级向量的知识库,重新Embedding要花几个小时的算力,重建期间服务不可用,如果每天更新一次,运维成本高到让人崩溃。

4.1 增量入库与文档级去重

正确的姿势是增量入库。每个文档入库前先算一个内容哈希(比如MD5),如果hash没变,直接跳过;如果hash变了,只更新这个文档相关的块。实现上,我在向量库里为每个块都记录了它的来源文档ID和块级hash。更新文档时,先查出这个文档ID下的所有旧块,全部删除,再把新块批量写入。整个过程只涉及一个文档,而不是整个知识库,速度能快几个数量级。

这里有个很反直觉的坑:向量库删了旧块之后,如果没有及时重建索引或刷新统计信息,新增的块可能不会被正确检索到。不同向量库处理方式不一样,有些需要显式刷新,有些会自动异步索引。我在某次升级后遇到过“删了老数据,新数据明明插进去了,搜索就是找不到”的诡异问题,排查到最后就是索引刷新的问题。建议更新完成后跑一个冒烟测试,用文档里一句标志性的话去检索,确认能命中再上线。

4.2 模型版本升级时必须双写灰度

还有一类更新不是换内容,而是换Embedding模型。换模型这件事比换数据更危险,因为新旧模型的向量维度可能不一样,即使维度一样,向量空间的语义分布也不同。如果直接把新模型生成的向量插进旧模型建立的索引里,检索质量会莫名其妙地下降,而且非常难排查。我现在的规范流程是:新模型生成的向量先用一套新的collection保存,老模型继续服务线上,等新库建好并验证通过后,一次性切流量过去。宁可多花存储成本,也不做原地混合。

另一个和更新相关的点是内容过期。知识库里经常有废止的制度、旧版本的说明书、已下架的型号信息。不做内容生命周期管理的话,时间越久,检索到过时内容的概率越大,大模型就会一本正经地引用过时信息。我在实践中的处理是:在metadata里保存文档的生效日期和失效日期,检索阶段通过过滤条件排除失效文档;暂时没做完整时间线的话,至少也要在块内容里保留“版本号”和“更新时间”,让大模型在生成时可以引用这些信息,避免把旧信息当成如今的事实。

5. 第五处:查询理解,用户的问题往往不是好的检索词

RAG系统里最隐蔽的一个短板,是用户提出的问题本身就没头没尾。“那个新出的手机怎么样”这种问题,如果直接拿去做向量检索,效果能好才怪。用户不会按照检索系统的思维方式去提问,他们说话口语化、指代不清、省略严重。这个环节不做处理,后面所有检索优化都被输入质量的天花板压住。

5.1 query改写与子查询分解

我常用的方案分两层。第一层是query改写,把用户的问题改写成更适合检索的陈述句或关键词组合。比如“那个新出的手机怎么样”,如果有上下文(比如之前聊到具体型号),就补全为“某型号手机的评测和用户评价”;没有上下文时,就改写为“近期新发布手机汇总评价”。这一步我用一个小模型加一段固定的prompt就能实现,不依赖大参数模型。

第二层是子查询分解。用户的问题经常是复合型问题,比如“A产品的价格和B产品的保修政策分别是什么”,直接拿整个句子去检索,可能A召回不到、B也召回不到。我让模型把问题拆成两个独立子查询,分别检索,把结果合并后再交给生成阶段。实际操作中,我设定了最多拆成3个子查询,拆太多既增加耗时,又会引入大量噪音。中间再加一道过滤器:判断子查询和原问题是否相关,不相关的直接丢弃。

5.2 HyDE:让“答案”去检索问题

还有一个技巧叫HyDE(Hypothetical Document Embeddings),思路是先让大模型根据用户问题生成一个“假设性的回答文档”,然后用这个假设文档的向量去做检索,而不是用原始query去检索。背后的逻辑是,假设性回答在语义空间里更接近真实文档的表达方式,能提升召回率。我做过一次对比测试,对于一些描述性很强的问题,HyDE的召回效果确实更好。

但HyDE不是免费的:每次检索都要多一次大模型生成的开销,如果知识库规模很大、并发很高,最终端到端延迟和成本都会涨不少。我的建议是:只在query确实模糊、常规检索召回效果差的时候启用HyDE,并且可以用小参数模型来生成假设答案,没必要让最大的模型干这个活。同时,生成的假设答案要控制长度,我一般限制在50~100字,太长反而会带入模型自己的幻觉信息,干扰检索。

查询理解这个环节做好之后,还可以进一步接意图路由。就是先判断用户的问题属于“知识库问答”“闲聊对话”“内容总结”还是“需要调用外部工具”,不同的意图走不同的链路。比如闲聊就直接交给聊天模板,不需要傻乎乎地去检索知识库;需要实时数据的就去调接口。这一步也是从普通RAG过渡到RAG智能体的重要节点,但它建立在一个前提上:基础检索能力已经比较稳了,否则路由再准也发挥不了作用。

6. 第六处:评测,别再用肉眼感受“效果变好了”

做RAG做到后面,我发现最容易被低估的环节其实是评测。很多人调参靠感觉,改一版切分策略,问几个问题,觉得“好像变好了”,就上线了。但效果是否真的变好?是A类问题变好了还是B类问题变差了?改检索参数之后召回率具体提升多少?生成答案的幻觉率有没有变化?这些问题没有评测体系支撑的话,根本回答不了。

6.1 一套可复跑的最小评测集

我的建议是无论项目大小,先攒一套评测集。不需要一开始搞几百条,20~50条就够用。评测集里的问题要从真实用户提问中抽取,而不是开发者自己拍脑袋编出来的。每条问题要带标准答案、检索到的相关段落ID、以及期望回答包含的关键事实点。有了这套评测集,以后每次改动都可以跑一遍,用数据判断改得好不好,而不是靠直觉。

评测指标也要分层。检索层主要看Recall@k和MRR,判断正确内容有没有被召回到;生成层主要看Faithfulness和Answer Relevance,判断回答有没有忠实于检索到的上下文、有没有答非所问。Faithfulness低,说明大模型在自由发挥、产生幻觉,要先检查上下文是否完整;Answer Relevance低,说明回答离题,要检查query理解和prompt设计。分模块定位问题,效率比整体盯着一个回答看好得多。

6.2 用RAGAS或本地模型做自动化评测

自动化评测工具方面,我试过RAGAS框架,它能用大模型作为评判器,对context relevance、faithfulness、answer relevance这些维度打分。RAGAS默认会调用OpenAI的接口,但如果数据安全要求高,也可以改成调用本地部署的模型来当judge。不过要提前做好心理准备:本地小模型当法官,打分稳定性不如大模型,评测集里的case越多,偶然误差影响越小。

跑评测的时候还有一个要注意的点:答案是概率生成的,同一问题跑两遍结果可能不同。所以评测时最好固定temperature参数,甚至直接改成0,保证结果可复现。另外,评测集不是建好就完了,每次上线前都要拿新版本跑一遍旧评测集,看有没有回归。我见过不少项目,县长跑了一个“优化”,用户反馈某个老问题反而变差了,就是因为没有回归测试。

6.3 线上监控:评分是虚的,引用率是实的

离线评测之外,线上监控也不能少。我会重点盯三个指标:空检索率(即检索后没有结果的比例)、引用查证率(用户是否点击了回答里附的原文链接)、以及负反馈数。空检索率突然上升,说明数据同步或索引出了问题;引用查证率高,说明用户信任回答;负反馈暴增,多半是内容过期或者模型幻觉了。这些指标比任何“回答流畅度”的评分都更能反映RAG系统在真实场景里的价值。

最后再分享一点个人体会:这六个分水岭不是孤立的,它们环环相扣。切分策略决定了检索的上限,检索决定了下游生成能拿到什么素材,查询理解又直接影响检索的输入质量,评测则是把这些环节串起来的“仪表盘”。我见过很多团队一上来就砸重金换大模型、买GPU,却没把切分和检索这些基础功课做好,结果模型再强也喂不饱有效信息。反过来,如果前五个环节做到位了,哪怕用的是一个中等规模的模型,最终效果也完全能扛住大部分业务场景。如果让我给一个建议,那就是先把你自己的知识库当“病人”一样做一次全面体检,从切分质量开始查,一项项排除,你会发现所谓的RAG瓶颈,大部分都出在这些不起眼的分水岭上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询