LangChain 教學:RAG 中的 Multi‑Query、HyDE 與 ReRanker 最佳化
簡介
在大型語言模型(LLM)日益普及的今天,檢索增強生成(Retrieval‑Augmented Generation, RAG) 已成為提升答案正確性與資訊新鮮度的關鍵技術。單純依賴模型內部的參數常會出現「幻覺」或過時資訊,而 RAG 透過外部文件搜尋把最新、可靠的內容帶入生成流程。
本單元聚焦 Multi‑Query、HyDE(Hybrid‑Dense Retrieval)與 ReRanker 三大最佳化策略。它們分別從 多樣化查詢生成、結合稠密向量與語意生成、以及 結果再排序 三個層面提升檢索品質,讓最終的生成回應更精確、更具上下文相關性。即使是剛接觸 LangChain 的新手,也能在以下範例中快速上手。
核心概念
1. Multi‑Query Retriever
單一問題往往只能對應到一個檢索向量,若使用者的提問較為抽象或包含多個子概念,單一向量可能找不到完整的相關文件。Multi‑Query Retriever 會先讓 LLM 產生多個不同表達的查詢,再分別執行檢索,最後合併結果。
實作步驟
- 使用
LLMChain產生多個改寫後的查詢。 - 以
FAISS、Pinecone或其他向量資料庫分別檢索。 - 合併取得的文件集合(去重、排序)。
程式碼範例(Python)
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
# 1️⃣ 建立多查詢產生器
template = """請將以下問題改寫成三個不同的搜尋查詢,保持語意相同,語氣自然。
問題:{question}
改寫結果:"""
prompt = PromptTemplate.from_template(template)
llm = OpenAI(model="gpt-3.5-turbo")
query_chain = LLMChain(llm=llm, prompt=prompt)
def generate_multi_queries(question: str) -> list[str]:
resp = query_chain.run({"question": question})
# 回傳的文字示例: "1. ...\n2. ...\n3. ..."
return [line.split(". ", 1)[1] for line in resp.splitlines() if ". " in line]
# 2️⃣ 執行向量檢索
embeddings = OpenAIEmbeddings()
vector_store = FAISS.load_local("faiss_index", embeddings)
def multi_query_retrieval(question: str, top_k: int = 5):
queries = generate_multi_queries(question)
docs = []
for q in queries:
docs.extend(vector_store.similarity_search(q, k=top_k))
# 去重、依相似度排序(簡易示例)
unique_docs = {doc.page_content: doc for doc in docs}.values()
return list(unique_docs)[:top_k]
# 使用範例
results = multi_query_retrieval("什麼是量子糾纏的實驗驗證?")
print(f"取得 {len(results)} 篇相關文件")
重點:
generate_multi_queries會交給 LLM 產生 多樣化 的檢索句,提升召回率。
2. HyDE(Hybrid‑Dense Retrieval)
HyDE 先讓 LLM 自行產生答案草稿(即「假想答案」),再把這段草稿作為檢索查詢。這樣可以把使用者的隱含意圖與 LLM 的語意推理結合,對於長篇說明或需要推理的問題特別有效。
實作流程
- LLM 產生「假想答案」文字。
- 把假想答案嵌入向量空間,與文件向量比對。
- 取回最相關的文件作為「證據」,最後再交給 LLM 完成最終生成。
程式碼範例(Python)
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
# 1️⃣ 假想答案產生器
hyde_template = """請根據以下問題,寫出一段簡短的答案草稿(不必正確,只要能概括問題核心)。
問題:{question}
草稿:"""
hyde_prompt = PromptTemplate.from_template(hyde_template)
hyde_chain = LLMChain(llm=llm, prompt=hyde_prompt)
def hyde_retrieval(question: str, top_k: int = 5):
# 產生草稿
draft = hyde_chain.run({"question": question})
# 用草稿做向量檢索
docs = vector_store.similarity_search(draft, k=top_k)
return docs
# 使用範例
hyde_docs = hyde_retrieval("如何在 Python 中使用 asyncio 進行併發?")
for d in hyde_docs:
print("- " + d.metadata.get("source", "unknown"))
提示:HyDE 的草稿不需要精確,只要能捕捉關鍵詞即可,這樣檢索的召回率往往比直接使用原始問題更高。
3. ReRanker(結果再排序)
即使 Multi‑Query 與 HyDE 已大幅提升召回,仍可能返回一些與問題相關度較低的文件。ReRanker 透過交叉注意力(Cross‑Encoder)模型,對檢索結果進行二次打分,挑出最具參考價值的前幾篇。
常見 ReRanker 實作
cross‑encoder/ms-marco-MiniLM-L-6-v2(輕量、速度快)sentence‑transformers/all‑mpnet‑base‑v2(精度較高)
程式碼範例(Python)
from sentence_transformers import CrossEncoder
# 1️⃣ 載入輕量 ReRanker
re_ranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
def rerank(query: str, docs: list, top_n: int = 3):
# 建立 (query, doc) 配對
pairs = [(query, d.page_content) for d in docs]
scores = re_ranker.predict(pairs)
# 依分數排序
sorted_docs = [doc for _, doc in sorted(zip(scores, docs), reverse=True)]
return sorted_docs[:top_n]
# 結合 Multi‑Query、HyDE 與 ReRanker
def rag_pipeline(question: str):
# 先 Multi‑Query 召回
candidate_docs = multi_query_retrieval(question, top_k=10)
# 再使用 ReRanker 篩選
final_docs = rerank(question, candidate_docs, top_n=5)
return final_docs
# 示範
final = rag_pipeline("什麼是區塊鏈共識機制?")
for i, d in enumerate(final, 1):
print(f"{i}. {d.metadata.get('source')}")
技巧:若檢索量很大,可先用 粗排(FAISS)得到前 100 篇,再交給 ReRanker 做精排,兼顧效率與精度。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 查詢過長導致向量失真 | LLM 產生的 HyDE 草稿或 Multi‑Query 有時過於冗長。 | 只保留關鍵詞或使用 truncate;或先用 text‑splitter 切分再嵌入。 |
| ReRanker 計算成本過高 | 每次都對全部召回結果做跨模型打分。 | 先以向量相似度取前 50~100 篇,再交給 ReRanker;或使用 distil 版的 Cross‑Encoder。 |
| 文件去重失敗 | 多個查詢可能返回同一篇文件,導致回答重複。 | 用 set 或 dict 以 doc.metadata['source'] 為鍵去重。 |
| 模型授權或速率限制 | OpenAI、Cohere 等 API 可能觸發速率上限。 | 併發控制(asyncio.Semaphore)或自行部署開源模型(如 Llama‑2)。 |
| 幻覺答案仍未被完全抑制 | 即使有證據文件,LLM 仍可能自行捏造。 | 在最終生成前,加入 「只使用提供的文件」 的 Prompt 限制,或使用 Fact‑Checking 步驟。 |
最佳實踐
- 先召回、後精排:向量檢索作粗排、Cross‑Encoder 作精排。
- 多樣化查詢 + HyDE:兩者可以同時使用,互補提升召回。
- 批次處理:對大量問題時,先把所有問題的 Multi‑Query、HyDE 結果一次性向量化,減少重複計算。
- 日誌與監控:記錄每階段的文件數量與分數,便於後續調校。
實際應用場景
| 場景 | 為何需要 Multi‑Query / HyDE / ReRanker |
|---|---|
| 客服機器人 | 客戶問題常帶有口語化、模糊描述,Multi‑Query 能擴散搜尋範圍;ReRanker 確保回覆只引用最相關的 SOP 文件。 |
| 法律文件檢索 | 法條解釋需要精準引用,HyDE 可先產生法律概念的草稿,再找出具體條文;ReRanker 保障引用的正確性。 |
| 學術研究助理 | 研究者的問題可能跨領域,Multi‑Query 產生多個專業詞彙;HyDE 讓模型自行概括研究背景,提高召回率。 |
| 產品說明生成 | 從產品手冊中抽取資訊,HyDE 產生功能說明草稿,ReRanker 挑出最具說服力的段落組合。 |
| 金融風險分析 | 多變的市場用語需要 Multi‑Query;HyDE 能模擬分析報告的框架,ReRanker 確保引用的報表最新且可靠。 |
總結
- Multi‑Query Retriever 透過 LLM 產生多樣化查詢,解決單一向量召回不足的問題。
- HyDE 把「假想答案」作為檢索文本,結合語意推理與向量檢索,特別適合需要推理的長問句。
- ReRanker 用跨注意力模型二次打分,從召回的大量結果中挑選最具參考價值的文件,提升最終生成的正確性與可信度。
在 LangChain 中將這三者組合,能形成 「召回 → 再排序 → 生成」 的完整 RAG 流程,讓 LLM 產出的答案不再是空中樓閣,而是有據可依、貼合實務需求的高品質回覆。只要遵守上述最佳實踐、避免常見陷阱,即可在客服、法律、學術、金融等多種領域快速構建可靠的智能應用。祝你在 RAG 的世界裡玩得開心、寫得順手!