RAG From Scratch 系列教程-2:查询转换(Query Translation) - 教程
在检索增强生成(RAG)系统中,查询转换(Query Translation)是提升检索效果的核心技术。本文将详细解析五种前沿的查询优化方法,包含原理说明、工作流程和可运行的代码示例。
一、多查询生成(Multi-query)
原理
通过语言模型生成原始问题的多个变体,扩大检索覆盖范围。研究表明,这种方式可以将检索召回率提升15-30%。
工作流程
接收原始用户问题
生成3-5个语义相同但表述不同的查询
并行执行所有查询的检索
合并并去重检索结果
代码实现
python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
multi_query_prompt = ChatPromptTemplate.from_template(
"""请为以下问题生成3个不同表述的搜索查询,用换行符分隔:
问题:{question}
查询变体:"""
)
multi_query_chain = (
{"question": itemgetter("question")}
| multi_query_prompt
| ChatOpenAI(temperature=0.7)
| StrOutputParser()
| (lambda x: x.split("\n"))
)
# 使用示例
queries = multi_query_chain.invoke({"question": "如何预防感冒?"})
print(queries)
# 输出示例:
# ["感冒的预防措施有哪些?", "怎样避免得感冒?", "预防感冒的有效方法"]
二、RAG-Fusion
原理
结合关键词检索(如BM25)和向量检索的优势,通过加权分数融合两种检索结果。
工作流程
分别执行关键词检索和向量检索
对两组结果进行归一化评分
按权重合并排序(通常权重为0.3:0.7)
去除重复文档
代码实现
python
from rank_bm25 import BM25Okapi
from langchain_community.vectorstores import FAISS
def hybrid_retrieval(query, text_list, vector_store, k=5):
# 关键词检索
tokenized_corpus = [doc.split() for doc in text_list]
bm25 = BM25Okapi(tokenized_corpus)
bm25_scores = bm25.get_scores(query.split())
# 向量检索
vector_results = vector_store.similarity_search_with_score(query, k=k)
vector_scores = [res[1] for res in vector_results]
# 分数归一化(0-1范围)
norm_bm25 = (bm25_scores - min(bm25_scores)) / (max(bm25_scores) - min(bm25_scores))
norm_vector = 1 - (vector_scores / max(vector_scores))
# 加权融合(BM25权重0.3)
combined_scores = 0.3*norm_bm25 + 0.7*norm_vector
return sorted(zip(text_list, combined_scores), key=lambda x: x[1], reverse=True)
三、问题分解(Decomposition)
原理
将复杂问题拆解为多个子问题,分别检索后综合答案。特别适合多要素比较类和分步解决类问题。
工作流程
识别问题中的子任务
为每个子任务生成独立查询
并行检索所有子问题
综合子答案生成最终回复
代码实现
python
decompose_prompt = ChatPromptTemplate.from_template(
"""将以下复杂问题分解为3-5个子问题,用数字编号列出:
问题:{question}
子问题:"""
)
def retrieve_sub_answers(sub_questions, retriever):
return [retriever.invoke(q) for q in sub_questions]
decomposition_chain = (
{"question": itemgetter("question")}
| decompose_prompt
| ChatOpenAI()
| StrOutputParser()
| (lambda x: [q.strip() for q in x.split("\n") if q.strip()])
| retrieve_sub_answers
)
# 使用示例
sub_answers = decomposition_chain.invoke({
"question": "比较PyTorch和TensorFlow在图像分类和序列建模任务上的优缺点"
})
四、Step-back Prompting
原理
通过先回答抽象的上层问题建立理论框架,再解决具体问题。相当于让AI先"思考原理"再"应用知识"。
工作流程
生成step-back抽象问题
检索抽象问题的相关原理
检索原始问题的具体信息
结合两部分信息生成最终答案
代码实现
python
step_back_prompt = """请根据具体问题生成一个更抽象的原理性问题:
具体问题:{question}
抽象问题:"""
def step_back_qa(question, retriever, llm):
# 生成抽象问题
abstract_q = llm.invoke(step_back_prompt.format(question=question))
# 两阶段检索
abstract_docs = retriever.invoke(abstract_q)
concrete_docs = retriever.invoke(question)
# 综合回答
response = llm.invoke(
f"基于以下理论原理:{abstract_docs}\n"
f"和具体信息:{concrete_docs}\n"
f"请回答:{question}"
)
return response
五、假设文档嵌入(HyDE)
原理
通过假设答案构建与目标文档相似的向量表示,突破查询表述的字面限制。
工作流程
生成假设性答案(无需准确)
计算假设答案的嵌入向量
用该向量检索真实文档
基于真实文档生成最终答案
代码实现
python
hyde_prompt = """请为以下问题生成一个假设性答案(不需要准确):
问题:{question}
假设答案:"""
def hyde_retrieval(query, embeddings, vector_db, llm):
# 生成假设答案
hypothetical_answer = llm.invoke(
hyde_prompt.format(question=query)
)
# 向量化检索
hypo_vector = embeddings.embed_query(hypothetical_answer)
results = vector_db.similarity_search_by_vector(hypo_vector)
# 生成最终答案
return llm.invoke(
f"根据以下文档回答{query}:\n{results}"
)
技术对比与选型建议
| 技术 | 适用场景 | 计算开销 | 实现难度 | 效果提升 |
|---|---|---|---|---|
| Multi-query | 开放域简单问题 | 中 | 低 | 15-20% |
| RAG-Fusion | 专业术语密集型问题 | 高 | 中 | 25-35% |
| Decomposition | 复杂多要素问题 | 高 | 高 | 30-40% |
| Step-back | 需要理论支撑的问题 | 中 | 中 | 20-30% |
| HyDE | 零样本/低资源场景 | 低 | 低 | 15-25% |
组合使用建议:
对专业领域问题:RAG-Fusion + Decomposition
对复杂推理问题:Step-back + Multi-query
快速实现方案:HyDE + Multi-query

浙公网安备 33010602011771号