- Published on
RAG 進階檢索(二):Rerank、MMR 與完整評分管線
Table of Contents
在上一篇中,我們用 Multi-Query 和各種 Query 改寫策略,成功地把更多相關文件從知識庫裡撈了出來。
但撈回來之後,你可能會發現一個尷尬的狀況:
你問「如何在 Kubernetes 上部署微服務?」,系統撈回了 20 篇文件。你一看排名:
- Top 1:「K8s Deployment YAML 範例」
- Top 2:「K8s Deployment 最佳實踐」
- Top 3:「K8s Deployment 入門教學」
- Top 4:「K8s Deployment 常見錯誤」
- Top 5:「K8s Deployment vs StatefulSet」
五篇全部都在講 Deployment。但你的問題其實還涉及「Service Mesh」、「CI/CD Pipeline」、「Helm Chart 管理」等面向——這些文件明明也被撈回來了,卻排在第 12、15、18 名,根本不會被送進 LLM 的 Context。
這就是 RAG 的兩大隱藏殺手:排序不夠精準 + 結果過度冗餘。
Bi-Encoder vs Cross-Encoder:為什麼需要兩階段?
要理解 Rerank(重排序)為什麼重要,我們得先搞清楚一個根本問題:你的向量搜尋(Vector Search)到底是怎麼算「相似度」的?
答案是:用 Bi-Encoder(雙塔模型)。
Bi-Encoder:快但粗
Bi-Encoder 的架構很直覺:它有兩個獨立的 Encoder(實務上通常是同一個模型用兩次)。一個負責把 Query 編碼成向量,另一個負責把 Document 編碼成向量。然後用 Cosine Similarity 算兩個向量的距離。
Query → Encoder → 向量 [0.2, -0.4, 0.8, ...]
→ cosine similarity → 0.87
Doc → Encoder → 向量 [0.1, -0.3, 0.7, ...]
這個架構的最大優勢是:Document 的向量可以預先計算好存在資料庫裡。當使用者發問時,只需要即時計算 Query 的向量,然後跟資料庫裡的十萬個向量做距離比較。這就是為什麼向量搜尋可以在毫秒級完成。
但代價是什麼?Query 和 Document 在編碼的過程中完全沒有交互。Encoder 在處理 Query 的時候,根本不知道 Document 長什麼樣子;反之亦然。這意味著它無法捕捉到 Query 和 Document 之間那些細微的語意關聯。
Cross-Encoder:慢但精
Cross-Encoder 的做法完全不同:它把 Query 和 Document 拼接在一起,丟進同一個 Transformer 模型裡。
輸入:[CLS] Query [SEP] Document [SEP]
↓
Transformer(全層 Self-Attention)
↓
相關性分數:0.94
因為 Query 和 Document 的每一個 Token 都在 Self-Attention 層裡互相「看到」對方,所以 Cross-Encoder 能捕捉到極其細微的語意關聯。例如,它能理解「部署微服務」和「Service Mesh 的流量管理」之間的隱含關係——這是 Bi-Encoder 很難做到的。
但代價也很明顯:每一對 (Query, Document) 都要跑一次完整的 Transformer 推理。如果你有十萬篇文件,就要跑十萬次。這在延遲和成本上都是不可接受的。
圖說:Bi-Encoder 像是看照片選人(快但可能看走眼),Cross-Encoder 像是面對面聊天(慢但判斷精準)。
所以最佳實踐是:兩階段架構
- L1(Retrieval):用 Bi-Encoder 做粗篩,從十萬筆中快速撈出 Top 50
- L2(Rerank):用 Cross-Encoder 對這 50 篇做精排,重新排出真正的 Top 5
這就是業界所說的 Retrieve-then-Rerank 架構。L1 追求速度和召回率,L2 追求精準度。
Cross-Encoder Rerank 實戰
開源模型選擇
目前最常用的 Cross-Encoder Rerank 模型:
| 模型 | 來源 | 特點 |
|---|---|---|
ms-marco-MiniLM-L-6-v2 | Sentence-Transformers | 輕量、速度快,適合 POC |
bge-reranker-v2-m3 | BAAI (智源) | 多語言支援好,中文表現優秀 |
jina-reranker-v2 | Jina AI | API 服務,不用自己部署 |
rerank-v3.5 | Cohere | API 服務,業界標竿 |
用 Sentence-Transformers 實作 Rerank
from sentence_transformers import CrossEncoder
# 載入 Cross-Encoder 模型
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
query = "如何在 Kubernetes 上部署微服務?"
documents = [
"K8s Deployment YAML 範例與最佳實踐...",
"Service Mesh 在微服務架構中的流量管理...",
"使用 Helm Chart 管理 K8s 應用部署...",
# ... 從 L1 撈回來的 Top 50
]
# Cross-Encoder 會對每一對 (query, doc) 計算相關性分數
pairs = [(query, doc) for doc in documents]
scores = reranker.predict(pairs)
# 按分數重新排序
ranked = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True)
top_5 = ranked[:5]
跟 Azure Semantic Ranker 的關係
如果你有在用 Azure AI Search,你可能會想:「這跟 Semantic Ranker 有什麼不同?」
本質上,Azure 的 Semantic Ranker 就是微軟自家訓練的 Cross-Encoder(或類似架構的深度語言模型)。差別在於:
- Semantic Ranker:微軟託管、一鍵開啟、不用管模型部署,但只能在 Azure AI Search 裡用
- 開源 Cross-Encoder:你自己部署、自己選模型,可以用在任何向量資料庫(Chroma、Pinecone、Qdrant...)
如果你的架構不在 Azure 上,或者你想要更細緻的控制(例如用自己的資料 Fine-tune Reranker),那開源 Cross-Encoder 就是你的選擇。
MMR:解決「前五名都在講同一件事」
Rerank 解決了「排序不夠精準」的問題,但還有一個問題沒解決:結果冗餘。
回到開頭的例子:即使 Cross-Encoder 重新排序後,那五篇 Deployment 文章可能還是佔據前五名——因為它們跟 Query 的相關性確實都很高。但對 LLM 來說,五篇講同一件事的文件,跟一篇的資訊量差不多。
MMR(Maximal Marginal Relevance,最大邊際相關性) 就是來解決這個問題的。
MMR 的核心思想
MMR 在選擇下一篇文件時,同時考慮兩個因素:
- 跟 Query 的相關性(越高越好)
- 跟已選文件的差異性(越不同越好)
公式長這樣:
MMR = λ × Sim(Query, Doc) - (1-λ) × max(Sim(Doc, 已選文件))
λ是平衡參數,通常設0.5~0.7λ = 1時,完全只看相關性(等於沒有 MMR)λ = 0時,完全只看多樣性(可能選到不相關的文件)
直覺解釋
想像你在自助餐選菜。如果只看「好不好吃」(相關性),你可能會夾五盤紅燒肉。但如果同時考慮「跟盤子裡已經有的菜不一樣」(多樣性),你就會夾一盤紅燒肉、一盤青菜、一盤魚、一盤湯——營養更均衡。
實作範例
import numpy as np
def mmr_select(query_embedding, doc_embeddings, doc_scores, k=5, lambda_param=0.6):
"""
query_embedding: Query 的向量
doc_embeddings: 候選文件的向量列表
doc_scores: Cross-Encoder 給的相關性分數
k: 要選幾篇
lambda_param: 相關性 vs 多樣性的平衡
"""
selected = []
candidates = list(range(len(doc_scores)))
for _ in range(k):
best_score = -float('inf')
best_idx = -1
for idx in candidates:
# 相關性分數(已由 Cross-Encoder 計算)
relevance = doc_scores[idx]
# 跟已選文件的最大相似度
if selected:
similarities = [
cosine_sim(doc_embeddings[idx], doc_embeddings[s])
for s in selected
]
max_sim = max(similarities)
else:
max_sim = 0
# MMR 分數
mmr = lambda_param * relevance - (1 - lambda_param) * max_sim
if mmr > best_score:
best_score = mmr
best_idx = idx
selected.append(best_idx)
candidates.remove(best_idx)
return selected
λ 參數怎麼調?
| λ 值 | 效果 | 適合場景 |
|---|---|---|
| 0.9 | 幾乎只看相關性 | 精確查詢(料號、人名) |
| 0.7 | 偏重相關性,適度多樣 | 一般知識問答 |
| 0.5 | 相關性和多樣性各半 | 開放式探索問題 |
| 0.3 | 偏重多樣性 | 腦力激盪、創意發想 |
完整 RAG Scoring Pipeline
把前面所有技術串起來,一條完整的 RAG Scoring Pipeline 長這樣:
圖說:從 L0 到 L4,每一層都在做「減量提質」——資料量越來越少,但品質越來越高。
五層架構一覽
- L0 - Query 擴展:Multi-Query / HyDE / Step-back,把 1 個查詢變成 3~5 個
- L1 - Retrieval:Bi-Encoder + Hybrid Search + RRF,從十萬筆中撈出 Top 50
- L2 - Rerank:Cross-Encoder 逐一精讀,重排出 Top 10
- L3 - Diversity:MMR 去除冗餘,精選 Top 3~5 篇多樣化文件
- L4 - Generation:LLM 用精選的 Context 生成最終回答
不是每一層都必須
這五層是「完整版」。在實戰中,你可以根據場景選擇性地啟用:
| 場景 | 建議啟用的層 | 原因 |
|---|---|---|
| 簡單 FAQ Bot | L1 + L4 | 問題明確,不需要擴展和重排 |
| 企業知識庫 | L1 + L2 + L4 | 需要精準排序,但文件重複度低 |
| 複雜研究助手 | L0 + L1 + L2 + L3 + L4 | 問題模糊、知識庫龐大、需要多角度 |
| 客服對話系統 | L1 + L2 + L3 + L4 | 需要精準且多樣的回答依據 |
延遲與成本的現實
每多一層,就多一份延遲和成本。以一次查詢為例:
- L0:LLM 生成查詢變體 → +0.5~1 秒,+1 次 LLM API 呼叫
- L1:向量搜尋 → +50~200ms(取決於資料量和索引類型)
- L2:Cross-Encoder 重排 50 篇 → +0.5~2 秒(取決於模型大小和硬體)
- L3:MMR 計算 → +10~50ms(純數學運算,很快)
- L4:LLM 生成回答 → +1~5 秒
所以完整跑一輪大約 3~8 秒。對於即時對話來說可能偏慢,但對於「搜尋後閱讀」的場景(例如企業知識庫)完全可以接受。
實戰加速技巧
L0 的多個查詢變體可以平行檢索(同時發出 35 個搜尋請求),這樣 L0+L1 的總延遲不會是 35 倍,而是跟單次搜尋差不多。L2 的 Cross-Encoder 也可以用 batch inference 加速,把 50 對 (Query, Doc) 一次丟進 GPU。
跟 Azure AI Search 的關係
如果你用的是 Azure AI Search,這條 Pipeline 的對應關係是:
| Pipeline 層 | Azure AI Search 對應 | 通用方案 |
|---|---|---|
| L0 Query 擴展 | ❌ 需自己實作 | LangChain / LlamaIndex |
| L1 Retrieval | ✅ Hybrid Search + RRF | 任何向量資料庫 |
| L2 Rerank | ✅ Semantic Ranker | Cross-Encoder / Cohere |
| L3 Diversity | ❌ 需自己實作 | MMR 演算法 |
| L4 Generation | ✅ Azure OpenAI | 任何 LLM |
Azure 幫你處理了 L1 和 L2(而且 Semantic Ranker 的效果非常好),但 L0 和 L3 需要你自己在應用層實作。如果你想深入了解 Azure 的方案,可以參考我的 Azure AI Search 系列。
小結
這篇我們從 Bi-Encoder 和 Cross-Encoder 的根本差異出發,理解了為什麼需要兩階段的 Retrieve-then-Rerank 架構。然後用 MMR 解決了結果冗餘的問題,最後把所有技術組裝成一條完整的五層 Scoring Pipeline。
兩篇 RAG 進階檢索系列的核心觀念:
- 第一篇:Query 端優化(Multi-Query、HyDE、Step-back、Sub-question)→ 撈得更多、更全
- 第二篇:Result 端優化(Cross-Encoder Rerank、MMR)→ 排得更準、更多樣
掌握這些技術後,你的 RAG 系統就不再只是「能用」,而是真正能在企業場景中交出高品質答案的生產級系統。