加了 reranker 却没变好,原因在这里

排障 · 约 9 分钟阅读 ·

你加了重排序这一步,延迟涨了,答案还是原来那些。这个结果很常见,而且几乎从来不是因为 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 需要发挥空间:先宽召回,再排序,最后截断。

召回重排后保留效果
55无效 —— 同一批内容,顺序略有不同
105微弱;reranker 只有 5 个备选位可以提拔
505典型的有效配置
100–2005–10质量最好;注意延迟与成本

召回 50 保留 5,reranker 就有 45 个候选可以提拔进最终答案,提升正是从这里来的。放大候选池对账单的影响可以用我们的成本计算器算 —— 通常比大家担心的小,而且在按次计费下是阶梯式而非线性上涨。

3. 段落被静默截断了

每个 cross-encoder 都有输入长度上限,而且查询和段落共用这个额度。超了,段落尾部就会被切掉 —— 而那往往正是真正回答问题的部分。不会报任何错,只是分数变得没有意义。

经典的 MiniLM 系 reranker 对「查询+段落」整体上限是 512 token。现在的托管模型宽裕得多 —— Cohere Rerank 4Voyage 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-m3Qwen3-Reranker 覆盖 100+ 种,而好几个很强的英文模型只覆盖一种。

领域的影响更隐蔽。代码、法律条款、临床记录都会让基于网页文本训练的模型失灵,因为标志「相关」的词汇体系不一样。如果你的内容属于这几类,先换一个适配该领域的模型再测,别急着下「重排序没用」的结论。Demo 里的代码检索场景就能看出,通用模型对精确函数名和对同义改写的处理差别有多大。

6. 误读了分数

这里有两个常见误区:

如果你在用 score > 0.5 之类的条件过滤、结果却总是空的,原因就在这里。

7. 你看不见提升

有时重排序确实在起作用,只是你的测量方式不够灵敏,看不出来。检查两点:

排查清单

  • 检索器的 recall@50 高于约 0.8
  • 召回量至少是保留量的 5–10 倍
  • 加上查询后,没有段落超过模型的输入上限
  • 分块是 200–400 token,而不是整篇文档
  • 模型覆盖你的语言,且在你的领域表现正常
  • 你是按分数排序,而不是拿未校准的数字设阈值
  • 你在 30 条以上标注查询上测 NDCG 或 MRR,而不是凭感觉

如果每一条都满足了,重排序仍然没有提升,那本身就是一个有效结论:对你的查询而言检索已经足够好,可以砍掉这一步、把延迟省下来。这比留着一个默默不起作用的 reranker 要好。

直接观察这些失败模式

粘贴你自己的段落,把它改短、改长 —— 亲眼看看分数从什么时候开始失去意义。

打开在线 Demo →

继续阅读