Reranker 上线评估生产实战:用 Hard Negatives、NDCG@10 与 Shadow Re-ranking 守住检索质量
执行摘要
本轮主题聚焦 Reranker 的生产评估与安全发布。核心不是再讨论 Embedding、向量索引或 RAG 上下文组织,而是回答一个更具体的问题:第一阶段 Retriever 已经能召回候选之后,怎样判断一个新的 Cross-Encoder / Reranker 是否真的值得上线,以及怎样避免”离线指标上涨、线上搜索反而变差”。
一句话总结:把 Reranker 当生产系统来治理——固定回归数据、分层门禁、Shadow 验证,而不是只看一个漂亮的离线分数就切流量。
为什么 Reranker 最容易出现”离线很好、线上翻车”
现代搜索和 RAG 检索通常不是一次排序完成,而是两阶段甚至多阶段:
- 第一阶段(Retriever):使用 BM25、稠密向量或混合检索快速召回几十到几百个候选;
- 第二阶段(Reranker):使用更昂贵的 Cross-Encoder 对候选重新排序。
这种架构的好处很明显:把昂贵模型限制在小候选集上,用可控的成本换取更好的相关性。Vespa 的 phased ranking 也是同一思路——便宜的一阶段处理更大集合,复杂模型只对 top candidates 做 second/global phase,并通过 rerank-count 一类参数对计算量设置硬上限。
问题在于,Reranker 的质量高度依赖它看到的候选分布。一个模型在”正样本 + 随机无关文档”上表现很好,并不代表它能区分真实线上最困难的候选。例如用户搜索”苹果退款多久到账”,真正难分的负样本可能是”Apple Pay 退款""App Store 退款政策""银行卡退款到账时间”,而不是一篇完全无关的体育新闻。
因此,生产 Reranker 的评估重点应该从”模型会不会判断相关”转成:
它能不能在真实 Retriever 已经挑出来的相似候选里,把真正有用的文档稳定地排到前面。
核心一:Hard Negatives 必须来自真实第一阶段 Retriever
随机负样本会高估模型能力
随机采样的负文档通常词面、主题和语义都离查询很远,模型很容易区分。真正有价值的负样本应该是 Hard Negatives:它们被第一阶段检索器打了高分,却被人工标注为不相关或只部分相关。
Sentence Transformers 的 CrossEncoder 训练与评估文档专门提供了 mine_hard_negatives(),并建议将挖出的候选直接交给 CrossEncoderRerankingEvaluator。这类数据有一个重要优势:不仅能看新 Reranker 的排序效果,还能对比 Base Retriever → Reranked 的增益。
生产数据集建议至少保留以下字段:
{
"query_id": "q-10293",
"query": "如何修改企业发票抬头",
"retriever_version": "hybrid-v7",
"candidates": [
{"doc_id": "d1", "retriever_rank": 1, "label": 3},
{"doc_id": "d2", "retriever_rank": 2, "label": 1},
{"doc_id": "d3", "retriever_rank": 3, "label": 0}
]
}
其中 label 最好不是简单的 0/1,而是与业务一致的分级相关性,例如:
| Label | 含义 |
|---|---|
| 3 | 直接解决问题 |
| 2 | 部分相关 |
| 1 | 弱相关 |
| 0 | 无关 |
只有分级标注才适合使用 NDCG 一类支持 graded relevance 的指标。
不要偷偷把”本来没召回的正样本”塞进候选集
这是一个很容易被忽略的评估陷阱。CrossEncoderRerankingEvaluator 默认可以把 positive 文档强制加入 reranking 集合,这会让评估信号更稳定,但也可能形成一个比真实线上更乐观的上限——因为生产环境里 Reranker 只能重排 Retriever 已经召回的内容。
因此建议同时保存两组指标:
- Oracle rerank:确保 positive 在候选中,衡量 Reranker 自身排序能力;
- End-to-end rerank:严格使用 Retriever 原始 top-K,衡量真实系统效果。
如果 Oracle 很高而 End-to-end 很低,通常问题不在 Reranker,而在第一阶段召回。
核心二:用 NDCG@10 做主门禁,但不要只看一个平均数
Sentence Transformers 的 CrossEncoder Reranking Evaluator 会计算 MRR@K、NDCG@K、MAP,其 NanoBEIR evaluator 默认主指标就是 NDCG@10。Elasticsearch 的 _rank_eval API 同样支持 reciprocal rank、precision、discounted cumulative gain 等经典 IR 指标。
对于有多级相关性标注的搜索系统,NDCG@10 很适合作为主指标,因为它同时考虑:
- 高相关文档是否出现;
- 高相关文档是否排得足够靠前;
- 位置越靠后,收益按折扣衰减。
但生产门禁不能只有一个”全量平均 NDCG@10”。至少要按下面的切片分别观察:
- 查询语言:中文、英文、中英混合;
- 查询长度:短关键词、自然语言问题、长问题;
- 业务域:售后、产品、合同、技术、政策等;
- 新鲜度:近期新增文档与长期稳定文档;
- 难度:有唯一答案、多文档答案、歧义查询;
- Retriever 类型:BM25、dense、hybrid;
- 候选规模:top-20、top-50、top-100 等。
平均值可能掩盖严重回退。一个模型可能总体提升 2%,但在最重要的”中文售后问题”切片下降很多,这种版本不应该直接进入全量生产。
核心三:候选数量是质量、延迟和成本之间的第一控制旋钮
Reranker 并不是候选越多越好。Cohere 当前 Rerank API 接收 query + documents 并返回排序后的结果和 relevance_score,官方文档明确建议不要在一次请求中发送过多文档;长文档还可能受 max_tokens_per_doc 影响而截断。同时,相关性分数是 query-dependent 的,不能把不同查询之间的绝对 score 当成统一概率解释。
这带来三个生产结论:
1. candidate_k 必须版本化
不要只记录 reranker_model=v4,还要记录完整 pipeline:
retrieval_pipeline:
retriever_version: hybrid-v7
candidate_k: 100
reranker_version: rerank-v4
output_k: 10
max_tokens_per_doc: 4096
同一个 Reranker 在 top-20 和 top-200 候选集上的延迟与质量都可能完全不同。
2. 长文截断要单独监控
如果文档超过模型输入限制,系统可能截断、切块或执行多次推理。不能只监控请求总延迟,还应统计:
truncated_document_rate;- avg/max tokens per candidate;
- chunks per document;
- 每个查询实际参与 rerank 的候选数。
3. 不要用固定 relevance score 直接做全局阈值
一个查询的 0.8 和另一个查询的 0.3 不能简单比较成”前者更相关”。如果业务确实需要做相关性过滤,应在自己的代表性 query/document 样本上做阈值校准。
核心四:上线前增加 Shadow Re-ranking,而不是直接 5% 用户灰度
传统 Canary 会让一小部分真实用户直接看到新版本结果。对排序系统来说,这一步仍然太靠前,因为排名变化可能立即影响搜索点击、知识引用和最终答案。
更稳妥的方法是先做 Shadow Re-ranking:
User Query
|
+--> Retriever --> Production Reranker --> 返回给用户
|
+--> Shadow Reranker --> 只记录,不返回
Shadow 路径应该复用同一份 query 和 candidate list,这样差异才能归因于 Reranker,而不是 Retriever。建议记录以下 diff 指标:
| 指标 | 含义 |
|---|---|
shadow_top10_overlap | Top-10 重叠度 |
shadow_rank_correlation | 排序相关性 |
shadow_top1_changed_rate | Top-1 变化率 |
shadow_relevant_doc_promote_rate | 相关文档提升率 |
shadow_relevant_doc_demote_rate | 相关文档降级率 |
shadow_p95_latency | P95 延迟 |
shadow_timeout_rate | 超时率 |
shadow_truncation_rate | 截断率 |
需要特别强调:Shadow 本身不能证明质量提升。如果没有人工 relevance label,你只能看到”排序发生了什么变化”,不能自动知道”变化是不是更好”。
因此最有效的做法是把 Shadow 中高差异查询自动沉淀成下一批人工标注集:
- 线上 Shadow 找出 Production 与 Candidate 差异最大的查询;
- 采样这些 query + candidate pairs;
- 人工做 graded relevance;
- 加入 regression dataset;
- 下一版本继续离线门禁。
这会形成一个真正能持续进化的 Reranker 回归测试闭环。
工程落地:一套可执行的四层发布门禁
Gate 1:数据门禁
发布包必须绑定固定的数据集版本:
evaluation_dataset:
version: search-regression-2026-08-30
query_count: <recorded-value>
label_schema: graded-0-3
retriever_snapshot: hybrid-v7
hard_negative_source: production-top-k
禁止只保留一个不断覆盖的 latest.csv。
Gate 2:相关性门禁
至少比较:
- Base Retriever NDCG@10;
- Production Reranker NDCG@10;
- Candidate Reranker NDCG@10;
- MRR@10;
- 各核心业务切片 delta。
上线条件应该写成”Candidate 不允许核心切片回退超过约定阈值”,而不是只要求总体平均提升。
Gate 3:性能门禁
使用与生产一致的 candidate_k、文档长度分布和并发模型,测量:
- P50 / P95 / P99 latency;
- timeout / 429 / 5xx;
- tokens/doc;
- candidates/query;
- batch size;
- GPU/CPU 利用率;
- 单查询 rerank 成本。
Gate 4:Shadow 门禁
Shadow 阶段不改变用户结果,但要确认:
- 新模型没有明显错误率上升;
- 长文与多语言请求没有系统性异常;
- Top-K 排名变化符合预期;
- 极端 diff 查询完成抽样人工复核。
通过后再进入真正的 Canary。
适用场景
这套方法尤其适合以下系统:
- 企业知识搜索;
- RAG 的第二阶段检索排序;
- 电商商品搜索;
- 客服知识库;
- 代码与 API 文档检索;
- 多语言内容搜索;
- Hybrid Search 后的 Cross-Encoder reranking。
如果你的系统只有几十篇文档、查询量很小,并且可以人工检查全部结果,则没有必要建立复杂 Shadow 平台;但 Hard Negatives + 固定回归集 + NDCG 切片 仍然值得保留。
常见误区
| 误区 | 正确认知 |
|---|---|
| 只看 Reranker 自己的 benchmark | 公开 benchmark 能了解模型能力,但不能替代你自己的生产 query distribution |
| Reranker 分数更高,就代表结果更可信 | score 的主要用途是同一查询内排序,不要未经校准就包装成”可信度百分比” |
| 只测模型,不固定 Retriever | 候选集变化会导致测的不是纯 Reranker 差异,离线 A/B 必须固定 Retriever snapshot |
| 只看质量,不测尾延迟 | Cross-Encoder 计算复杂度远高于第一阶段,必须把昂贵计算限制在可预测范围 |
| Shadow 流量越大越好 | Shadow 有额外成本,应优先采样高价值、高风险和分布代表性查询 |
上线检查清单
- Retriever 版本与 candidate_k 已冻结并记录;
- 数据集包含来自真实 Retriever 的 Hard Negatives;
- Oracle rerank 与 End-to-end rerank 指标分开;
- NDCG@10、MRR@10 已按业务域和语言切片;
- 新模型的长文截断策略已验证;
- relevance score 没有被误当成跨查询统一概率;
- P95/P99 latency、timeout、成本已压测;
- Shadow 请求不会阻塞用户主链路;
- Shadow 高差异样本进入人工复核;
- Production / Candidate 可以快速切换;
- 回滚不依赖重新构建索引;
- 评估数据、模型版本、参数和结果都可以追溯。
参考资料
- Sentence Transformers — CrossEncoder Evaluation:https://sbert.net/docs/package_reference/cross_encoder/evaluation.html
- Sentence Transformers — CrossEncoder Training Overview / Hard Negative Mining:https://www.sbert.net/docs/cross_encoder/training_overview.html
- Cohere — Rerank API v2:https://docs.cohere.com/reference/rerank
- Cohere — Best Practices for using Rerank:https://docs.cohere.com/docs/reranking-best-practices
- Elasticsearch — Ranking evaluation API:https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval
- Vespa — Phased Ranking:https://docs.vespa.ai/en/ranking/phased-ranking.html
- Thakur et al. — BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models:https://arxiv.org/abs/2104.08663