混合检索与重排:从"能找到"到"排得准"
RAG 系统的检索质量通常决定最终回答上限。单纯依赖向量检索,在 demo 中经常表现不错,但进入真实业务后会遇到很多问题:精确词召回不稳、结构化条件无法严格满足、重复 chunk 过多、Top K 噪声大、真正相关文档排在后面。
混合检索和重排的目标,就是把召回率和排序质量分开优化:先尽量把可能相关的候选找回来,再用更精细的模型或规则重新排序。
1. 为什么单路检索不够
向量检索擅长语义匹配,但不擅长所有检索问题。
它适合:
- 同义表达。
- 口语化问题。
- 语义相近但字面不同的资料。
- 开放式知识问答。
它不擅长:
- 精确编号。
- 人名、地名、代码。
- 日期和数值范围。
- 强布尔条件。
- 术语必须命中的场景。
例如用户问"找 5 年以上经验且会 Go 的候选人",其中"5 年以上"应由结构化过滤保证,"Go"可能需要关键词召回,"候选人相关经验"则适合语义召回。单一路径很难同时做好。
2. 混合检索的基本结构
混合检索通常由多路召回组成:
text
用户问题
-> 参数抽取
-> 结构化过滤
-> 稠密向量召回
-> 稀疏向量召回
-> 全文检索召回
-> 合并去重
-> 重排
-> 返回结果
这里的关键思想是:不同召回器解决不同问题。
- 稠密向量负责语义相似。
- 稀疏向量负责词项匹配。
- 全文检索负责关键词、短语和字段。
- 结构化过滤负责硬条件。
这比"所有问题都向量化"更符合业务现实。
3. 稠密和稀疏信号如何融合
稠密向量和稀疏向量的分数来源不同,不能随意直接相加。常见融合方式包括:
- 加权融合:给不同通道设置权重。
- RRF:根据排名而不是原始分数融合。
- 规则优先:硬条件过滤后再做语义排序。
- 分阶段处理:先召回候选,再交给重排模型。
加权融合简单直观,但权重需要评估。语义问题多的场景可以提高稠密权重;精确词、术语多的场景可以提高稀疏或全文权重。
不要把权重当成永久真理。它应该基于测试集、业务反馈和线上日志调整。
4. 元数据过滤是业务检索的稳定器
很多条件不应交给模型猜。例如:
- 年龄小于 35。
- 工作经验大于 5 年。
- 城市为北京。
- 日期在某个范围内。
- 文档类型为合同。
这类条件适合在检索阶段用 metadata filter 或数据库过滤表达式执行。这样能保证硬条件严格生效。
参数抽取在这里非常重要。它负责把自然语言变成结构化条件:
json
{
"age_max": 35,
"experience_min": 5,
"city": "北京"
}
之后程序再构造过滤表达式,而不是让模型自己判断哪些文档符合条件。
5. 重排模型解决排序问题
召回阶段为了不漏掉,通常会取更多候选。但候选多了以后,噪声也会增加。重排模型的作用是对 (query, document) 进行更细致的相关性判断。
典型流程:
text
多路召回 Top 30
-> 构造 query-document pair
-> CrossEncoder 打分
-> 按 rerank_score 排序
-> 返回 Top 3 或 Top 5
与向量检索不同,CrossEncoder 通常会同时看问题和文档内容,相关性判断更精细,但计算成本也更高。因此它适合处理召回后的候选集合,而不适合直接对全库文档打分。
6. 召回数量与最终返回数量要分离
一个常见错误是用户要 3 条结果,系统就只召回 3 条。这样没有给重排留下空间。
更合理的方式是:
text
召回数量 = 最终数量的若干倍
最终数量 = 用户真正需要展示的数量
例如最终返回 3 条,可以先召回 15 或 30 条,再重排筛选。具体倍数取决于数据规模、延迟要求和召回质量。
如果召回数量太少,重排没有意义;如果太多,重排成本会上升。调参时要同时看准确率和响应时间。
7. 合并去重决定上下文质量
多路召回一定会产生重复。重复可能发生在:
- 同一 chunk 被多个通道命中。
- 同一文档的多个相邻 chunk 被命中。
- 父子块同时被命中。
- 内容相似的重复文档被命中。
去重粒度要根据业务目标决定:
- 如果回答基于片段,可按 chunk ID 去重。
- 如果推荐的是文档,可按 doc ID 去重。
- 如果要展示来源,可保留最高分片段并关联原文档。
不去重会导致上下文重复,模型窗口浪费,回答也可能啰嗦或偏向某一来源。
8. 混合检索的评估方法
混合检索调优不能只看最终回答。建议建立一组查询样例,并标注期望命中文档。观察:
- Recall@K:正确文档是否进入前 K。
- MRR:第一个正确结果排在多靠前。
- nDCG:排序整体质量。
- 重排前后排名变化。
- 无关文档比例。
- 平均检索耗时。
如果没有标注集,也可以先人工抽样构建几十条高频问题。哪怕样本不大,也比凭感觉调权重可靠。
9. 常见工程风险
混合检索常见风险包括:
- 多通道分数不可比却被直接相加。
- metadata 类型不稳定,过滤表达式失效。
- 文档切分太碎,重排看不到完整语义。
- 重排输入过长,延迟明显增加。
- 召回结果没有来源信息,无法回溯。
- 只优化准确率,忽略响应时间。
- 过滤过早导致正确文档被排除。
这些问题需要通过日志和评估定位。每次检索最好记录:原始 query、改写 query、过滤条件、各通道召回数量、合并后数量、重排分数和最终返回结果。
10. 过滤、召回和重排的顺序
混合检索中,一个关键设计问题是:过滤应该发生在召回前,还是召回后?
如果过滤条件非常明确,例如权限范围、城市、日期、文档类型,通常应在召回前执行。这样可以减少候选范围,提高效率,也避免把用户无权访问的数据带入后续上下文。
如果过滤条件来自模型抽取且不够可靠,可以采用更谨慎的方式:先宽召回,再在合并阶段过滤或二次校验。这样能避免因为参数抽取错误过早排除正确文档。
重排一般发生在候选集合形成之后。它不应该替代权限过滤,也不应该承担硬条件判断。重排负责相关性排序,硬约束仍应由程序逻辑保证。
11. 上线后的调参策略
混合检索上线后,不建议频繁凭感觉改权重。更稳的方式是建立查询样例池:
- 高频真实问题。
- 失败问题。
- 边界问题。
- 业务重点问题。
- 容易混淆的相似问题。
每次修改切分、权重、Top K、重排模型或过滤逻辑,都在样例池上回归。这样可以避免"修好了一个问题,破坏了另一批问题"。
12. 小结
混合检索的核心不是堆组件,而是分工:
text
结构化过滤保证硬条件,稠密向量保证语义召回,稀疏/全文检索保证关键词命中,重排模型保证最终排序。
当系统从 demo 走向业务,检索质量的优化会越来越像搜索系统工程。只有把召回、过滤、去重、排序和评估拆开看,RAG 才能稳定提升。