RAG(检索增强生成)这两年真是被聊烂了。随便刷刷就能看到"十分钟搭建本地知识库""零基础搞定企业问答机器人",点进去内容高度一致:加载文档、切分、向量化、存库、检索、拼Prompt、调用大模型。实话实说,这套流水线只要看过两篇教程的人都能搭出来,但放到真实业务里跑一圈,差距立刻见分晓。有人搭出来的系统检索不准、答非所问,有人却能把准确率从及格线一路拉到90%以上。分水岭从来不在流水线本身,而在于你愿不愿意在六个被人忽视的环节上下硬功夫。
这篇文章我把这六个环节拆开讲清楚:文本切分、召回质量、查询处理、知识组织、评估体系、工程化工具链。内容偏实战,会带上我在本地私有化部署和几个真实项目里踩过的坑、验证过的做法。适合正在做RAG项目、或者被"能跑但不好用"困扰的朋友参考,也顺便聊聊RAG瓶颈到底卡在哪。
1. 先泼盆冷水:流水线人人都会搭,差距从不在这里
1.1 那条"标准流水线"到底有多简单
RAG的标准流程用一句话就能概括:把外部知识切成块,编码成向量存进向量库,用户提问时先从库里捞相关片段,再把片段和问题一起丢给大模型生成答案。就这么四步。我在本地用Ollama搭过一个最小可用的知识库demo,从零到能问答前后不到两小时,全程没有需要调参的地方。网上那些"零基础可复制教程"基本也是这个套路:装个embedding模型、装个向量库、写个retriever、拼个prompt,完事。
问题在于,这个demo的准确率大概率只有60%上下。你问"去年的销售数据是多少",它可能抓回来一条无关的会议纪要;你问"A方案和B方案的区别",它可能只召回A方案的片段,然后把B方案的内容硬编出来。能跑和能用,中间隔着的就是标题里说的那六处细节。套用一句同行的话:RAG流水线谁都能写,但能把准确率从及格线拉到优秀线的人,才拿得到项目该有的预算。
1.2 为什么"能跑"和"能用"是两回事
我见过太多项目死在"demo做完就以为交付了"这一步。架一个知识库很容易,但业务方要的不是一个会引用文档的聊天机器人,而是一个在权限范围内对每个问题都给出准确答案的系统。差别体现在几个维度上:召回是否齐全且干净、答案是否忠于原文、复杂问题是否被正确拆解、知识更新后系统能否快速感知。这些维度没有哪个靠默认配置就能解决,全都需要针对数据特性和业务场景单独调整。
换句话讲,流水线只是地基,六处细节才是装修。地基烂了大家看不出,但装修优劣一眼便知。后面我把这六处逐一展开,每处都给出我实测过的做法和踩过的坑,能少走一段弯路就是这篇文章最大的价值。
2. 分水岭一:文本切分,决定知识库"下限"的第一关
2.1 固定窗口切分的问题
所有RAG项目的第一步都是把文档拆成小块,但恰恰是这一步最容易被敷衍。默认方案是固定窗口切分:按256或512个token硬切,不考虑段落边界,更不考虑语义完整性。结果就是一句话被拦腰截断,一个表格被劈成两半,一个完整的操作步骤被拆得七零八落。检索时召回来的片段经常语义残缺,大模型只能基于半个句子脑补,幻觉几乎无法避免。
我记得有个项目在处理产品手册时,PDF里每个章节的标题和正文是分开的,固定窗口切分后,标题经常被丢掉,剩下的正文片段连"这是哪个章节的内容"都看不出来。后来我把切分粒度从512降到200,召回质量反而提升了,就是因为小片段更容易精准命中,再配合后续的多段召回,完整度并没有损失。切分不是越大越好,也不是越小越好,关键是要和你的召回策略配套。
2.2 三种进阶切分策略与实测对比
切分这件事,我试下来有三条路是真正有用的。
第一是结构感知切分。对PDF、Word这类带标题层级和表格结构的文档,先用解析器把结构还原出来,再按标题级别切分,让每一块都带上下文头。这样检索到的片段天然知道"自己属于哪一章",大模型回答时也能顺着结构走。本地工具的话,我常用Unstructured和MinerU这类开源解析库,先把版面结构抽出来再喂给切分器。
第二是语义切分。用embedding模型计算句子之间的相似度,语义断崖处就是切分点。这种方法适合口语化的对话记录、访谈稿这类结构不明显的内容。我实测过,语义切分比固定窗口在内部知识问答上大概能提升5到8个百分点的命中率,代价是切分速度慢了不少,离线跑一次几万字的文档要几十秒,但索引是一次性的,这个成本完全值得。
第三是递归切分加重叠。先按段落切,段落太大再递归往下切,同时让相邻块保留少量重叠token。重叠的作用是防止关键信息正好落在边界处被切断。这个方案适合代码文档、接口说明这类"块状"内容,实现起来也简单,LangChain和LlamaIndex里都有现成的RecursiveCharacterTextSplitter,改几个参数就能用。
切分这件事没有银弹。我的心得是:先看你文档的类型,再决定策略;不要在切分参数上省调研时间,因为它是后面所有环节的地基,地基歪了,重排模型再强也救不回来。
3. 分水岭二:召回质量,一个向量数据库解决不了召回
3.1 混合检索:BM25、向量和RRF怎么搭
很多人以为装个向量库、调个embedding就算完成召回了,但真实场景里,召回不到正确答案的原因往往不是向量化不好,而是"相似度"这个概念本身太单薄。用户提问"库存周转率怎么算",如果知识库里恰好有"周转率计算公式"这种关键词匹配度很高的文档,向量检索反而可能输给传统的BM25关键词检索。这就是为什么我强烈建议在正规项目里做混合检索。
混合检索的常规做法是三条路并行:向量检索负责语义召回、BM25负责关键词精确召回、再用RRF(Reciprocal Rank Fusion)把两路结果合并排序。RRF的原理很朴素:每个文档在两路结果里的排名取倒数,然后相加排序。排名第一得1分,第二得0.5分,以此类推,两路都命中的文档天然排前面。我对比过纯向量和向量加BM25的组合,在包含大量专有名词、型号、编号的企业知识库里,混合检索的Recall@5一般能提升10到20个百分点,效果非常直观。
3.2 重排模型:把候选集从100缩到5的关键一环
混合检索召回的结果通常有几十上百条,但真正能喂给大模型的只有三五条。如果直接把前100条都塞进Prompt,不仅浪费token,还会引入大量噪声,导致模型答非所问。这时候就需要重排模型(Reranker)出场。重排模型和embedding模型的本质区别在于,它是把"问题+文档"拼在一起做深度交叉编码,计算的是两者之间的相关性,而不是各自向量之间的距离。
我常用的做法是二阶段召回:第一阶段用轻量检索把候选集捞到100条,第二阶段用重排模型重新打分,只取前3到5条送入生成。BGE-Reranker和Cohere Rerank是我用得比较多的选择,本地私有化场景下BGE系列部署很省心,一个6B级别的模型在普通企业服务器上也能跑得动。这一步对效果的提升极其明显,很多项目做完这个改造,Faithfulness(忠实度)直接上涨一截,因为模型收到的上下文干净了。
重排这里有个细节容易踩坑:重排模型的输入长度有限,候选文档太长要先做截断。我习惯把每篇候选先切到512个token以内再交给重排,长文档截哪一段、保留哪一段,要和前面的切分策略对齐,否则可能把结论部分丢掉,重排出来一个没有答案的片段。
4. 分水岭三:查询处理,别让用户的烂问题毁掉好检索
4.1 查询改写与意图理解
大部分RAG项目的第一个线上事故,都是被用户的原始问题直接打崩的。用户不会像搜索引擎那样输入精确关键词,他们说的是"那个东西怎么弄""上次说的那个方案后来咋样了"。这类指代不清、信息不足的问题直接拿去检索,召回结果基本靠猜。所以查询处理这一步,本质上是"在检索之前,先把问题变成一条好的检索语句"。
我常用的做法是先用大模型做一轮查询改写:把口语问题转成适合检索的短句,补充缺失的实体信息,并把多轮对话里的指代消解掉。比如用户说"它支持图片吗",如果前面讨论的是某个知识库系统,改写后就变成"该系统是否支持图片格式的知识条目存储"。Dify这类平台上也有现成的查询改写节点,配置一下就能生效,但要注意改写过程本身消耗一次大模型调用,延迟会多几百毫秒,批量接口场景要评估好性能预算。
4.2 多轮对话与HyDE,两个高频实战方案
多轮对话是查询处理里最常见的难题。用户在RAG智能体里连续提问,第二句往往省略了主语和上下文。解决方案一般有两种:一是把最近几轮对话拼在一起作为检索上下文,由大模型抽取当前轮的真正意图;二是做会话级摘要,把整个会话浓缩成一段背景信息,每次都携带。前者实现简单但token开销大,后者适合长会话场景,各有利弊。
另一个值得一试的方案是HyDE(Hypothetical Document Embeddings):让大模型先根据问题生成一段假设性的答案文本,再用这段文本去做向量检索。原理是假设答案和真实文档的语义距离比"问题"和"文档"更近,在问题表述模糊时尤其好用。我实测这个技巧在处理"我忘了具体名词,只记得大概意思"的问题时效果很明显,坏处是每次检索要多一次生成调用,成本翻倍,不适合所有场景。做不做HyDE,建议先在bad case里统计一下"问题表述模糊"占比再决定。
5. 分水岭四:知识组织,Ontology RAG 解决的是"关系"问题
5.1 平铺文本 vs 关系结构
如果说切分、召回、查询处理解决的是"片段级"问题,那知识组织解决的就是"关系级"问题。传统RAG把知识库当成一个平面文档堆,检索时只考虑相似度,不考虑概念之间的关系。比如"客户投诉处理流程"和"售后工单升级机制"明明是强相关的两条知识,但因为用词不同、段落分散,向量检索可能完全关联不上。这恰恰是RAG瓶颈在复杂业务场景里暴露得最明显的地方。
Ontology RAG的思路,是在文档之外再加一层知识结构:先把核心概念、实体、关系抽出来,构建一个轻量级的本体或者主题图,检索时先定位"问题涉及哪些实体和关系",再沿着关系去拉取相关文档。这种做法在Wiki和RAG的对比中特别有价值——Wiki擅长展示概念之间的链接关系,而普通RAG拿到的是一个个孤立的文本块。把Wiki的链接结构思想引入RAG,其实就是在构建知识组织层。
5.2 用一个轻量主题图改造已有知识库
很多人一听知识图谱就头大,觉得要上Neo4j、要写抽取流水线,成本太高。实际落地可以轻得多。我在一个售后知识库项目里做过一个简化版:先让大模型离线抽取文档里的核心实体和"实体-关系-实体"三元组,生成一张简单的主题图,把每个实体关联到的段落编号记录下来。检索时先查主题图,找到问题相关的实体,再根据实体反向定位段落,最后和向量检索的结果合并排序。
这个改造成本不高,一个脚本就能跑抽取,但效果很显著。原来"内存不足"和"扩容方案"这两类知识完全各管各,引入关系之后,问"内存不足怎么办"能同时召回扩容文档和排查手册,知识的覆盖面立刻不一样了。如果你的知识库涉及大量专业术语、产品型号、流程节点,强烈建议在纯文本RAG之外补一层关系结构。需要注意的是,Ontology抽取的质量依赖大模型能力,抽完必须人工抽检一轮,否则错误的关系会把检索带偏。
6. 分水岭五:评估体系,没有评测的RAG就是盲人摸象
6.1 离线评估:召回率、命中率、Faithfulness
做RAG项目最怕没有评估就上线。你改了切分参数、换了embedding模型,到底有没有变好?凭感觉不靠谱,必须有数据说话。我建议每个项目在第一天就建立一套离线评测集:挑50到100个真实业务问题,人工标注标准答案和对应知识来源。这些问题是后面所有优化动作的标尺。
离线评估至少要看三个指标:召回层面的Recall@K,看正确答案是否在召回的K个片段里;生成层面的Faithfulness,看大模型的回答是否支持引用的文档、有没有编造内容;还有端到端的答案准确率,由人工或强模型打分。我见过不少团队只盯着"答案像不像"看,忽略了召回环节的指标,结果重排怎么调都没头绪。其实逻辑链路是:先保证Recall达标,再优化Faithfulness,最后才是答案质量的精细调优,顺序不能乱。
6.2 在线反馈与bad case闭环
离线评测集是静态的,线上用户的真实问题才是动态的。RAG上线后一定要做bad case闭环:把线上答错的对话沉淀下来,每周归类分析,看错误集中在哪个环节。是检索没召回?还是召回对了但大模型没用好?还是切分把上下文切丢了?归因清楚才能对症下药。
我见过一个很有意思的案例:某系统的bad case大量集中在"用户问缩写"上,比如"QPS""SLA"这类,业务方根本不会在文档里写全称。后来在查询改写环节加了一个缩写映射表,问题瞬间解决大半。这种问题如果不做bad case分析,靠再强的模型也发现不了。评估体系就是这样,它的核心价值不是证明你的系统好,而是告诉你下一步往哪改。
7. 分水岭六:工程化与工具链,框架选型决定开发效率
7.1 本地私有化:Ollama + 轻量工具链的搭法
很多团队因为数据保密要求必须做本地化部署,这时候工具链的选择直接影响项目周期。我自己在本地跑RAG的标配是:Ollama跑embedding模型和生成模型,向量库用Milvus或者Chroma,切分解析用前面提到的开源库,整个链路全部离线。Ollama的一大优势是模型管理极其简单,拉模型、跑服务一条命令搞定,内存和显存占用也透明,适合快速验证。
有人问我"RAG知识库能存图片吗",答案是能,但要看你说的是哪种存法。如果只是把图片原样存在知识库里做档案管理,任何文件系统都能做;如果你想让图片参与检索和问答,就得走多模态路线,比如把图片转成文字描述存进向量库,或者直接用多模态embedding模型。本地场景下我建议先做"图片转描述文本"的方案,用VLM模型给图片生成一段说明文字,再走文本RAG的完整链路,成本低、见效快。纯多模态检索虽然上限高,但部署和调优的复杂度会明显上一个台阶。
7.2 平台化工具:Dify、LangChain4j 怎么选
如果团队不想从零造轮子,直接用平台化工具是更稳的选择。Dify这类平台把知识库流水线做成了可视化节点,上传文档、配置切分、设定检索策略、编排Agent都能在界面上完成,特别适合业务人员一起参与调优。Dify知识库流水线里有一个容易被忽略的节点是"知识库检索"和"知识库重排"是分开的,默认情况下只做向量检索,记得手动打开混合检索和重排开关,不然就白白浪费了工具的能力。
Java技术栈的团队可以看LangChain4j,它在Spring生态里集成得很好,支持声明式RAG(easy RAG),结合Spring AI使用,能快速把RAG能力嵌进已有的Java服务里,坑比直接调原版LangChain的Java封装少很多。我的选型建议很简单:快速交付选Dify这类低代码平台;深度定制选LangChain4j或LlamaIndex这类框架;纯私有化且团队能力强,直接手写核心链路也完全可行。工具没有绝对好坏,匹配团队现状和项目诉求的才是对的。
8. 常见问题与排查技巧实录
8.1 检索不到、答非所问、幻觉三大顽疾怎么定位
项目跑起来之后,翻来覆去遇到的无非就三类问题。第一类是"检索不到答案",优先查切分和embedding:文档有没有被正确解析?切分后的片段是否语义完整?领域专有名词有没有在embedding模型词表里?第二类是"答非所问",优先查查询处理和重排:用户的原始问题太模糊,先看改写环节有没有生效;再查重排模型是不是把相关文档排到了后面。第三类是"幻觉",即模型编造内容,优先查Prompt和上下文:回答是否被要求严格基于引用片段?召回的片段数量是否过多、噪声太大?
排查的顺序建议固定下来:先看召回片段是对的,再看Prompt有没有问题,最后才怀疑生成模型本身。很多团队一出问题就怪大模型能力不行,实际上八成的bad case都出在检索上游。我在调试时习惯把每次检索的中间结果全部可视化打出来,看一眼就知道是哪个环节掉的链子,比瞎猜高效得多。
8.2 五条实战避坑清单
最后整理一份我踩过坑之后沉淀下来的清单,都是常规教程里不会写的东西。
第一,小切分加多召回比大切分加单召回稳妥得多。宁可每次多捞几段再做重排,也不要让一个片段包办一切。第二,评估集必须包含"刁钻问题"。只有常规问答的评测集,反映不了系统在边界场景的真实水平。第三,重排模型和embedding模型最好不是同一个。用同一个模型做召回又做重排,等于用一个尺子量两种东西,效果上不去。第四,Prompt里必须写明"找不到就直说"。让模型在证据不足时承认不知道,比硬编一个答案体面得多,也更容易被业务方接受。第五,任何RAG项目都要给知识更新留出通道。文档版本一变,旧的向量和索引要能快速重建,这个能力在架构设计阶段就要想清楚,后面再补会很痛苦。
RAG能不能做好,从来不是模型、框架或向量库单一决定的,而是这六处细节叠加出来的结果。我个人的体会是,前三个分水岭(切分、召回、查询)决定了系统的下限,后三个(知识组织、评估、工程化)决定了系统的上限。如果你现在正被自己的RAG项目折磨,不妨按这个顺序把六处逐个过一遍,大概率能在其中某一两处找到突破口。