RAG查询转换(Query Transformations)的优化方案(1)——多查询(Multi Query)
·
多查询(Multi Query)、RAG融合(RAG-Fusion)、分解(Decomposition)、回退(Step Back)、HyDE(Hypothetical Document Embeddings)。
这些方法的目的都是将问题转换成一种更适合用于检索的形式。
如下图所示:
我将以系列的方式讲述如何使用。
下面我们先介绍多查询(Multi Query)方法

以下代码的核心功能是 基于 LangChain 构建一个多查询 RAG(检索增强生成)系统,用于从网页中提取医学相关内容,并根据用户问题提供精准答案。
代码流程概览
-
爬取网页数据并进行向量化索引
- 访问指定网页并提取核心内容(如
main_box里的文本)。 - 对文本进行切分,使其适应向量检索。
- 将文本转换为向量并存储到 FAISS 向量数据库中,方便后续检索。
- 访问指定网页并提取核心内容(如
-
构造多角度查询(Multi-Query Retrieval)
- 让大模型(LLM)对用户问题生成 5 个不同的改写版本,增加检索的召回率。
- 用改写后的问题去向量数据库查询,提高找到相关文档的概率。
-
RAG(检索增强生成)
- 从 FAISS 向量数据库中检索出最相关的文本。
- 让 LLM 结合检索到的内容,回答用户的问题。
第一部分:网页数据爬取和索引
1.1 加载网页内容
import bs4
from langchain_community.document_loaders import WebBaseLoader
loader = WebBaseLoader(
web_paths=("http://www.chealth.org.cn/mon/drugs/article/MB145014258.html",),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("main_box")
)
),
)
blog_docs = loader.load()
📌 解释
- 这里使用
WebBaseLoader从http://www.chealth.org.cn/mon/drugs/article/MB145014258.html爬取网页内容。 bs_kwargs参数指定了main_box这个 HTML 类,表示我们只提取网页中这个部分的文本,过滤掉不相关内容(如广告、导航栏等)。
1.2 文本切分
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=300,
chunk_overlap=50
)
splits = text_splitter.split_documents(blog_docs)
📌 解释
- 网页可能包含大量文本,直接存储不利于高效检索,因此 将文本切成 300 字的段落,并且设置 50 字的重叠,避免信息丢失。
RecursiveCharacterTextSplitter是 LangChain 提供的 按字符长度递归切分 的工具,适用于中文文本。
1.3 文本向量化 & FAISS 向量数据库存储
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
model_name = "BAAI/bge-small-zh-v1.5"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
hf_embeddings = HuggingFaceBgeEmbeddings(
model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
)
vectorstore = FAISS.from_documents(documents=splits,
embedding=hf_embeddings)
retriever = vectorstore.as_retriever()
📌 解释
- 使用
HuggingFaceBgeEmbeddings(中文向量化模型) 把文本转换成向量表示,方便后续的语义检索。 - 用 FAISS 构建向量数据库,这样我们可以高效地基于相似度进行搜索。
第二部分:多角度查询(Multi-Query)
2.1 让 LLM 生成多个查询版本
from langchain.prompts import ChatPromptTemplate
template = """你是一个 AI 语言模型助手。
你的任务是生成给定用户问题的五个不同版本,用于从向量数据库中检索相关文档。
通过从多个角度生成用户问题,你的目标是帮助用户克服基于距离的相似度搜索的一些局限性。
请用换行符分隔这些替代问题。 原始问题是: {question}"""
prompt_perspectives = ChatPromptTemplate.from_template(template)
from langchain_groq import ChatGroq
from langchain_core.output_parsers import StrOutputParser
generate_queries = (
prompt_perspectives
| ChatGroq(temperature=0)
| StrOutputParser()
| (lambda x: x.split("\n"))
)
generate_queries.invoke("异戊巴比妥的成人常用量是多少?")
📌 解释
- 这部分代码让 LLM 重新生成 5 个不同版本的问题,帮助我们从多个角度去检索。
- 例如,如果原始问题是:
LLM 可能会生成如下问题:"异戊巴比妥的成人常用量是多少?"1. 异戊巴比妥的推荐剂量是多少? 2. 成年人每天可以服用多少异戊巴比妥? 3. 异戊巴比妥的安全剂量范围是什么? 4. 异戊巴比妥的常见用法是什么? 5. 医生通常如何给患者开异戊巴比妥?
2.2 通过多个查询检索文档
from langchain.load import dumps, loads
def get_unique_union(documents: list[list]):
""" Unique union of retrieved docs """
flattened_docs = [dumps(doc) for sublist in documents for doc in sublist]
unique_docs = list(set(flattened_docs))
return [loads(doc) for doc in unique_docs]
# 运行多查询检索
question = "异戊巴比妥的成人常用量是多少?"
retrieval_chain = generate_queries | retriever.map() | get_unique_union
docs = retrieval_chain.invoke({"question":question})
len(docs)
📌 解释
- 这里的
retriever.map()表示 每个生成的问题都会在 FAISS 数据库里执行检索。 get_unique_union()作用是 去重,避免重复的文档。
第三部分:RAG(检索增强生成)
from operator import itemgetter
from langchain_core.runnables import RunnablePassthrough
template = """请根据以下提供的上下文回答问题:
上下文:
{context}
问题:
{question}
请基于上述上下文,提供清晰、准确的答案。如果无法从上下文中找到答案,请回答 "无法从上下文中找到相关信息"。
"""
prompt = ChatPromptTemplate.from_template(template)
llm = ChatGroq(temperature=0)
final_rag_chain = (
{"context": retrieval_chain,
"question": itemgetter("question")}
| prompt
| llm
| StrOutputParser()
)
final_rag_chain.invoke({"question":question})
📌 解释
- 这部分代码让 LLM 结合检索到的文档内容回答问题。
retrieval_chain返回上下文,让 LLM 只根据检索到的文档回答,避免胡编乱造(Hallucination)。
完整流程示例
假设用户问:
"异戊巴比妥的成人常用量是多少?"
系统会:
- 爬取网页内容,提取
main_box里的文本。 - 把文本向量化并存储到 FAISS 数据库。
- 让 LLM 生成 5 个改写版本的问题,提高召回率。
- 用这 5 个问题去数据库检索,获取相关文档。
- LLM 读取检索到的文本,并 基于事实回答:
"根据提供的医学文献,异戊巴比妥的成人常用剂量为 100mg,每日 1-2 次。"
总结
🚀 这段代码的目标是:
- 用 多角度查询 提高检索精准度。
- 结合 LLM + 向量数据库 构建 RAG,确保答案基于可靠来源。
- 适用于医学、法律等需要精准回答的领域。
更多推荐

所有评论(0)