RAG检索优化实战:混合搜索、重排序与召回率评估

RAG错误答案多源于检索失败,应先用recall@k和MRR量化检索,再逐项优化切分、混合搜索、重排序与过滤。
文章指出,RAG系统给出错误答案时,问题通常不在生成模型,而在检索环节——正确的文本块根本没被召回,再强的模型也无从作答。因此应先脱离生成模型,用50到100个带标注的真实问题建立检索评估基准,用recall@k和MRR两个指标量化检索质量。在此基础上依次优化:按文档结构(标题、段落、表格行)切分并附加标题路径前缀;用BM25关键词检索与向量检索混合,通过RRF融合结果以兼顾精确字符串与语义匹配;用交叉编码器对少量候选重排序;把元数据和权限过滤前置到检索之前;并为确实无答案的问题设置分数阈值,让模型如实说明找不到信息。
RAG系统给出错误答案时,问题往往不在生成模型本身。模型只是基于检索到的文本块作答,如果正确的文本块压根没被检索出来,再强的模型也无能为力。修复检索环节,大部分错误答案会自动消失。
先单独衡量检索质量
在调整提示词或更换模型之前,需要先建立一套检索评估基准。准备50到100个真实问题,并为每个问题标注真正能回答它的文本块。然后脱离生成模型,单独测量检索效果。

核心指标有两个:
- recall@k: 在top k结果中至少包含一个正确文本块的问题比例。如果recall@5只有60%,意味着模型在动笔之前,十个问题里就有四个找不到答案。
- MRR(平均倒数排名): 第一个正确文本块位置倒数的平均值。它反映正确结果是在顶部附近,还是勉强挤在第9名。
后续每一项改动都应该推动这两个数字变化。如果指标没动,说明改动没有实际效果。
按文档结构切分,而非字符数
固定500字符切分会把表格拦腰截断,也会把步骤4和步骤1-3拆散。更好的做法是按照文档自身的结构切分:标题、段落、列表项、表格行保持完整。
每个文本块应带上标题路径前缀,比如"计费 > 退款 > 部分退款",这样即使块内只写了"这需要5天",也能明确"这"指代什么。
切分大小是一种权衡。太小的块丢失上下文,太大的块把多个主题混入同一个嵌入向量,关键的那句话会被稀释。
混合搜索:关键词与向量结合
嵌入向量擅长语义匹配,但在精确字符串上表现糟糕。当查询涉及错误码E1042、SKU 88-210或某人的姓氏时,向量搜索经常返回主题相关但并非目标实体的结果。BM25这类关键词搜索恰好能处理这些场景。
同时运行两种检索并合并结果列表。倒数排名融合(RRF)是简单且稳健的合并方式,因为它只使用每个列表中的排名,而不依赖两个系统之间不可比较的分数:
def rrf(result_lists, k=60):
scores = {}
for results in result_lists:
for rank, doc_id in enumerate(results, start=1):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
return sorted(scores, key=scores.get, reverse=True)
k=60是原始RRF论文中的取值,也是一个合理的默认值。
BM25 是信息检索领域沿用数十年的经典排序函数,属于稀疏检索(sparse retrieval)家族。它把文档和查询都表示为词项集合,核心思想基于两点:词频(TF)越高,该词对文档越重要;但词在语料中出现得越普遍(逆文档频率 IDF 越低),区分度就越小。BM25 在此基础上引入文档长度归一化,避免长文档仅因词多而占优。它不做任何语义泛化,因此对 E1042、88-210 这类罕见字符串极为敏感且精准,但对同义改写无能为力——这正是它与稠密向量检索互补的原因。
RRF(Reciprocal Rank Fusion,倒数排名融合) 解决的是多路检索结果如何合并的问题。不同检索系统给出的相似度分数往往不在同一量纲上,直接加权求和不可靠。RRF 干脆丢弃分数,只看每个文档在各结果列表中的排名,用 1/(k+rank) 累加得分,排名越靠前贡献越大。常数 k 起平滑作用,防止排名第一的文档权重过高;k=60 来自 2009 年 Cormack 等人的原始论文,实践中 20 到 100 之间都表现稳定。由于只依赖排名,RRF 对不同检索器天然鲁棒,几乎不需要调参。
对候选结果做重排序
嵌入搜索比较的是查询向量与预先计算好的文本块向量。交叉编码器重排序器则同时读取查询和每个文本块,对相关性的判断更准确,但速度太慢,无法对全量语料运行。实际做法是:低成本检索50个候选,重排序后保留top 5。
交叉编码器(cross-encoder) 与生成嵌入向量的双编码器(bi-encoder)在架构上根本不同。双编码器分别把查询和文档编码成两个独立向量,再用余弦相似度比较,文档向量可以离线预计算,因此支持对全量语料快速检索。交叉编码器则把查询和文档拼接成单一输入送进 Transformer,让注意力机制在两者之间直接交互,能捕捉更细粒度的语义关联,相关性判断明显更准。代价是无法预计算:每个(查询,文档)对都要跑一次前向推理,对百万级语料完全不可行。因此标准做法是把交叉编码器仅用于对检索返回的几十个候选做精排,形成“粗召回 + 精排序”的两阶段流水线。常用的开源模型包括基于 BERT 或 MiniLM 的 cross-encoder 系列,越小越快的版本在延迟敏感场景更合适。
先过滤,再排序
如果问题限定在某个产品、某一年份或某位客户的文档范围内,应将其作为元数据过滤器应用,而不是寄希望于相似度自动区分。权限过滤尤为关键:在检索之前就过滤掉用户无权查看的文档。在提示词里写"不要提及机密文档"不构成访问控制。
元数据过滤 指的是在检索阶段用结构化字段(如产品名、年份、客户 ID、文档类型、访问权限标签)对候选集合做预筛选,而不是让相似度分数自行承担区分职责。常见实现有两种:预过滤(pre-filtering)先按条件缩小文档集合再执行向量搜索,保证结果不越界,但会牺牲部分索引效率;后过滤(post-filtering)先检索再剔除不匹配项,速度快却可能导致有效结果不足。成熟的向量数据库(如带元数据字段的 FAISS 封装、Qdrant、Weaviate、pgvector 配合 SQL 条件)通常支持预过滤。权限过滤 是元数据过滤中安全性最高的一类:必须在检索前依据用户的访问控制列表排除无权文档,任何依赖模型“自觉不提”提示词约束的做法都不构成真正的访问控制,一旦用户通过诱导性提问就可能泄露机密内容。
为"找不到"做好准备
有些问题在数据中确实没有答案。如果重排序后的最高分低于基于测试集验证过的阈值,应告知模型未找到相关内容,让它如实回复。"我在文档中找不到这个信息"是一个好答案。用松散相关的文本块拼凑出的自信回答,才是最糟糕的结果。
相关推荐

Workbuddy零基础入门:桌面AI智能体如何落地工作流
Workbuddy桌面AI智能体零基础教程解读:从安装、积分领取到文件管理与自动化工作流搭建,对比CodeX的适用场景与成本差异,帮助职场人完成从AI提问者到AI管理者的思维转变。

OpenAI智能体新栈:计算机操作、决策API与超速工作流
OpenAI Dev Day发布智能体新栈:Computer Use操作速度超越普通人,Decisions API基于Luna实现极低延迟分类,Agents API开放同源计算机操作能力,GPT-6.1 Sol成本仅为Astra七分之一。本文梳理核心更新、实测数据与开发者落地要点。

梁文峰开源推理框架DeepInfo,硬刚美国禁令
DeepSeek在开源模型后再开源推理框架DeepInfo,包含GPU/CPU交叉流水线、小模型预判大模型复核、华为昇腾与英伟达芯片全面适配等技术,直面美国联邦禁令。本文解析其对AI产业落地的意义与开源策略。