一架梯子,一头程序猿,仰望星空!
LangChain教程(Python版本) > 内容正文

LangChain 检索器(Retriever)


Retrievers

检索器(retriever)是LangChain封装的一个接口,它可以根据非结构化查询返回相关文档。它比向量存储更通用。检索器不需要能够存储文档,只需要返回(或检索)即可。向量存储可以用作检索器的底层实现,LangChain支持多种retriever接口的底层实现。

Retriever入门

LangChain中BaseRetriever类的接口设计如下所示:

from abc import ABC, abstractmethod
from typing import Any, List
from langchain.schema import Document
from langchain.callbacks.manager import Callbacks

class BaseRetriever(ABC):
    ...
    def get_relevant_documents(
        self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
    ) -> List[Document]:
        """检索与查询相关的文档。
        Args:
            query: 要查找相关文档的字符串
            callbacks: 回调管理器或回调列表
        Returns:
            相关文档列表
        """
        ...

    async def aget_relevant_documents(
        self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
    ) -> List[Document]:
        """异步获取与查询相关的文档。
        Args:
            query: 要查找相关文档的字符串
            callbacks: 回调管理器或回调列表
        Returns:
            相关文档列表
        """
        ...

检索器的接口定义很简单,可以调用 get_relevant_documents 或异步 aget_relevant_documents 方法来检索与查询问题相关的文档,至于怎么查询相关文档,是由具体的检索器(Retriever)实现类实现。

本章主要学习的检索器类型是 Vectorstore 检索器。

为了理解什么是 Vectorstore 检索器,下面章节以Chroma向量数据库为例介绍检索器。

默认情况下,LangChain 使用 Chroma 作为向量(矢量)库来索引和搜索向量数据。

我们首先需要安装 chromadb

pip install chromadb

这个例子展示了如何基于文档数据进行问答。我们选择这个例子作为入门的示例,因为它很好地结合了许多不同的元素(文本分割器、向量计算、向量存储),然后还展示了如何在Chain中使用这些组件。

  • 提示:Chain是LangChain设计的任务链,大致意思就是把多个子任务,串起来按顺序执行,后续的章节会学习到相关内容。

基于文档数据的问答包括四个步骤:

  1. 创建索引
  2. 从索引中创建检索器
  3. 创建问答链
  4. 进行问题提问!

每个步骤都有多个子步骤和潜在的配置。在本章节,我们主要关注(1)。

首先,让我们导入一些基础类。

from langchain.chains import RetrievalQA
from langchain.llms import OpenAI

我们先加载用于测试的文档数据,后面将根据这些文档进行问答 ,你可以到github下载测试数据[state_of_the_union.txt](https://github.com/hwchase17/langchain/blob/master/docs/extras/modules/state_of_the_union.txt)文件

from langchain.document_loaders import TextLoader
loader = TextLoader('../state_of_the_union.txt', encoding='utf8')

创建索引

我们可以使用 VectorstoreIndexCreator创建向量索引,用于存储向量数据。

from langchain.indexes import VectorstoreIndexCreator
# 通过前面的loader加载文档数据,并存储到向量数据库
index = VectorstoreIndexCreator().from_loaders([loader])
# 输出日志
Running Chroma using direct local API.
Using DuckDB in-memory for database. Data will be transient.

现在我们已经创建了索引,可以根据问题查询相关文档内容。

query = "What did the president say about Ketanji Brown Jackson"
index.query(query)
# 查询结果
" The president said that Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He also said that she is a consensus builder and has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans."
# 查询相关文档,并且返回数据源信息
query = "What did the president say about Ketanji Brown Jackson"
index.query_with_sources(query)
# 查询结果
{'question': 'What did the president say about Ketanji Brown Jackson',
     'answer': " The president said that he nominated Circuit Court of Appeals Judge Ketanji Brown Jackson, one of the nation's top legal minds, to continue Justice Breyer's legacy of excellence, and that she has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.\n",
     'sources': '../state_of_the_union.txt'}

VectorstoreIndexCreator返回的是VectorStoreIndexWrapper,提供了queryquery_with_sources文本相似度查询功能。如果我们只想直接访问vectorstore,引用底层存储引擎,这可以这样。

index.vectorstore
<langchain.vectorstores.chroma.Chroma at 0x119aa5940>

如果我们想要访问VectorstoreRetriever,可以使用以下方式:

index.vectorstore.as_retriever(
VectorStoreRetriever(vectorstore=<langchain.vectorstores.chroma.Chroma object at 0x119aa5940>, search_kwargs={})

深入理解

前面简单的介绍索引的创建,底层索引是如何创建的?

很多实现都隐藏在这个VectorstoreIndexCreator中。

在文档加载后,有三个主要步骤:

  1. 将文档按指定大小分片,切割成一个个文本片段
  2. 为每个文档片段计算向量
  3. 将文档和向量存储到向量数据库中

下面通过代码一步步操作一遍

# 加载文档
documents = loader.load()

接下来,我们将把文档分成块。

from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

然后我们选择使用OpenAI的嵌入模型,计算向量。

from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()

然后把切片好文档内容texts和指定的向量计算模型embeddings,存储到向量数据库中

from langchain.vectorstores import Chroma
db = Chroma.from_documents(texts, embeddings)
# 输出日志
Running Chroma using direct local API.
Using DuckDB in-memory for database. Data will be transient.

这样就基于向量数据库,创建了索引。然后,我们就可以直接引用检索器接口。

retriever = db.as_retriever()

然后,和之前一样,我们创建一个问答链(chain)并用它来回答问题!

# 创建问答链,问答链可以指定使用的AI模型,检索器(retriever)
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=retriever)
# 向问答链提问,问答链内部会先根据问题,到检索器(retriever)查询相关的文档片段,然后拼接到提示词里面,提交给AI,让AI回答问题。
# 这里可以简单了解,langchain的chain(任务链)机制,其实就是封装好的任务模块,会自动调用前面介绍的各种组件。
query = "What did the president say about Ketanji Brown Jackson"
qa.run(query)
# 问答结果
" The President said that Judge Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He said she is a consensus builder and has received a broad range of support from organizations such as the Fraternal Order of Police and former judges appointed by Democrats and Republicans."

VectorstoreIndexCreator只是所有逻辑的一个包装器。它可以配置所使用的文本分割器、嵌入式和向量存储。例如,您可以将其配置如下:

index_creator = VectorstoreIndexCreator(
    vectorstore_cls=Chroma,
    embedding=OpenAIEmbeddings(),
    text_splitter=CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
)


关联主题