RAG 切分与重排调优:把知识库问答准确率从 60% 提到 90%
RAG 切分与重排调优:把知识库问答准确率从 60% 提到 90%
上一篇文章讲了怎么搭一套本地 RAG(检索增强生成),这次聊聊进阶:为什么同样的模型、同样的知识库,你的答案准确率只有六成?问题几乎都出在「切分(Chunking)」和「重排(Rerank)」这两个环节。这篇文章用实测数据告诉你怎么调。

一、先建立度量:没有评测集就别谈调优
调优的第一步不是改参数,而是先有标尺。没有标准答案的对比,你根本不知道改完是好是坏。
建评测集三步走:
从知识库里挑 20~30 个真实问题(覆盖不同文档、不同难度);
对每个问题标注「应该命中哪几段」(期望检索结果);
跑一遍当前系统,记录命中率。
之后每次改切分/重排参数,都跑同一份评测集,看命中率变化。这一步的价值怎么强调都不为过——RAG 调优最大的坑就是「凭感觉调参」。
评测集怎么建才有代表性
随便挑几个问题不算评测集。有代表性的评测集要注意:
难度分层:三分之一是「直接抄原文就能答」的简单题,三分之一是需要跨段拼接的中等题,三分之一是「原文里没写、需要推理」的难题。只挑简单题,命中率虚高,掩盖了系统真实水平;
覆盖不同文档:每个文档类型都要有题,避免「测来测去只测了一类文档」;
问题用真实口吻:别写「完美提问」,用户实际会怎么问就怎么写——口语、简称、错误表述都要有,检索系统在「不标准提问」下的表现才是真水平;
标注「可接受命中」:一道题可能多段都相关,标出 1~2 段「有这段就算对」,比只标一段更贴近实际。
提示:评测集本身也是「活文档」。知识库更新后,题目对应的答案可能变了,定期(比如每季度)重跑一遍,确保系统没随数据漂移而退化。
二、切分(Chunking):质量的生死线
切分决定了「模型看到什么」。切太大,一段里揉进多个主题,检索命中后喂给 LLM 的内容不聚焦;切太小,语义被切碎,上下文丢失。下面是实测规律。
先想清楚切分在 RAG 链路里的位置:文档被切分成「检索的最小单位」和「喂给模型的最小单位」。检索是按 chunk 算相似度的,所以 chunk 太大,关键词会被稀释在无关文字里,相似度偏低;chunk 太小,单个 chunk 信息量不足,模型拿到手「只有碎片没有上下文」。切分的本质是在「检索粒度」和「语义完整性」之间找平衡——这正是后面所有调参的基础。
不同 chunk_size 的命中率(30 题评测集)
| chunk_size | 命中率 | 备注 |
|---|---|---|
| 200 | 62% | 太碎,经常切在句子中间 |
| 512 | 78% | 通用起点 |
| 800 | 74% | 略大,杂音变多 |
| 1200 | 66% | 明显下降 |
结论:512~768 是最稳区间。中文场景建议 chunk_size=512、overlap=80(重叠是为了防止关键句恰好被切断)。
为什么要设 overlap(重叠)?因为切分器按固定长度硬切,一条完整信息可能正好横跨两个 chunk 的边界——重叠段保证这句「至少完整出现在其中一个 chunk 里」。重叠太小防不住边界切断,太大会让相邻 chunk 高度重复、检索时多条相似结果挤占名额。80120 字符(约中文 4060 字)是稳妥区间。
1 | from langchain_text_splitters import RecursiveCharacterTextSplittersplitter = RecursiveCharacterTextSplitter( chunk_size=512, chunk_overlap=80, separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""],) |
提示:
separators的顺序很关键——先按段落分、再按句号分,中文要显式把。!?放进分隔符,否则「一句话被拦腰截断」是常态。
结构化文档:别只靠切分器
技术手册、产品文档有天然的标题层级。用「按标题切分」而不是纯字符切分,命中率能再上一个台阶:
MarkdownHeaderTextSplitter:按
###标题层级切,每块自带标题上下文;Parent-Child 切分:粗块(大段落)用于检索,细块(小句)用于喂给 LLM,兼顾「找得准」和「读得懂」。
Parent-Child 切分的实现
Parent-Child(父块-子块)是很实用的一套思路,代码也不复杂:
1 | from langchain_text_splitters import RecursiveCharacterTextSplitterparent_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=120)child_splitter = RecursiveCharacterTextSplitter(chunk_size=120, chunk_overlap=20)# 先用粗块切大段落,再对每块内部切细块# 检索时对 child 向量化并检索,命中后返回其 parent 喂给 LLM |
核心逻辑一句话:检索用 child(精准定位),生成用 parent(上下文完整)。命中一块细小的关键词所在的子块,却能拿到包含它的完整段落,兼顾了「检索精度」和「回答上下文」两个诉求。对产品手册、说明书这类「段落独立成文」的文档尤其有效。

三、重排(Rerank):从「找得全」到「选得准」
向量检索的第一步是召回——它追求「相关的都捞进来」,所以 top-20 里可能有一半是噪声。重排的作用是第二步精修:把候选逐条和问题做深度语义比对,重新排序,只留 top-3。
实测:加不加重排的差距
| 配置 | Top-3 命中率 | 答案满意度 |
|---|---|---|
| 纯向量检索,直接取 top-3 | 61% | 6.5/10 |
| 召回 top-20,再取前 3 | 70% | 7/10 |
| 召回 top-20 + 重排选 top-3 | 84% | 8.5/10 |
重排的收益非常直观:召回多一点(top-20),重排精一点(top-3)。
重排器选型
| 重排器 | 特点 | 适用 |
|---|---|---|
| bge-reranker-v2-m3 | 中文好,多语言 | 中文知识库首选 |
| Qwen3-Reranker | 阿里系,兼容好 | 阿里生态 |
| Cohere Rerank | 云端 API | 不介意上云 |
1 | from langchain.retrievers import ContextualCompressionRetrieverfrom langchain.retrievers.document_compressors import CrossEncoderRerankerfrom langchain_community.cross_encoders import HuggingFaceCrossEncoderreranker = CrossEncoderReranker( model=HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-v2-m3"), top_n=3,)compressor = ContextualCompressionRetriever( base_compressor=reranker, base_retriever=retriever, # 原来的 retriever,k=20) |
提示:重排会增加 50~200ms 延迟,但因为它让喂给 LLM 的是精选片段,总 token 反而更少、答案更准——「没有理由不加」的一环。
重排参数怎么调
重排不是「开了就行」的开关,几个参数值得调:
top_n:最终保留几条。3 是默认甜点;答案需要「综合多段信息」时调到 5;追求「每问必对单一事实」时 3~4 足够;
召回数量:重排器的输入(base_retriever 的 k)建议取 20~50。太少,好片段可能根本没被召回;太多,重排本身变慢、意义也不大;
重排器与 Embedding 配套:重排器和向量模型尽量用同一语言生态(比如都选 BAAI 家的),跨厂家组合偶有「打分不协调」的问题。
四、混合检索:补上精确匹配的短板
向量检索擅长语义匹配,但对「精确型号、专有名词、编号」这类词很弱。比如用户问「CWM-3040 的功率」,向量检索可能找不到字面匹配。混合检索 = 稠密向量 + BM25(词面匹配),用 RRF 合并排名:
1 | # 以 ChromaDB 为例,开启混合检索retriever = db.as_retriever( search_type="mmr", search_kwargs={"fetch_k": 20, "k": 4},) |
BM25 对「精确词」的命中几乎是向量检索望尘莫及的。社区评测共识:纯向量 → 加混合检索,命中率提升 10~20%。Weaviate、Qdrant 等向量库原生支持混合检索,一次查询返回融合结果,最省事。
混合检索的两种实现路线
原生支持的向量库(Weaviate / Qdrant / Elasticsearch):在查询参数里同时传
vector和bm25_query,返回融合结果,一行配置完成,最省事;代码组合(LangChain / LlamaIndex):分别跑向量检索和 BM25 检索,再用 RRF(Reciprocal Rank Fusion)合并两个排名列表。
RRF 合并的核心公式很朴素:
1 | score(d) = Σ 1 / (k + rank_i(d)) |
即对每个文档,汇总它在两路检索中的排名倒数。排名越靠前,贡献分越高;两边都排得靠前的文档自然胜出。k 通常取 60,代码实现约十行,网上有大量现成实现可直接抄。
什么场景必须混合检索
判断你的知识库需不需要混合检索,问几个问题:
文档里有没有型号、编号、日期、代码片段这些「必须字面匹配」的内容?有 → 需要;
用户提问时会不会照抄原文的词(比如直接报型号)?会 → 需要;
文档是不是同一主题的高密度内容(向量检索容易把相似句子全捞出来)?是 → 需要。
反之,如果都是「讲概念的散文型文档」、用户提问也是语义化描述,纯向量就够,混合检索提升有限。
五、完整的调优清单
按优先级排,从投入产出比最高开始:
建评测集(半小时)——没有度量,一切白搭;
切分 512~768(10 分钟)——命中率 +10~15%;
开启混合检索(10 分钟)——精确词命中 +10~20%;
加重排器(10 分钟)——Top-3 准确率 +10%+;
结构化切分(文档规范时)——技术文档 +5~10%;
提示词约束(「仅基于上下文回答」)——答案质量 +,抑制幻觉。
提示:上述百分比的量级基于社区公开评测和本站实验,具体数值因数据而异,但「切分 → 混合 → 重排」的顺序是稳定的——越靠前的环节,越先做。
一个真实的调优案例
用一份 200 页的产品技术手册(含大量型号参数)做实测,完整走一遍调优流程,看数字变化:
| 阶段 | 配置 | Top-3 命中率 |
|---|---|---|
| 起点 | 默认切分 1000、纯向量 | 58% |
| ① 建评测集后测出基线 | — | 58% |
| ② 切分调 512 + overlap 80 | 仅改切分 | 71% |
| ③ 加混合检索(BM25+向量) | 精确词命中 | 78% |
| ④ 加重排器(top-20 → top-3) | 精炼排序 | 86% |
| ⑤ 结构化标题切分 | 技术文档 | 90% |
从 58% 到 90%,每个环节 +6~8 个百分点。注意规律:每加一环收益都在递减,但最终组合的累计提升非常可观。这组数据想传达的另一个信息是:没有一个环节是「银弹」,但把每个环节都做到位,结果就是质变。
六、调参时的三个陷阱
陷阱 1:只加词不建评测。改完切分大小凭感觉「好像好了」,下个月回退都不知道为什么。
陷阱 2:chunk 大小一刀切。不同类型文档用同一套参数。产品手册用结构切分,聊天记录用定长切分,新闻稿用段落切分。让「文档类型」决定「切分策略」。
陷阱 3:重排后直接 top-1。重排器有时也会把次相关排第一,建议 top-3 都喂给 LLM,让模型综合判断。
更多容易忽视的坑
陷阱 4:Embedding 模型和语言不匹配。中文文档配了个英文向量的 Embedding 模型,语义匹配自然稀烂。中文知识库优先 bge-m3、bge-large-zh、Qwen3-Embedding,别用英文为主的模型硬扛。
陷阱 5:改了 chunk 忘了重新向量化。切分参数变了,但库里存的还是旧向量——参数改了等于没改。每次改 chunk 后必须重建向量库(删掉重建或增量重索引),否则命中率不会变。
陷阱 6:追求「单次调优到完美」。RAG 是系统性工程,一次改一个变量、跑评测、看数字,才是正确的循环。想「一把调好」,结果往往是越调越乱。
七、进阶方向
调完上面这些,还有几个更深的杠杆:
Query 改写:把用户问题先交给 LLM 改写/拆解成多个子查询再检索;
递归检索:第一轮检索结果的摘要再作为第二轮查询的条件;
Graph RAG:实体关系图 + 向量检索结合,适合强关系的知识图谱类资料。
这些属于「量变到质变」的下一步,普通知识库先把本文六步做到位,准确率从 60% 提到 90% 完全现实。
Query 改写:检索前先想清楚
用户提问通常很口语、很简短,直接拿去检索命中率有限。Query 改写就是让 LLM 先把问题「翻译」成更适合检索的形式:
1 | from langchain_community.llms import Ollamallm = Ollama(model="qwen3:8b", temperature=0)def rewrite(query): prompt = f"""把下面的用户问题改写成更适合知识库检索的形式,可以拆成多个子问题,用分号分隔。只输出改写结果,不要多余内容。用户问题:{query}改写:""" return llm(prompt).strip()# 改写后按子问题分别检索,合并结果 |
典型例子:「内存怎么配置」这种含糊问题,改写成「内存配置步骤;内存常见参数;内存故障排查」三个子查询,命中率立刻提升。改写的成本是额外一次 LLM 调用(约 0.5~1 秒),换来的是检索质量提升,在「用户提问不规范」的场景里收益明显。
什么时候加这些进阶手段
进阶手段不是标配,有明确的使用场景:
Query 改写:用户提问口语化、模糊词多时值得加;
递归检索:文档层级深、需要「先定位章节再定位细节」时有用;
Graph RAG:强实体关系(如「A 产品依赖 B 组件」)的知识库,图谱能提供向量检索给不了的关系推理。
如果普通知识库的检索命中率已经 80%+,优先把已有链路打磨好,而不是急着上这些复杂度。
小结
RAG 调优的路径非常清晰:先建评测集、再调切分、然后开混合检索、最后加重排。这套组合拳能把本地知识库问答从「大概能答」提到「基本可信」。记住两个数字:chunk_size 取 512~768,召回 top-20、重排 top-3。指标对了,方向就不会跑偏。
最后用一句话总结方法论的核心:RAG 调优不是玄学,是「度量 + 逐个变量实验」的工程。每一个环节(切分、Embedding、检索、重排、提示词)都值得用评测集量化验证,不要凭感觉。一次改一个变量、跑评测、看数字,循环推进——从 58% 到 90% 就是这么一点点磨出来的。先做到这一步,再考虑 Query 改写、Graph RAG 这些进阶手段,你的知识库问答就已经超过大多数「搭起来就丢一边」的系统了。记住,这套方法的核心是把「调参」从玄学变成工程——每一步都有数字说话,结果自然可控。评测集花两个小时搭好,之后每一次改动都有依据,这才是调优的正确打开方式——数据驱动,而不是凭感觉。
延伸阅读:





