字节面试官:"向量检索打分最高的文档,凭什么不直接给大模型?你中间那层 Rerank 到底在干嘛?"
大家好,我是墨圆。
昨天有个训练营学员跟我复盘他的面试,说前面聊得都挺顺利,结果在 RAG 检索这块栽了。
面试官问他:”你们 RAG 系统检索完之后,直接把 Top-5 喂给大模型吗?”
他说是的,向量检索取相似度最高的 5 条。
面试官追了一句:”那你有没有想过,相似度最高的文档,就一定是回答用户问题最好的文档吗?”
他愣了一下,说应该是吧,毕竟向量距离最近。
面试官笑了:”你去药店买感冒药,货架上摆了二十种感冒药,离你最近的那盒就是最适合你的吗?”
然后话锋一转:”你听过 Rerank 吗?为什么业界主流的 RAG 系统都要做两阶段检索?”
他摇了摇头。
这个场景,我在训练营里见过太多次了。很多人做 RAG 项目,从头到尾就一条路:Embedding 编码 → 向量检索 → 取 Top-K → 扔给 LLM。整个链路看起来很通顺,但实际上中间缺了最关键的一环——精排。
今天就把这个问题彻底讲清楚:向量检索(Recall)和重排序(Rerank)到底各自在干什么?为什么一个不够,非得分两步走?
简要回答
一句话说清楚:向量检索负责”从海量文档中快速捞出一批大致相关的候选”,Rerank 负责”从这批候选中精挑细选出真正能回答问题的那几条”。
这两步解决的是完全不同层面的问题。向量检索追求的是速度和覆盖率——百万级文档库里,毫秒级找出几十条跟用户问题沾边的内容。但”沾边”不等于”精准”,向量检索经常会把一堆”话题相关但答案无关”的噪声文档捞上来。
Rerank 干的事情就是在这几十条候选里做精细化的相关性判断,把那些真正包含答案的文档挑出来、排到前面,把噪声文档压下去。最终只有排名最靠前的 3-5 条被送进大模型的上下文窗口。
为什么不能只用一步?因为能做到”又快又准”的检索方案,目前还不存在。快的不够准,准的不够快,所以工业界普遍采用的是”先粗筛再精排”的两阶段架构。这个思路其实不是 RAG 发明的,搜索引擎、推荐系统早就这么干了,RAG 只是把同样的范式搬过来用而已。
详细解释
向量检索为什么”快但不够准”?
理解两阶段架构,先得理解向量检索为什么有天然的精度上限。
向量检索用的是 Bi-Encoder(双塔模型):query 和文档各自独立通过一个编码器,各得到一个向量,然后算余弦相似度。
注意这里的关键词是”各自独立“——编码 query 的时候模型看不到文档,编码文档的时候模型也看不到 query。两边互不知道对方长什么样,只是各自压缩成一个固定维度的向量,最后比一比距离。
这就好比两个人各写一篇自我介绍,然后让第三个人看两篇介绍来判断他俩合不合适。介绍写得再好,也不如让两个人面对面聊一聊来得准确。
这种架构的优势是效率极高。文档的向量可以提前算好存在数据库里,用户提问时只需要算一次 query 向量,然后在已有的向量索引上做近邻搜索,百万量级可以做到毫秒级响应。
但代价就是精度有上限。因为 query 和文档之间没有任何直接交互,模型能捕捉到的只是”这两段文本在讨论相似的话题”,而无法判断”这个文档是否真正回答了用户的具体问题”。
举个实际的例子。用户问”员工年假最多能攒几天”,向量检索可能召回这些文档:
-
“年假是员工的基本权利,公司应依法保障” -
“年假天数根据工龄确定,详见下表” -
“年假可以跨年累积,但最多不超过 10 天” -
“请假审批流程见 HR 系统操作手册”
这四条文档都跟”年假”这个话题强相关,向量距离都很近。但真正回答了”最多能攒几天”这个具体问题的,只有第三条。其他三条是典型的”话题命中、答案未命中”——它们制造的不是价值,而是噪声。
如果你不加精排直接把 Top-4 扔给大模型,LLM 拿着四条里三条不相关的上下文去生成答案,大概率会掺杂幻觉,或者给出一个模棱两可的回复。
Rerank 凭什么更准?
Rerank 模型用的是 Cross-Encoder(交叉编码器),跟 Bi-Encoder 的设计思路正好相反。
Cross-Encoder 的做法是:把 query 和文档直接拼成一个整体,一起送进 Transformer。模型在每一层注意力计算中都能同时看到 query 的每个词和文档的每个词,让它们充分交互,最终输出一个 0 到 1 之间的相关性打分。
回到刚才的比喻——这次不是看自我介绍了,而是让两个人坐在一起面对面聊,观察他们的互动,然后再判断合不合适。精度自然高得多。
Cross-Encoder 能做到什么 Bi-Encoder 做不到的事?它能理解问题和答案之间的对应关系。它不只是看”这段文档也在聊年假”,而是能判断”用户问的是累积上限,这段文档里确实提到了具体天数”。
但 Cross-Encoder 有一个致命的效率问题:每一对 (query, doc) 都需要单独做一次完整的前向计算。如果文档库有 100 万条,每来一个查询就得跑 100 万次 Cross-Encoder 推理,延迟完全不可接受。
这就决定了 Cross-Encoder 只能用在候选集已经被缩小之后——先让 Bi-Encoder 从 100 万条里捞出 50 条,再让 Cross-Encoder 对这 50 条做精排。这就是两阶段的来历。
两阶段架构长什么样?
整个流程可以拆成三步:
第一步:粗召回。 用 Bi-Encoder(向量检索)或者混合检索(向量 + BM25 关键词)从全量文档中快速拿到 Top-30 或 Top-50 的候选集。这一步追求的是覆盖率——宁可多捞一些噪声,也不能漏掉真正有用的文档。
第二步:精排。 用 Cross-Encoder 对候选集中的每一条文档重新打分排序,取 Top-3 或 Top-5。这一步追求的是精准度——把噪声压下去,把真正包含答案的文档推上来。
第三步:送入 LLM。 把精排后的 Top-K 文档作为上下文,拼进 Prompt 里,让大模型基于这些高质量的参考资料来生成回答。
来看一段简化的伪代码,帮你把整个流程串起来:
deftwo_stage_retrieve(query, doc_store, reranker, recall_k=50, final_k=5):# 第一阶段:粗召回,求快求全 candidates = doc_store.vector_search(query, top_k=recall_k)# 第二阶段:精排,求准求精 scored = reranker.score(query, [c.text for c in candidates]) scored.sort(key=lambda x: x["score"], reverse=True)# 阈值过滤:分数太低的宁可不要 final = [s for s in scored[:final_k] if s["score"] > 0.45]return final # 送给 LLM 的高质量上下文
注意最后有一步阈值过滤。这是很多人忽略的细节:Rerank 打完分之后,即使取了 Top-5,如果这 5 条的相关性分数都很低,说明知识库里大概率没有相关内容。这时候硬着头皮把低质量文档喂给 LLM,只会制造幻觉。正确的做法是设一个分数下限,低于这个阈值的文档直接丢弃,哪怕最终一条都不剩,也比让大模型基于垃圾上下文编造答案要好。
面试里常见的追问和坑
这道题面试官特别喜欢连环追问,因为它的延伸空间很大。几个高频追问你要提前准备好:
“Rerank 计算成本这么高,线上扛得住吗?”
回答的核心思路是:Rerank 只对几十条候选做精排,不是对全量文档。一次 Cross-Encoder 推理几十条文档,耗时大概在 50-200ms 级别,在大多数业务场景中是可接受的。如果要进一步优化,可以做 batch 推理、模型量化,或者选用更轻量的 Rerank 模型(比如 MiniLM 系列的 Cross-Encoder)。
“粗召回阶段要召回多少条?”
没有标准答案,取决于业务场景。一般来说 20-100 条是常见范围。召回太少可能把正确文档漏掉,召回太多又会增加 Rerank 的计算负担。实际项目里通常通过离线实验来找这个平衡点——用标注数据跑不同的召回数量,看 Rerank 之后的 Precision@K 在哪个点达到性价比最优。
“不用 Cross-Encoder 行不行?有没有更轻的方案?”
有。ColBERT 是一种”迟交互”架构,介于 Bi-Encoder 和 Cross-Encoder 之间。它把 query 和文档分别编码成多个 token 级别的向量(而不是压缩成一个),然后在匹配阶段做 token 级别的相似度计算。精度接近 Cross-Encoder,但因为文档端的 token 向量可以预先计算,速度比 Cross-Encoder 快很多。Jina-ColBERT-v2 就是这类方案的代表。
“Embedding 模型和 Rerank 模型需要配套选吗?”
建议同系列搭配。比如 BGE-M3 做 Embedding + BGE-Reranker 做精排,因为它们训练时的数据分布和语义空间更一致。当然也不是说不同系列就不能混用,只是同系列搭配通常效果更稳,调参成本也更低。
一个容易翻车的认知误区
很多人以为”加了 Rerank 效果就一定变好”。不一定。如果你的粗召回阶段本身就没把正确文档捞上来,Rerank 再怎么排也排不出好结果——它只能对已有候选重新排序,不能凭空变出新文档。
所以两阶段架构的前提是,第一阶段的召回率要足够高。 如果你发现加了 Rerank 效果没有明显提升,第一个应该排查的不是 Rerank 模型的选择,而是粗召回阶段是不是有遗漏。比如要不要加混合检索(向量 + BM25),要不要做 query 改写和扩写来增加召回覆盖面。
换句话说,Rerank 提升的是精度(Precision),但前提是召回率(Recall)已经有保障。先保证”正确文档在候选集里”,再用 Rerank 把它排到前面。顺序不能反。
写在最后
两阶段检索的核心逻辑其实非常朴素:先用便宜的方法捞一大网鱼,再用精细的方法从里面挑最好的几条。
搜索引擎这么干了二十年,推荐系统这么干了十年,RAG 不过是把同样的范式搬到了知识问答的场景里。理解了这个底层逻辑,面试的时候不管面试官怎么追问,你都不会慌——因为你知道每一步为什么存在、解决什么问题、有什么取舍。
很多人觉得 RAG 就是”Embedding + 向量搜索 + LLM”,三板斧下去就完事了。但实际上,工业级的 RAG 系统,光检索这一个环节就有粗召回、精排、阈值过滤、多路融合这么多层。每一层都有存在的理由,每一层的缺失都会直接反映在最终的回答质量上。
下次面试官再问你”为什么要分两阶段检索”,别再说”业界都这么做”了。把 Bi-Encoder 的精度上限、Cross-Encoder 的效率瓶颈、以及两者互补的架构逻辑讲清楚,面试官自然知道你是真懂还是背答案。
点个关注,学习更多大模型知识!
夜雨聆风