跳到主要内容

高级检索策略

问题

如何提升 RAG 的检索质量?有哪些高级检索策略?

答案

Naive RAG 最大的问题是检索质量不够。高级检索策略从 Query 处理、检索方式、结果优化三个维度提升。

一、Query 处理

1. Query 改写(Query Rewriting)

用户的原始问题往往不适合直接检索:

# 原始 query: "它怎么用"(太模糊)
# 改写后: "React useEffect Hook 的使用方法和最佳实践"

from openai import OpenAI
client = OpenAI()

def rewrite_query(original_query: str, chat_history: list) -> str:
"""基于对话历史改写 query"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "将用户问题改写为适合搜索的独立查询,保留对话上下文。只输出改写后的查询。"},
*chat_history,
{"role": "user", "content": original_query}
]
)
return response.choices[0].message.content

2. HyDE(Hypothetical Document Embedding)

核心思想:先让 LLM 生成一个假设性答案,用答案的 Embedding 去检索(而非用问题去检索)。

def hyde_retrieval(query: str, vectorstore, llm):
# 1. 生成假设性答案
hypothetical_answer = llm.invoke(
f"请详细回答以下问题(即使不确定也请尽量回答):\n{query}"
)
# 2. 用假设性答案做检索(答案和文档更相似)
docs = vectorstore.similarity_search(hypothetical_answer.content, k=5)
return docs
为什么 HyDE 有效?

用户的问题("什么是 MVCC?")和文档("MVCC 通过版本链实现多版本并发控制...")的文体差异大。假设性答案和文档的文体更接近,Embedding 匹配度更高。

3. 多查询检索(Multi-Query)

将一个问题扩展为多个角度的子查询,分别检索后合并去重:

def multi_query_retrieval(query: str, llm, vectorstore, num_queries: int = 3):
# 1. 生成多个角度的子查询
response = llm.invoke(
f"将以下问题从{num_queries}个不同角度改写,每行一个:\n{query}"
)
sub_queries = response.content.strip().split("\n")

# 2. 分别检索
all_docs = []
for sub_q in sub_queries:
docs = vectorstore.similarity_search(sub_q, k=5)
all_docs.extend(docs)

# 3. 去重(按内容哈希)
unique_docs = list({doc.page_content: doc for doc in all_docs}.values())
return unique_docs

语义检索关键词检索结合,互补优势:

检索方式优势劣势
语义检索(Embedding)理解同义词、语义专有名词、ID 难匹配
关键词检索(BM25)精确匹配、专有名词不理解语义
混合检索两者互补需要融合权重
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever

# 关键词检索器
bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 5

# 语义检索器
embedding_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

# 混合检索(权重各 50%)
hybrid_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, embedding_retriever],
weights=[0.5, 0.5]
)

RRF(Reciprocal Rank Fusion)

最常用的混合排序算法,不依赖具体分数,只依赖排名:

RRF(d)=rR1k+r(d)\text{RRF}(d) = \sum_{r \in R} \frac{1}{k + r(d)}

其中 r(d)r(d) 是文档 dd 在检索器 rr 中的排名,kk 通常取 60。

三、元数据过滤

在向量检索之前或之后加上结构化过滤:

# Pinecone 示例:先过滤再搜索
results = index.query(
vector=query_embedding,
top_k=10,
filter={
"department": {"$eq": "engineering"},
"date": {"$gte": "2024-01-01"}
}
)

四、检索策略对比

策略复杂度效果提升适用场景
Query 改写对话场景
HyDE中-高问答差异大
多查询复杂问题
混合检索通用推荐
元数据过滤视场景有结构化属性

常见面试问题

Q1: 混合检索中 BM25 和语义检索的权重如何确定?

答案

  1. 经验值:通用场景通常 0.5:0.5 或 0.4:0.6(偏向语义)
  2. RRF:不需要设权重,只看排名,鲁棒性更强
  3. 学习排序:用标注数据训练权重(如果有评测数据集)

Q2: 什么时候不该用 HyDE?

答案

  • 事实性查询("公司2024年营收是多少"):LLM 生成的假设答案可能包含错误数字,误导检索
  • 关键词敏感查询("BUG-12345 状态"):需要精确匹配,语义检索反而有害
  • HyDE 最适合解释性、概念性问题

相关链接