向量存储和检索器
本教程将使您熟悉LangChain的向量存储和检索器抽象。这些抽象旨在支持从(向量)数据库和其他来源检索数据,以便与LLM工作流集成。它们对于在模型推理过程中获取数据以进行推理的应用程序非常重要,例如检索增强生成(RAG),有关RAG的更多信息,请参见我们的RAG教程这里。
概念
本指南专注于文本数据的检索。我们将涵盖以下概念:
- 文档;
- 向量存储;
- 检索器。
设置
Jupyter Notebook
此教程及其他教程最方便的运行方式是使用 Jupyter notebook。有关安装说明,请参见 这里。
安装
本教程需要 langchain
、langchain-chroma
和 langchain-openai
包:
- Pip
- Conda
pip install langchain langchain-chroma langchain-openai
conda install langchain langchain-chroma langchain-openai -c conda-forge
有关更多详细信息,请参阅我们的 安装指南。
LangSmith
您使用 LangChain 构建的许多应用程序将包含多个步骤和多次调用 LLM。 随着这些应用程序变得越来越复杂,能够检查您的链或代理内部究竟发生了什么变得至关重要。 实现这一点的最佳方式是使用 LangSmith。
在您在上述链接注册后,请确保设置您的环境变量以开始记录跟踪:
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY="..."
或者,如果在笔记本中,您可以使用以下代码设置它们:
import getpass
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()
文档
LangChain 实现了一个 Document 抽象,旨在表示一个文本单元及其相关元数据。它有两个属性:
page_content
: 一个表示内容的字符串;metadata
: 一个包含任意元数据的字典。
metadata
属性可以捕获关于文档来源、与其他文档的关系以及其他信息。请注意,单个 Document
对象通常表示较大文档的一部分。
让我们生成一些示例文档:
from langchain_core.documents import Document
documents = [
Document(
page_content="Dogs are great companions, known for their loyalty and friendliness.",
metadata={"source": "mammal-pets-doc"},
),
Document(
page_content="Cats are independent pets that often enjoy their own space.",
metadata={"source": "mammal-pets-doc"},
),
Document(
page_content="Goldfish are popular pets for beginners, requiring relatively simple care.",
metadata={"source": "fish-pets-doc"},
),
Document(
page_content="Parrots are intelligent birds capable of mimicking human speech.",
metadata={"source": "bird-pets-doc"},
),
Document(
page_content="Rabbits are social animals that need plenty of space to hop around.",
metadata={"source": "mammal-pets-doc"},
),
]
在这里,我们生成了五个文档,包含指示三个不同“来源”的元数据。
向量存储
向量搜索是一种常见的存储和搜索非结构化数据(例如非结构化文本)的方法。其思想是存储与文本相关联的数值向量。给定一个查询,我们可以将其嵌入为相同维度的向量,并使用向量相似性度量来识别存储中相关的数据。
LangChain VectorStore 对象包含将文本和 Document
对象添加到存储中的方法,并使用各种相似性度量对其进行查询。它们通常使用嵌入模型进行初始化,这决定了文本数据如何转换为数值向量。
LangChain 包含一套与不同向量存储技术的集成。一些向量存储由提供商托管(例如,各种云提供商),并需要特定的凭证才能使用;一些(如Postgres)在可以本地运行或通过第三方运行的单独基础设施中运行;其他的可以在内存中运行以支持轻量工作负载。这里我们将演示使用Chroma的 LangChain VectorStores,它包括一个内存中的实现。
要实例化一个向量存储,我们通常需要提供一个嵌入模型,以指定文本应如何转换为数值向量。这里我们将使用OpenAI 嵌入。
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
vectorstore = Chroma.from_documents(
documents,
embedding=OpenAIEmbeddings(),
)
在这里调用 .from_documents
将把文档添加到向量存储中。VectorStore 实现了可以在对象实例化后调用的添加文档的方法。大多数实现将允许您连接到现有的向量存储——例如,通过提供客户端、索引名称或其他信息。有关特定集成的更多详细信息,请参见文档。
一旦我们实例化了一个包含文档的 VectorStore
,我们就可以对其进行查询。VectorStore 包含查询的方法:
- 同步和异步;
- 通过字符串查询和通过向量;
- 是否返回相似性分数;
- 按相似性和最大边际相关性(以平衡查询的相似性与检索结果的多样性)。
这些方法的输出通常会包含一系列Document 对象。
示例
根据与字符串查询的相似性返回文档:
vectorstore.similarity_search("cat")
[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'})]
异步查询:
await vectorstore.asimilarity_search("cat")
[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'})]
返回分数:
# 注意提供者实现不同的分数;Chroma 在这里
# 返回一个距离度量,应该与相似性成反比。
vectorstore.similarity_search_with_score("cat")
[(Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
0.3751849830150604),
(Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
0.48316916823387146),
(Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
0.49601367115974426),
(Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'}),
0.4972994923591614)]
根据嵌入查询的相似性返回文档:
embedding = OpenAIEmbeddings().embed_query("cat")
vectorstore.similarity_search_by_vector(embedding)
[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'})]
了解更多:
检索器
LangChain VectorStore
对象不继承 Runnable,因此无法立即集成到 LangChain 表达式语言 链 中。
LangChain 检索器 是 Runnables,因此它们实现了一套标准的方法(例如,同步和异步的 invoke
和 batch
操作),并设计为可以纳入 LCEL 链中。
我们可以自己创建一个简单版本,而无需继承 Retriever
。如果我们选择希望用于检索文档的方法,我们可以轻松创建一个 runnable。下面我们将围绕 similarity_search
方法构建一个:
from typing import List
from langchain_core.documents import Document
from langchain_core.runnables import RunnableLambda
retriever = RunnableLambda(vectorstore.similarity_search).bind(k=1) # select top result
retriever.batch(["cat", "shark"])
[[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'})],
[Document(page_content='Goldfish are popular pets for beginners, requiring relatively simple care.', metadata={'source': 'fish-pets-doc'})]]
Vectorstores 实现了一个 as_retriever
方法,该方法将生成一个检索器,特别是一个 VectorStoreRetriever。这些检索器包括特定的 search_type
和 search_kwargs
属性,用于识别调用底层向量存储的方法,以及如何对其进行参数化。例如,我们可以用以下代码复制上述内容:
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 1},
)
retriever.batch(["cat", "shark"])
[[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'})],
[Document(page_content='Goldfish are popular pets for beginners, requiring relatively simple care.', metadata={'source': 'fish-pets-doc'})]]
VectorStoreRetriever
支持 "similarity"
(默认)、"mmr"
(最大边际相关性,上述描述)和 "similarity_score_threshold"
的搜索类型。我们可以使用后者通过相似度分数对检索器输出的文档进行阈值处理。
检索器可以轻松集成到更复杂的应用程序中,例如检索增强生成(RAG)应用程序,它将给定问题与检索的上下文结合成 LLM 的提示。下面我们展示一个最小示例。
- OpenAI
- Anthropic
- Azure
- Cohere
- NVIDIA
- FireworksAI
- Groq
- MistralAI
- TogetherAI
pip install -qU langchain-openai
import getpass
import os
os.environ["OPENAI_API_KEY"] = getpass.getpass()
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
pip install -qU langchain-anthropic
import getpass
import os
os.environ["ANTHROPIC_API_KEY"] = getpass.getpass()
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
pip install -qU langchain-openai
import getpass
import os
os.environ["AZURE_OPENAI_API_KEY"] = getpass.getpass()
from langchain_openai import AzureChatOpenAI
llm = AzureChatOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],
)
pip install -qU langchain-google-vertexai
import getpass
import os
os.environ["GOOGLE_API_KEY"] = getpass.getpass()
from langchain_google_vertexai import ChatVertexAI
llm = ChatVertexAI(model="gemini-1.5-flash")
pip install -qU langchain-cohere
import getpass
import os
os.environ["COHERE_API_KEY"] = getpass.getpass()
from langchain_cohere import ChatCohere
llm = ChatCohere(model="command-r-plus")
pip install -qU langchain-nvidia-ai-endpoints
import getpass
import os
os.environ["NVIDIA_API_KEY"] = getpass.getpass()
from langchain import ChatNVIDIA
llm = ChatNVIDIA(model="meta/llama3-70b-instruct")
pip install -qU langchain-fireworks
import getpass
import os
os.environ["FIREWORKS_API_KEY"] = getpass.getpass()
from langchain_fireworks import ChatFireworks
llm = ChatFireworks(model="accounts/fireworks/models/llama-v3p1-70b-instruct")
pip install -qU langchain-groq
import getpass
import os
os.environ["GROQ_API_KEY"] = getpass.getpass()
from langchain_groq import ChatGroq
llm = ChatGroq(model="llama3-8b-8192")
pip install -qU langchain-mistralai
import getpass
import os
os.environ["MISTRAL_API_KEY"] = getpass.getpass()
from langchain_mistralai import ChatMistralAI
llm = ChatMistralAI(model="mistral-large-latest")
pip install -qU langchain-openai
import getpass
import os
os.environ["TOGETHER_API_KEY"] = getpass.getpass()
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
base_url="https://api.together.xyz/v1",
api_key=os.environ["TOGETHER_API_KEY"],
model="mistralai/Mixtral-8x7B-Instruct-v0.1",
)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
message = """
Answer this question using the provided context only.
{question}
Context:
{context}
"""
prompt = ChatPromptTemplate.from_messages([("human", message)])
rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm
response = rag_chain.invoke("tell me about cats")
print(response.content)
Cats are independent pets that often enjoy their own space.
了解更多:
检索策略可以丰富而复杂。例如:
- 我们可以从查询中推断出硬规则和过滤器(例如,“使用2020年之后发布的文档”);
- 我们可以以某种方式返回与检索上下文相关的文档(例如,通过某些文档分类法);
- 我们可以为每个上下文单元生成多个嵌入;
- 我们可以从多个检索器集成结果;
- 我们可以为文档分配权重,例如,为近期文档赋予更高的权重。
如何指南的检索器部分涵盖了这些和其他内置检索策略。
扩展BaseRetriever类以实现自定义检索器也很简单。请参阅我们的如何指南这里。