检索召回了 30 条,真正塞进上下文的只有 5 条。选哪 5 条,就是重排要解决的问题。
可复现前提: 接续上一节的混合检索结果。重排模型用的托管 API,脚本 bench/rerank。
为什么需要重排
检索阶段的排序依据是向量相似度和词频,它们都不理解"这段到底能不能回答这个问题"。重排模型会把 query 和每个候选段一起送进去做交叉打分,准确得多——代价是慢。
实测
召回 30 条,重排后取 5 条:
| 配置 | top-1 命中 | recall@5 | 端到端 P95 |
|---|---|---|---|
| 不重排 | 0.64 | 0.95 | 120ms |
| 重排 top-30 → 5 | 0.75 | 0.96 | 160ms |
| 重排 top-100 → 5 | 0.76 | 0.96 | 310ms |
召回 30 条送重排是甜点位。 扩到 100 条只多 1 个点,延迟翻倍。
top-1 为什么重要
recall@5 只涨了 1 个点,看起来重排没什么用。但生成质量的实际差异很大——模型有明显的位置偏好,排在第一位的片段对答案影响最大。我们做过一次盲评:重排后的答案在 120 条里有 71 条被评为更好,只有 18 条更差。
一个必须做的优化:缓存
同一个 query 反复问是常态。给重排结果加一层 5 分钟的缓存,线上重排调用量降了 43%。
key = hashlib.sha256(f"{query}|{','.join(doc_ids)}".encode()).hexdigest()注意 key 要包含候选文档的 id 列表——文档更新后候选集会变,只用 query 做 key 会返回过期结果。这个坑我们踩过。