考察点
面试官想确认你理解「召回快但不准、精排准但慢」这个检索系统的经典矛盾,以及 RAG 里为什么普遍用两阶段架构。后半问考指标思维:Recall 和 Precision 分别在哪一阶段兜底,Top-K 不是拍脑袋常数而是可以按分数分布动态调的。追问一般往 rerank 模型选型与微调、延迟优化、阈值校准走。
参考答案
为什么需要 Rerank
向量召回用的是 bi-encoder 双塔结构:query 和文档各自独立编码成向量,靠向量点积算相似度。好处是文档向量可以离线预计算,线上查询只做一次 query 编码加 ANN 检索,百万级文档也能毫秒返回;代价是 query 和文档之间没有细粒度交互,两个向量压完之后很多匹配细节丢了。
Cross-encoder 正好反过来:把 query 和文档拼成一个序列喂进模型,token 级充分交互后输出相关性分数,精度明显高一截;但它没法预计算,每来一个 query 要对每个候选文档跑一次推理,候选集一大就扛不住。所以标准解法是分工:召回阶段用双塔加混合检索,宁多勿漏地取回 top-50 或 top-100 保住 Recall;精排阶段用 cross-encoder 只对这几十条算分,取 top-3 到 top-5 保住 Precision。几十条候选过一次 bge-reranker-base 级别的模型,单 GPU 上通常几十到几百毫秒,在线上延迟预算内。
常用 rerank 模型有 bge-reranker-v2-m3(多语言、和长文本兼容好)、Cohere Rerank(API 省心)、Jina reranker 等。预算充足也可以用 LLM 做 listwise rerank,但成本和延迟高一两个量级,一般不值得。
Recall@K 和 Precision@K 怎么取舍
这两个指标在 RAG 里不是同一个阶段的事,谈不上二选一。召回阶段的目标是 Recall:标准答案所在的 chunk 只要进了候选集,后面就还有救,所以这一阶段的 K 要放宽,并用召回命中率(hit rate)来监控。精排之后进入 LLM 上下文的是最终 K,这个阶段要 Precision:塞进去的 chunk 多一条噪声,生成质量就掉一分,还有 lost in the middle 问题——上下文越长,中间部分的信息越容易被模型忽略。所以取舍原则是:召回侧放宽、精排后收紧,中间用 rerank 做桥梁。最终进上下文的 K 一般在 3-10 之间,再大的收益迅速衰减,还烧 token。
Top-K 怎么动态调整
固定 K 的问题在于不同 query 的相关文档数量天差地别:事实型问题可能只有 1 条文档真正相关,取 5 条就是引入 4 条噪声;综述型问题「对比一下 A 和 B 的优劣」可能需要 8-10 条才够。动态调整几个实用手段:
- 分数阈值截断:rerank 输出的是可比的相关性分数,设一个下限(比如低于某分位或绝对阈值的直接丢弃)。阈值要在自己的数据上校准,rerank 模型的分数分布随领域漂移,别照抄别人博客里的数字。
- 分数落差截断(score gap):排序后找相邻分数的最大落差,在落差处下刀——前 3 条分数 0.9、0.87、0.85,第 4 条掉到 0.4,就取 3。
- 按 query 类型路由:先用分类器或 LLM 判断 query 意图,事实型给 K=3,列举/综述型给 K=8。
- 上下文预算反推:按剩余 token 预算和 chunk 平均长度算能塞几条,配合分数排序从高往低装。
工程上这几个手段通常组合用:阈值兜底、落差截断、预算封顶。
可能的追问
- Rerank 模型怎么选、要微调吗?答:先用 bge-reranker 这类开源模型在评测集上测 nDCG 或命中率提升,够好就不微调;垂直领域术语多时用召回难例(召回 top-K 里不相关的)做负例微调 cross-encoder,提升比较明显。
- Rerank 成为延迟瓶颈怎么办?答:换更小的模型(reranker 从 large 换 base)、蒸馏、降低召回候选数、对高频 query 的 rerank 结果做缓存,或者干脆只对分数边界模糊的候选精排。
- 没有标注数据怎么定阈值?答:先离线画出 rerank 分数分布,按召回侧 hit rate 和端到端答案质量两个指标扫阈值;上线后用人工抽检校准。