首页 › AI Agent › Agent100道面试题 › RAG › Re-ranking 的原理与实现:Cross-Encoder vs Bi-Encoder

Re-ranking 的原理与实现:Cross-Encoder vs Bi-Encoder

难度:中级 分类:RAG​

简短回答​

Bi-Encoder 将查询和文档独立编码为向量,速度快但精度有限,用于第一阶段的大规模检索(Retrieval)。Cross-Encoder 将查询和文档拼接后联合编码,精度高但速度慢,用于第二阶段的精排(Reranking)。生产 RAG 系统的标准架构是"Bi-Encoder 检索 Top-100 → Cross-Encoder 重排 Top-5",这种两阶段流水线在效率和精度间取得最佳平衡。​

详细解析​

为什么需要重排序?​

第一阶段检索(Bi-Encoder)从百万级文档中快速找出 Top-K 候选,但存在精度损失:​

•

Bi-Encoder 必须将文档的所有可能含义压缩进单一向量,信息不可避免地丢失​

•

Bi-Encoder 编码文档时没有查询的上下文信息(因为查询还未到达)​

•

结果排序可能不够精确​

重排序用更精确的模型对候选结果重新打分,确保送入 LLM 的上下文是最相关的。​

Bi-Encoder vs Cross-Encoder​

​

飞书文档 - 图片

​

Bi-Encoder 独立编码、向量可预计算,故快而粗;Cross-Encoder 逐对联合编码、完整推理,故慢而精——这是两阶段检索的分工依据。​

​

#LmoSxHPBAZMA text { white-space: pre; }#LmoSxHPBAZMA .fb0 { stroke: var(--N900-FG, 0.15); }#LmoSxHPBAZMA .fb0 {stroke: rgba(31,35,41, 0.15)}#LmoSxHPBAZMA .fx0 { stroke-miterlimit: 10; }#LmoSxHPBAZMA .fx0 {stroke-miterlimit: 10}#LmoSxHPBAZMA .fa1 { fill: rgb(187, 191, 196); }#LmoSxHPBAZMA .fa1 {fill: rgb(187, 191, 196)}#LmoSxHPBAZMA .fx1 { font: 400 12px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#LmoSxHPBAZMA .fx1 {font-style: normal}#LmoSxHPBAZMA .fx2 { text-anchor: middle; }#LmoSxHPBAZMA .fx2 {text-anchor: middle}#LmoSxHPBAZMA .fx3 { dominant-baseline: central; }#LmoSxHPBAZMA .fx3 {dominant-baseline: central}#LmoSxHPBAZMA .fx4 { stroke: none; }#LmoSxHPBAZMA .fx4 {stroke: none}#LmoSxHPBAZMA .fc0 { fill: var(--text-caption); }#LmoSxHPBAZMA .fc0 {fill: #646a73}#LmoSxHPBAZMA .fa2 { fill: var(--text-link-disabled); }#LmoSxHPBAZMA .fa2 {fill: rgb(187, 191, 196)}#LmoSxHPBAZMA .fx5 { font: 400 16px MonospacedNumber, LarkHackSafariFont, LarkEmojiFont, LarkChineseQuote, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Tahoma, "PingFang SC", "Microsoft Yahei", Arial, "Hiragino Sans GB", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; }#LmoSxHPBAZMA .fx5 {font-style: normal}#LmoSxHPBAZMA .fx6 { text-anchor: start; }#LmoSxHPBAZMA .fx6 {text-anchor: start}#LmoSxHPBAZMA .fx7 { dominant-baseline: alphabetic; }#LmoSxHPBAZMA .fx7 {dominant-baseline: alphabetic}#LmoSxHPBAZMA .fc1 { fill: rgb(31, 35, 40); }#LmoSxHPBAZMA .fc1 {fill: rgb(31, 35, 40)}#LmoSxHPBAZMA .fb1 { fill: var(--N900-FG, 0.15); }#LmoSxHPBAZMA .fb1 {fill: rgba(31,35,41, 0.15)}#LmoSxHPBAZMA .fb2 { fill: rgb(209, 217, 224); }#LmoSxHPBAZMA .fb2 {fill: rgb(209, 217, 224)}#LmoSxHPBAZMA .fa3 { fill: var(--bg-body); }#LmoSxHPBAZMA .fa3 {fill: #fff}ABC1234567维度编码方式精度速度可扩展性用途预计算Bi-EncoderQuery 和 Doc 独立编码中等极快(文档向量预计算)10 万文档只需编码 10 万次第一阶段:召回候选可以Cross-EncoderQuery 和 Doc 拼接后联合编码高(查询感知的文档表示)极慢(每对需完整推理)10 万文档需编码 10 万对!第二阶段:精确重排不可以

​

可扩展性的本质差异​

假设数据库有 100,000 个文档:​

•

Bi-Encoder:编码 100,000 个文档(一次性,可离线)+ 编码 1 个查询 = 100,001 次​

•

Cross-Encoder:编码 100,000 对 (query, doc) = 100,000 次(每次查询都要)​

即便用小型 Cross-Encoder(如 ms-marco-MiniLM)在 V100 GPU 上对 100,000 个文档逐对打分,单次查询也需数分钟到数十分钟(取决于 batch size 与硬件),完全无法在交互式 RAG 中接受。​

这就是为什么 Cross-Encoder 只能用于重排少量候选(通常 20-100 个)。​

两阶段检索流水线(标准架构)​

​

代码块​

Python

from sentence_transformers import SentenceTransformer, CrossEncoder​

​

# 第 1 阶段:Bi-Encoder 召回 Top-100​

bi_encoder = SentenceTransformer("all-MiniLM-L6-v2")​

query_embedding = bi_encoder.encode(query)​

candidates = vector_db.search(query_embedding, top_k=100)​

​

# 第 2 阶段:Cross-Encoder 重排 → Top-5​

cross_encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")​

pairs = [(query, doc.content) for doc in candidates]​

scores = cross_encoder.predict(pairs)​

​

# 按重排分数排序,取 Top-5​

reranked = sorted(​

zip(candidates, scores),​

key=lambda x: x[1],​

reverse=True​

)[:5]​

​

# 将 Top-5 送入 LLM 生成回答​

context = "\n\n".join([doc.content for doc, score in reranked])​

​

重排序的实际效果​

重排序阶段对高质量 RAG 是不可妥协的——LLM 的输出质量直接取决于上下文的质量。没有重排序的 RAG 系统通常会将不够相关的文档送入 LLM,导致回答偏离主题或包含无关信息。​

常用重排序方案​

​

​

选择重排序模型​

推荐使用 MTEB Leaderboard 的 Reranking 排行榜选择模型。Average 列是综合质量的良好代理指标。​

框架集成​

​

代码块​

Python

​

进阶:ColBERT(晚期交互模型)​

ColBERT 是 Bi-Encoder 和 Cross-Encoder 的折中方案:​

•

像 Bi-Encoder 一样独立编码 Query 和 Doc​

•

但保留 token 级别的向量(不压缩为单一向量)​

•

检索时做 token 级别的细粒度交互​

​

代码块​

Plain Text

Bi-Encoder: doc → [single vector] → dot product​

Cross-Encoder: [query + doc] → [score] → full attention​

ColBERT: doc → [token vectors] → late interaction (MaxSim)​

​

ColBERT 的精度接近 Cross-Encoder,速度接近 Bi-Encoder,是一个有前景的方向。​

常见误区 / 面试追问​

误区:"Bi-Encoder 精度低是因为模型小" — 精度差异是架构本质决定的。Bi-Encoder 编码文档时没有查询上下文,无论模型多大,信息压缩的损失都存在。​

误区:"直接用 Cross-Encoder 做全量检索更好" — 计算上不可行。10 万文档 × 单次查询 = 10 万次推理,延迟不可接受。​

追问:"Top-K 的 K 设多少?" — 第一阶段:检索 50-100 个候选(K 太小会漏掉相关文档,太大会拖慢重排)。第二阶段:重排后取 3-10 个送入 LLM(取决于上下文窗口大小)。​

追问:"Cross-Encoder 对长文档有什么问题?" — 很多模型截断输入到 token 窗口长度(如 512 tokens),可能切掉文档最相关的部分。解决方案:先分块再重排,或使用支持长上下文的重排模型。​