这道题其实是在问[[RAG]]系统里,召回之后是不是还需要再做一轮排序,以及这一轮排序到底解决什么问题。我的看法是,[[重排]]不光是可引入,在线上对准确性要求高的场景,它基本是标配。
先说为什么需要重排。向量检索,也就是用 [[Bi-Encoder]] 把 query 和 doc 都压成一个向量,然后用内积去算相似度,这个做法效率很高,但有两个硬伤。一个是表示瓶颈,你把一整段文本压缩成一个向量,很多细粒度的语义信息就丢了。另一个是交互缺失,query 和 doc 从头到尾没有真正碰过,只是最后比了个向量距离,像这种“苹果手机退款政策”和“iPhone退货流程”这种语义相近但表述不同的匹配,它就很难抓住。重排模型就是来补这个 gap 的。
具体到架构,RAG 的检索链路其实是个两阶段流程。第一阶段用 [[ANN]] 做快速召回,比如从几百万文档里捞回 top 100 到 1000,这个阶段追求的是召回率,别把相关文档漏掉。第二阶段,在这个小候选集上,用重排模型做精细排序,把最相关的 top 5 到 10 个送进生成模块。说白了,第一阶段是海选,第二阶段是决赛。
这里有个边界划分的问题。很多人会问,为什么不用重排模型直接做召回?因为重排模型慢。典型的 [[Cross-Encoder]] 模型,比如 BERT-reranker,需要把 query 和 doc 拼接起来过 Transformer,计算量很大,不可能对上百万文档逐一打分。所以向量检索和重排是互补的,一个管效率,一个管精度。真正落地,一定是两者结合,而不是二选一。
举个例子,电商客服场景,用户问“我买的东西降价了,能退差价吗?”。向量检索先从知识库里召回一批相关文档,可能包括“价格保护政策”、“退款流程”、“优惠券使用规则”等等。但这里面有些文档可能只是沾边,比如提到了“价格”两个字但实际讲的是促销活动。这时候重排模型上场,它会仔细比对 query 和每个文档的语义相关性,把真正讲“降价退差价”的文档排到最前面。这样下游的生成模型拿到的上下文质量就高很多,回答也更精准,首问解决率能明显提升。
不过,引入重排也有几个落地风险。第一个前提是重排模型不能太重,否则整个检索链路的延迟会超标。我见过一些团队直接上 BERT-large 做重排,结果延迟从 50ms 飙到 500ms,线上根本扛不住。所以实际中常用的是轻量级模型,比如 MiniLM 或者[[蒸馏]]过的版本,或者用 [[ColBERT]] 这种延迟交互的方案,效果接近 Cross-Encoder 但快很多。第二个坑是输入截断。重排模型通常有长度限制,比如 512 tokens,而很多文档比这个长。如果不做截断策略,直接把文档尾巴切掉,可能会丢掉关键信息。我一般会先对文档做关键片段提取,或者用[[滑动窗口]]把长文档切成多段分别打分,再取最高分。
所以我的倾向是,把重排看成 RAG 系统的标配组件,但不是无脑上。在效果和延迟之间,我会优先用 ColBERT 做第一级重排,快速过滤掉明显不相关的,然后再用 Cross-Encoder 对 top 10 做最终精排。这样既保证了精度,又把延迟控制在可接受范围。
另外,还有一个有意思的点是,重排模型本身也需要持续迭代。比如线上用户点击数据可以用来做 Distillation,把大模型的知识蒸馏到小模型上,这样重排效果会越用越好。但这里有个前提是点击数据要干净,不然容易引入偏置。
总的来说,重排是 RAG 从“能跑”到“好用”的关键一步,但前提是做好效率与精度的平衡。