老铁们,今天我们来聊一聊如何使用MultiQueryRetriever这个神器来提升我们的文本检索能力。传统的基于向量的数据库检索方法虽然在某些场景下表现不错,但偶尔也会因为查询语句的微小变化或嵌入表示能力的不足而给出不理想的结果。这个时候,老铁们可能会想到手动调整提示词来优化,但是如此繁琐的操作并不友好。幸运的是,MultiQueryRetriever帮我们自动化了这个过程。它通过使用大型语言模型(LLM)对用户输入的查询生成多个视角的版本,进而从不同角度获取更全面的文档集合。
技术背景介绍
MultiQueryRetriever是一种利用LLM生成多视角查询的检索工具。它通过对用户的查询生成多种版本,从而获取到更多相关的文档。这种方法可以有效缓解基于距离的向量检索在语义捕捉不足时带来的局限。
原理深度解析
MultiQueryRetriever的核心在于利用LLM生成多个不同视角的查询语句,通过这些不同视角的查询去检索可能相关的文档集,并通过对结果去重,得到更丰富的结果集。说白了就是给同一个问题增加不同的“解读”,从而提升检索的全面性。
实战代码演示
我们通过一个实际应用来演示如何使用MultiQueryRetriever。这里使用了Lilian Weng在RAG教程中的一篇博客来构建样本向量数据库。
# 构建样本向量数据库
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 加载博客文章
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
data = loader.load()
# 文本分割
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
splits = text_splitter.split_documents(data)
# 向量数据库构建
embedding = OpenAIEmbeddings()
vectordb = Chroma.from_documents(documents=splits, embedding=embedding)
# 使用MultiQueryRetriever
from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain_openai import ChatOpenAI
question = "What are the approaches to Task Decomposition?"
llm = ChatOpenAI(temperature=0)
retriever_from_llm = MultiQueryRetriever.from_llm(
retriever=vectordb.as_retriever(), llm=llm
)
# 设置日志查看生成的查询
import logging
logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)
unique_docs = retriever_from_llm.invoke(question)
print(len(unique_docs))
在上面的代码中,我们设置了一个问题"任务分解的方法有哪些?",MultiQueryRetriever生成了多个不同版本的查询,最终检索到了丰富的文档集。你会看到日志中记录的生成的查询版本,帮助你了解其在背后做了哪些工作。
优化建议分享
在使用MultiQueryRetriever时,建议通过自定义提示词模板来更好地控制生成的查询。通过一个PromptTemplate,可以对输入的问题生成更符合实际需求的多版本查询。
from typing import List
from langchain_core.output_parsers import BaseOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
class LineListOutputParser(BaseOutputParser[List[str]]):
def parse(self, text: str) -> List[str]:
lines = text.strip().split("\n")
return list(filter(None, lines))
output_parser = LineListOutputParser()
QUERY_PROMPT = PromptTemplate(
input_variables=["question"],
template="""You are an AI language model assistant. Your task is to generate five
different versions of the given user question to retrieve relevant documents from a vector
database. By generating multiple perspectives on the user question, your goal is to help
the user overcome some of the limitations of the distance-based similarity search.
Provide these alternative questions separated by newlines.
Original question: {question}"""
)
llm_chain = QUERY_PROMPT | llm | output_parser
question = "What are the approaches to Task Decomposition?"
retriever = MultiQueryRetriever(
retriever=vectordb.as_retriever(), llm_chain=llm_chain, parser_key="lines"
)
unique_docs = retriever.invoke("What does the course say about regression?")
print(len(unique_docs))
补充说明和总结
这波操作可以说是相当丝滑,MultiQueryRetriever通过自动化生成多视角查询为我们提供了一种新的优化检索结果的方法。如果你也希望自己的检索能够在更为复杂的语义中表现优异,不妨试试这个工具。
今天的技术分享就到这里,希望对大家有帮助。开发过程中遇到问题也可以在评论区交流~
—END—