本文 AI 產出,尚未審核

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 產生多個不同表達的查詢,再分別執行檢索,最後合併結果。

實作步驟

  1. 使用 LLMChain 產生多個改寫後的查詢。
  2. FAISSPinecone 或其他向量資料庫分別檢索。
  3. 合併取得的文件集合(去重、排序)。

程式碼範例(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 的語意推理結合,對於長篇說明或需要推理的問題特別有效。

實作流程

  1. LLM 產生「假想答案」文字。
  2. 把假想答案嵌入向量空間,與文件向量比對。
  3. 取回最相關的文件作為「證據」,最後再交給 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。
文件去重失敗 多個查詢可能返回同一篇文件,導致回答重複。 setdictdoc.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 的世界裡玩得開心、寫得順手!