在 Day 9 中,我們完成了 Hybrid Search,結合:
但即使結合多種搜尋方法,仍然可能遇到一個問題:
搜尋結果中可能包含相關文件,但真正重要的文件沒有排在前面
這些文件可能都與問題有關聯,但第 3 筆才是最直接的答案依據
如果我們只取前 2 筆文件建立 Context,LLM 就可能無法取得最重要的內容
因此,今天要引入 Reranking (重新排序),讓系統在初步搜尋後,再次判斷文件與使用者問題的關聯程度
今天主要處理以下問題:
整體概念是:
先廣泛搜尋,再精準排序
這種方式也稱為 Retrieve → Rerank。
Reranking 是指:
對初步檢索取得的候選文件,再使用另一個排序模型或規則重新評估,產生更符合查詢需求的排序結果
初步搜尋的目標是提高 Recall (找得到相關文件的能力)。
Reranking 的目標則是改善 排序品質與 Precision (前段結果的相關程度)。
Vector Search 主要透過向量相似度找出候選文件
但「語意相似」不一定代表「回答問題時最有用」
文件 B 可能比文件 A 更直接符合使用者的具體需求
Embedding 通常會分別建立:
再比較兩者之間的相似程度
這種方法可以有效處理大量文件,但不一定會深入分析:
因此,我們可以使用第二階段模型,直接將 Query + Document 一起輸入模型,進行更細緻的相關性評估
Bi-Encoder 會分別將 Query 與 Document 轉換成向量
優點:
限制:
Cross-Encoder 會分別評估兩份文件與查詢的關聯程度
在這個例子中,文件 B 包含更直接的回答資訊,因此可能取得較高的相關性分數
因此,常見做法是:
先使用快速檢索縮小範圍,再使用 Cross-Encoder 重新排序
假設初步搜尋只取得 3 筆:
如果真正相關的文件沒有進入這 3 筆,Reranking 就沒有機會將它找出來
因此,可以在檢索範圍與推論成本之間取得平衡
本次使用 Sentence Transformers 提供的 Cross-Encoder
安裝套件:
pip install sentence-transformers torch
模型名稱與下載來源可能隨版本變化
實際使用前,請確認模型的語言支援、授權條款與硬體需求
from sentence_transformers import CrossEncoder
class DocumentReranker:
def __init__(
self,
model_name="cross-encoder/ms-marco-MiniLM-L-6-v2"
):
self.model = CrossEncoder(model_name)
def rerank(
self,
query,
documents,
top_k=5
):
pairs = [
[query, document]
for document in documents
]
scores = self.model.predict(pairs)
ranked_results = sorted(
zip(documents, scores),
key=lambda item: float(item[1]),
reverse=True
)
results = []
for document, score in ranked_results[:top_k]:
results.append({
"document": document,
"score": float(score)
})
return results
建立 Query 與 Document 的配對:
pairs = [
[query, document]
for document in documents
]
接著取得每份文件的分數,並按照分數由高到低排序
不同模型的分數定義可能不同:
因此,不能直接將不同模型的分數視為相同意義,也不應直接與 BM25 或向量距離相加。
這個實驗可以觀察:
前面 Day 9 的 Hybrid Search 會產生候選文件
接下來將候選文件傳給 Reranker
from src.retrieval.reranker import DocumentReranker
class RerankPipeline:
def __init__(
self,
hybrid_searcher,
reranker
):
self.hybrid_searcher = hybrid_searcher
self.reranker = reranker
def search(
self,
query,
retrieve_top_k=20,
final_top_k=5
):
candidates = self.hybrid_searcher.search(
query=query,
top_k=retrieve_top_k
)
documents = [
result["document"]
for result in candidates
]
reranked_results = self.reranker.rerank(
query=query,
documents=documents,
top_k=final_top_k
)
return reranked_results
上面的程式只保留文件文字與 Reranking 分數
但在實際 RAG 系統中,應該保留原本的 Metadata
否則重新排序後,可能無法追蹤文件來源
Reranking 應該保留完整的候選資料
接著就能在建立 Context 時,繼續顯示引用來源
Vector Search 可以預先建立文件向量,查詢時只需要進行向量比對
但 Cross-Encoder 必須針對每一組 Query 與 Document 進行推論
不要直接對整個文件庫執行 Reranking
透過先縮小候選集合,可以降低推論時間與硬體負擔
本篇示範的模型主要針對英文資訊訓練
如果文件與查詢以繁體中文為主,可能遇到:
選擇 Reranker 時,需要確認:
不能只因為模型名稱包含 Cross-Encoder,就假設它適合所有語言與場域
Reranking 模型的分數代表模型對相關性的判斷,不等於事實正確性
這份文件可能與查詢相關,但沒有明確說明是否支援 Ubuntu 24.04
需要區分:
Reranking 主要改善排序,不能單獨解決所有 RAG 問題
如果 Chunk 太短,可能缺少必要上下文
如果 Chunk 太長,可能同時包含多個不同主題
對於複雜問題,Chunk B 可能更有用,因為它包含完整的回答背景
Reranking 不是用來取代良好的 Chunking,而是建立在合理文件切分之上
不能只觀察模型輸出的分數,應該使用實際測試案例進行評估
觀察前 K 筆文件中,有多少筆真正相關
MRR (Mean Reciprocal Rank) 主要觀察第一份相關文件出現的位置
第一份相關文件越早出現,MRR 通常越高
NDCG (Normalized Discounted Cumulative Gain) 可以處理不同程度的相關性
這比單純判斷「相關/不相關」更有彈性
可以將文件 C 評為高度相關,文件 A 評為相關
TEST_CASES = [
{
"query": "Ubuntu 24.04 安裝需要注意什麼?",
"expected_keywords": [
"Ubuntu",
"24.04",
"相依套件"
]
},
{
"query": "安裝完成後需要做什麼?",
"expected_keywords": [
"重新啟動"
]
}
]
def is_relevant(document, expected_keywords):
return all(
keyword in document
for keyword in expected_keywords
)
def evaluate_results(results, expected_keywords):
relevant_count = 0
for result in results:
document = result["document"]
if is_relevant(
document,
expected_keywords
):
relevant_count += 1
total = len(results)
if total == 0:
return 0.0
return relevant_count / total
這是一個簡化的測試方法
實際上,文件不一定需要包含所有預期關鍵字才算相關。例如:
「重新啟動」與「重新開機」語意相近,但字串不同
因此,正式評估時應該建立人工標註資料,或搭配語意評估與人工審查。
觀察不同設定對以下指標的影響:
除了專用 Reranker,也可以讓 LLM 直接評估文件相關性
這種方法具有彈性,但也可能有以下問題:
因此,若系統需要大量且穩定的排序工作,通常會考慮使用專門的 Reranker。
今天從 Hybrid Search 進一步加入 Reranking,讓文件檢索流程從「找到候選文件」走向「重新判斷相關程度」。
Vector Search 與 Keyword Search 適合快速取得候選文件,但前段結果不一定是最適合回答問題的內容。
Reranking 會重新分析 Query 與 Document 的關係,讓較相關的文件排在前面。
第一階段:快速取得較大的候選集合
第二階段:精細判斷候選文件的相關性
這比直接對整個文件庫逐筆使用 Cross-Encoder 更有效率。
不能只看模型分數,而應該觀察:
Reranker 主要評估文件與問題的相關性,不能保證文件內容一定正確,也不能取代來源驗證與回答評估
目前的檢索流程已經從:
單純向量搜尋
逐步發展成:
Vector Search
+
Keyword Search
↓
Hybrid Search
↓
Reranking
↓
Context 建立
↓
LLM 回答
這樣的架構能讓 RAG 系統更有機會取得與問題直接相關的文件
即使我們已經加入:
仍然可能遇到一個核心問題:
檢索結果看起來相關,但 LLM 產生的答案是否真的正確
因此,下一步將從「檢索品質」延伸到「整體 RAG 評估」。
明天可以探討:
Day 11:RAG Evaluation——如何知道 AI 助理回答得好不好
確認「這些文件是否真的支援 AI 產生的答案」