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

数据检索器


检索器

检索器是一个接口,根据一个非结构化查询返回文档。它比向量存储更通用。检索器不需要能够存储文档,只需要能够返回(或检索)文档。向量存储可以用作检索器的支撑,但也可以使用其他类型的检索器。

开始使用

LangChain.js中BaseRetriever类的公共API如下:

export abstract class BaseRetriever {
  abstract getRelevantDocuments(query: string): Promise;
}

就是这么简单!您可以调用getRelevantDocuments方法来获取与查询相关的文档,其中”相关性”由具体的检索器对象定义。

当然,我们还会帮助构建我们认为有用的检索器。LangChain中的主要检索器类型是向量存储检索器。我们将在这里重点介绍它。

注意:在阅读之前,了解什么是向量存储非常重要。

这个示例展示了在文档上进行问题回答。我们选择这个示例作为入门示例,因为它很好地结合了许多不同的元素(文本分割器、嵌入、向量存储),并展示了如何在链中使用它们。

文档上的问题回答包括四个步骤:

  1. 创建索引
  2. 根据该索引创建检索器
  3. 创建一个问题回答链
  4. 提问!

每个步骤都有多个子步骤和可能的配置,但我们将通过使用HNSWLib来演示一个常见的流程,它是一个本地向量存储。这假设您正在使用Node,但如果需要,您可以替换为其他集成。

首先,安装所需的依赖:

// npm
npm install -S hnswlib-node
//Yarn
yarn add hnswlib-node
// pnpm
pnpm add hnswlib-node

state_of_the_union.txt 就是一个普通的文本文件,你可以随意替换成其他测试文本。

import { OpenAI } from "langchain/llms/openai";
import { RetrievalQAChain } from "langchain/chains";
import { HNSWLib } from "langchain/vectorstores/hnswlib";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import * as fs from "fs";

// 初始化要用于回答问题的LLM。
const model = new OpenAI({});
const text = fs.readFileSync("state_of_the_union.txt", "utf8");
const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000 });
const docs = await textSplitter.createDocuments([text]);

// 从文档创建一个向量存储。
const vectorStore = await HNSWLib.fromDocuments(docs, new OpenAIEmbeddings());

// 初始化一个包装在向量存储周围的检索器
const vectorStoreRetriever = vectorStore.asRetriever();

// 创建一个使用OpenAI LLM和HNSWLib向量存储的链
const chain = RetrievalQAChain.fromLLM(model, vectorStoreRetriever);
const res = await chain.call({
  query: "总统对布雷耶法官有什么说法?",
});
console.log({ res });
/*
{
  res: {
    text: '总统表示布雷耶法官是一名陆军退伍军人、宪法学者和美国最高法院的退休法官,并对他的服务表示感谢。'
  }
}
*/

让我们来看看这里发生了什么。

  1. 我们首先加载一段长文本,并使用文本分割器将其分割成较小的文档。然后,我们将这些文档(还使用传递的OpenAIEmbeddings实例嵌入文档)加载到我们的向量存储中的HNSWLib中,从而创建我们的索引。
  2. 虽然我们可以直接查询向量存储,但我们将向量存储转换为检索器,以便以正确的格式返回检索到的文档,供问题回答链使用。
  3. 我们使用.fromLLM方法初始化一个RetrievalQAChain,稍后在步骤4中调用它。
  4. 我们提问问题!

有关具体检索器的更详细介绍,请参见各个部分。

基于向量存储的检索器

向量存储检索器是使用向量存储来检索文档的检索器。它是对向量存储类的轻量级包装,使其符合Retriever接口。它使用向量存储中实现的搜索方法,如相似度搜索和MMR,来查询向量存储中的文本。

一旦构建了一个向量存储,构建一个检索器就非常容易。我们来看一个示例:

const vectorStore = ...
const retriever = vectorStore.asRetriever();

这里有一个更完整的示例:

import { OpenAI } from "langchain/llms/openai";
import { RetrievalQAChain } from "langchain/chains";
import { HNSWLib } from "langchain/vectorstores/hnswlib";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import * as fs from "fs";

// 初始化用于回答问题的LLM。
const model = new OpenAI({});
const text = fs.readFileSync("state_of_the_union.txt", "utf8");
const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000 });
const docs = await textSplitter.createDocuments([text]);

// 从文档中创建一个向量存储。
const vectorStore = await HNSWLib.fromDocuments(docs, new OpenAIEmbeddings());

// 初始化一个围绕向量存储的检索器包装器
const vectorStoreRetriever = vectorStore.asRetriever();

const docs = retriever.getRelevantDocuments("关于Ketanji Brown Jackson他说了什么");

配置

您可以指定要检索的最大文档数,以及在检索时要使用的向量存储特定的过滤器。

// 返回最多2个`metadataField`设置为`"value"`的文档
const retriever = vectorStore.asRetriever(2, { metadataField: "value" });

const docs = retriever.getRelevantDocuments("关于Ketanji Brown Jackson他说了什么");


关联主题