加了 reranker 却没变好,原因在这里
你加了重排序这一步,延迟涨了,答案还是原来那些。这个结果很常见,而且几乎从来不是因为 reranker 不行。绝大多数情况下,模型正常工作,只是输入让它的工作失去了意义。按顺序排查下面几条 —— 前两条能解释大部分失败。
一句话诊断:reranker 只能对检索交给它的东西重新排序。如果正确的段落根本不在候选列表里,再好的 reranker 也变不出来 —— 而把错误答案换个顺序,不会带来任何可测量的变化。
1. 检索压根没返回正确的文档
这是最常见的单一原因。重排序是精度环节,它修不了召回。如果向量检索完全漏掉了相关分块,reranker 只会老老实实地对一堆都答非所问的文档排序。
动 reranker 之前先直接测一下。取 20 条你已知正确文档的查询,按平时的 top-k 检索,然后只看命中与否:
hits = 0
for q, gold_id in labelled:
candidates = retriever.search(q, k=50)
if gold_id in [c.id for c in candidates]:
hits += 1
print(f"recall@50 = {hits / len(labelled):.2f}")
如果这个数低于 0.8 左右,就别再调 reranker 了 —— 你在错误的环节上使劲。先修检索:放大 k、改进分块,或者加上关键词匹配。把 BM25 和向量结合起来的混合检索,对召回的提升通常比换任何 reranker 都大,因为这两者失败的查询类型不同。
2. 候选池太小
对前 5 条重排再保留 5 条,等于什么也没做 —— 没有任何有意义的顺序可调。reranker 需要发挥空间:先宽召回,再排序,最后截断。
| 召回 | 重排后保留 | 效果 |
|---|---|---|
| 5 | 5 | 无效 —— 同一批内容,顺序略有不同 |
| 10 | 5 | 微弱;reranker 只有 5 个备选位可以提拔 |
| 50 | 5 | 典型的有效配置 |
| 100–200 | 5–10 | 质量最好;注意延迟与成本 |
召回 50 保留 5,reranker 就有 45 个候选可以提拔进最终答案,提升正是从这里来的。放大候选池对账单的影响可以用我们的成本计算器算 —— 通常比大家担心的小,而且在按次计费下是阶梯式而非线性上涨。
3. 段落被静默截断了
每个 cross-encoder 都有输入长度上限,而且查询和段落共用这个额度。超了,段落尾部就会被切掉 —— 而那往往正是真正回答问题的部分。不会报任何错,只是分数变得没有意义。
经典的 MiniLM 系 reranker 对「查询+段落」整体上限是 512 token。现在的托管模型宽裕得多 —— Cohere Rerank 4 和 Voyage rerank-2.5 都是 32,000 —— 所以换个模型就能直接解决。如果你自托管的是 512 token 的模型,先看看你的段落实际落在哪个区间:
lengths = [len(tok.encode(d)) for d in passages]
over = sum(1 for n in lengths if n > 480) # leave room for the query
print(f"{over}/{len(lengths)} passages will be truncated")
这个现象可以在 Demo 里直接看到:段落超过所选 max_length 时它会警告,你也能看到把长段落缩短后分数是怎么变的。
4. 分块太大,排不动
即便没超长度上限,大分块也会稀释信号。一篇 2000 词、只提了一次你关心主题的页面,在模型看来大部分内容都是在讲别的事。它的相关性分数会落在中间,低于一个通篇切题的短段落。
这和截断是相反的失败模式,解法也相反:把分块改小。200–400 token 加一点重叠,是重排序场景下比较合理的起点。如果生成时需要上下文,可以先用小块检索和排序,再在放进 prompt 前扩展到它所属的父级章节。
5. 语言或领域不匹配
把只支持英文的 reranker 用在中文、德文或混合语言内容上,它照样会给出分数,但那基本是噪声。确认你选的模型确实覆盖你的语言 —— bge-reranker-v2-m3 和 Qwen3-Reranker 覆盖 100+ 种,而好几个很强的英文模型只覆盖一种。
领域的影响更隐蔽。代码、法律条款、临床记录都会让基于网页文本训练的模型失灵,因为标志「相关」的词汇体系不一样。如果你的内容属于这几类,先换一个适配该领域的模型再测,别急着下「重排序没用」的结论。Demo 里的代码检索场景就能看出,通用模型对精确函数名和对同义改写的处理差别有多大。
6. 误读了分数
这里有两个常见误区:
- 把分数当概率。大多数 cross-encoder 输出的是无界 logit,而不是校准过的 0–1 相关度。分数 3.2 不代表「76% 相关」。只有顺序是有意义的。
- 跨模型或跨查询比较分数。A 模型的 0.4 和 B 模型的 0.4 之间没有任何可比性;即使同一个模型,两个不同查询下的分数也不可比。如果你需要一个绝对阈值,请在自己的标注数据上校准 —— 别直接抄博客里的数字。
如果你在用 score > 0.5 之类的条件过滤、结果却总是空的,原因就在这里。
7. 你看不见提升
有时重排序确实在起作用,只是你的测量方式不够灵敏,看不出来。检查两点:
- 你测的是排序,还是最终答案?如果在排序一般的情况下大模型本来就能答对,那么排序变好不会改变输出 —— 改变的是你的 token 账单。这仍然是实打实的收益,只是不在你盯着的那个指标上。
- 你的评测集够大吗?10 条查询区分不出 5% 的提升和噪声。30 到 100 条标注查询是通常的下限 —— 参见如何评测 reranker 中的 NDCG@k 与 MRR,它们能捕捉到精确匹配检查发现不了的顺序变化。
排查清单
- 检索器的 recall@50 高于约 0.8
- 召回量至少是保留量的 5–10 倍
- 加上查询后,没有段落超过模型的输入上限
- 分块是 200–400 token,而不是整篇文档
- 模型覆盖你的语言,且在你的领域表现正常
- 你是按分数排序,而不是拿未校准的数字设阈值
- 你在 30 条以上标注查询上测 NDCG 或 MRR,而不是凭感觉
如果每一条都满足了,重排序仍然没有提升,那本身就是一个有效结论:对你的查询而言检索已经足够好,可以砍掉这一步、把延迟省下来。这比留着一个默默不起作用的 reranker 要好。