向量检索不准怎么办?Rerank 重排序一次讲明白:召回之后,再精排一次(2026)

用向量数据库做知识库检索,top 10 里经常混着几条「看着像但不相关」的结果。这篇讲重排序(Rerank):一个专门负责精排的模型层,原理和开源用法都给你备好了。

几个月前写过Embedding 和向量数据库的入门,当时留了个尾巴:向量检索能召回「语义相近」的内容,但召回质量经常不够好,排在第 1 的不一定是真正相关的,top 10 里常有几条凑热闹的。

这个尾巴今天就补上。业界的标准解法是两阶段检索:先用向量召回,再用一个专门的模型精排,第二步就叫 Rerank(重排序)。

先说清楚:为什么向量检索会漏

向量检索的原理是把每段文本压缩成一个向量,查询和文档谁向量夹角小谁排前面。压缩是关键:一段 500 字的文档被压成一个几百维的向量,损失是必然的。

我自己用玩具词表模拟了一遍:查询「苹果 发布 手机」,语义相关的候选「这家公司的新款 iPhone 上市了」词面重叠是 0,另一条「水果店里香蕉到货」反而带着「苹果」两个字。向量模型如果没把「苹果=科技公司」这个语境学好,字面重叠的那条就可能被排到前面。

真实的向量模型比玩具强得多,但问题结构一样:双塔模型各自独立编码,查询和文档之间没有任何交互。「苹果」在查询里指手机厂商还是水果,文档编码时根本不知道查询长什么样。

Rerank 的原理:交叉编码器

Rerank 用的模型叫交叉编码器(Cross-Encoder),和双塔的本质区别只有一个:它不分别算向量,而是把查询和文档拼在一起送进模型,让每个词的注意力直接看到对方的每个词,最后输出一个相关度分数。

代价也在这里。双塔的文档向量可以提前算好存进库,查询来了只算一次;交叉编码器对每个「查询-文档」对都要跑一遍完整模型,100 个候选就是 100 次。所以它不可能拿去搜全库,只配做精排。

Sentence Transformers 官方文档对两阶段的描述是:先用双塔召回 top 100,再用交叉编码器对这 100 条逐对打分重排。这套「召回+精排」管线是目前语义搜索的标配。

开源模型:bge-reranker 怎么用

开源这边最常用的是 BAAI(北京智源)的 bge-reranker 系列。我拉了 bge-reranker-v2-m3 的官方 README,模型是 Apache 2.0 协议,多语言,用法几行:

from FlagEmbedding import FlagReranker
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)

score = reranker.compute_score(['查询', '候选文档'])
print(score)  # 单个分数

README 里自带了一组示例,很能说明问题:查询「what is panda?」,候选「hi」得分 -8.19,候选那段大熊猫的百科介绍得分 5.26。分数没有统一上限下限,比大小就行。也可以加 normalize=True 过一个 sigmoid,把分数压到 0 到 1 之间。

同一个模型在 Hugging Face 上还有 sentence-transformers 的 CrossEncoder 加载方式,model.rank(query, passages) 一行直接返回排好序的结果。

商业 API:Cohere Rerank

不想自己跑模型的话,商用 API 里 Cohere 的 Rerank 是代表。我抓了它官方文档的当前示例,模型名已经是 rerank-v4.0-pro,调用就三个核心参数:query、documents(候选数组)、top_n(要前几条)。返回结果里每条带一个 relevance_score,按分数从高到低排好。

官方示例里那个「美国首府」的例子挺直观:查询「What is the capital of the United States?」,正确答案 Washington, D.C. 那条 relevance_score 0.94,讲「死刑(capital punishment)」的那条只有 0.47。「capital」这个词面撞上了,语义上完全不相关,精排模型把这两条拉开了整整一倍的距离。这正是双塔容易被词面带偏、交叉编码器能掰回来的场景。

在 RAG 里怎么接

把前面说过的知识库流程串起来(RAG 本身是什么之前也单独讲过),加一步就是完整版:

文档切块、向量化、入库(离线做一次)→ 用户提问,向量检索召回 top 20~100(快,粗筛)→ Rerank 精排取 top 3~5(慢一点,但只处理几十条)→ 拼进提示词让大模型回答。

实际接的时候几个参数值得留意:召回数量别太抠,rerank 只能从召回的集合里挑,召回阶段漏了它就永远见不到;精排数量别太贪,top 10 以内通常够用,塞太多反而稀释大模型的注意力。

成本结构也值得算一笔:rerank 模型一般比生成模型小得多(bge-reranker-v2-m3 是 568M 参数级别),对几十个候选跑一遍的开销,远小于把一堆无关文本直接塞进大模型上下文的 token 费。精排这一步经常是省钱而不是花钱。

什么时候值得加这一层

不是所有检索都需要 rerank。我的判断标准很简单:看「召回的条数」和「最终用的条数」之间的落差。

落差大(召回 100 条只用 5 条),rerank 的价值就大,因为它专门负责在候选池里挑最好的那几条;召回 10 条直接全用的小场景,加一层只会拖慢速度。

另外分清楚两个容易混的概念:rerank 改变的是「已有候选的顺序」,它不能凭空变出召回阶段漏掉的文档。召回质量太差时,该优化的是 Embedding 模型和切块策略,rerank 救不了上游。

之前写过RAG、长上下文、微调三种方案怎么选,如果你在搭知识库,rerank 属于成本最低的那档改进:不动生成端、不改向量库,召回和提示词之间插一步就行。先把 top 10 精排到 top 3,很多「大模型答非所问」的问题会自己消失,因为大部分时候不是模型不会答,是喂给它的参考资料本身就不对。

发表评论