LangChain教程 - 向量存储与检索器

系列文章索引
LangChain教程 - 系列文章

介绍

在这个教程中,你将了解 LangChain 的向量存储和检索器抽象。这些抽象旨在支持从(向量)数据库和其他来源检索数据,并将其集成到大语言模型(LLM)的工作流程中。这在基于检索增强生成(RAG)等应用中尤为重要,因为这些应用需要在模型推理过程中提取数据供模型进行推理。

概念

本教程将主要关注文本数据的检索,涵盖以下几个核心概念:

  1. 文档(Documents):表示文本数据的基本单位,通常包含内容和元数据。
  2. 向量存储(Vector Stores):通过将文本转换为向量存储起来,便于快速相似性搜索。
  3. 检索器(Retrievers):基于向量存储,执行高效的文本检索。

环境准备

Jupyter Notebook

你可以在 Jupyter Notebook 中运行本教程。如果尚未安装,请参考 Jupyter 安装指南

安装依赖包

我们将使用以下 Python 包:

  • langchain
  • langchain-chroma
  • langchain-openai

通过以下命令安装:

pip install langchain langchain-chroma langchain-openai

安装后,我们就可以开始搭建 LangChain 环境。


文档(Documents)

LangChain 实现了一个文档抽象,用来表示一段文本及其关联的元数据。每个文档都有两个属性:

  • page_content:表示文档的文本内容;
  • metadata:一个包含文档相关信息的字典,比如文档来源等。

我们可以通过以下代码生成一些示例文档:

from langchain_core.documents import Document

documents = [
    Document(
        page_content="狗是忠诚和友好的伴侣。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是一种独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是新手的理想宠物,照顾起来相对简单。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是一种社交性很强的动物,喜欢有足够的空间活动。",
        metadata={"source": "mammal-pets-doc"},
    ),
]

这些文档示例为后续的检索实验提供了基础数据集。


向量存储(Vector Stores)

向量存储是一种常见的用于存储和检索非结构化数据(如文本)的方式。它的核心思想是将文本数据转换为数值向量,并使用向量相似性度量来进行查询。

LangChain 提供了多种向量存储的集成接口,可以将文本或文档添加到向量存储中,并通过多种相似性度量方法进行查询。我们这里使用 Chroma 的内存实现来演示向量存储的使用。

首先,我们需要一个嵌入模型来将文本转换为向量。下面的示例使用 OpenAI 的嵌入模型:

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

vectorstore = Chroma.from_documents(
    documents,
    embedding=OpenAIEmbeddings(),
)

from_documents 方法会将文档添加到向量存储中。你可以使用 similarity_search 方法来基于字符串查询文档。

results = vectorstore.similarity_search("猫")
for doc in results:
    print(doc.page_content)

这个查询会返回与 “猫” 最相关的文档。


检索器(Retrievers)

LangChain 提供了检索器(Retriever)接口,用于执行标准化的检索操作。检索器可以集成到复杂的应用中,例如结合检索上下文和问题的 RAG 应用。我们可以使用向量存储的 as_retriever 方法生成一个检索器。

retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 1})

然后我们可以通过批量查询来检索多个文档:

results = retriever.batch(["猫", "鲨鱼"])
for res in results:
    for doc in res:
        print(doc.page_content)

通过这种方式,我们可以根据相似性从向量存储中检索与多个查询相关的文档。


检索增强生成(RAG)应用示例

检索器可以方便地集成到 RAG 应用中,将问题和检索到的上下文结合生成答案。下面是一个简单的 RAG 实现示例:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

llm = ChatOpenAI(model="gpt-4o-mini")

message = """
请根据提供的上下文回答问题。

问题:
{question}

上下文:
{context}
"""

prompt = ChatPromptTemplate.from_messages([("human", message)])

rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm

response = rag_chain.invoke("告诉我关于猫的事情")
print(response.content)

这个例子展示了如何使用 LangChain 结合检索器和语言模型完成复杂的生成任务。


完整代码

# 导入必要的包
from langchain_core.documents import Document
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI

import os
import getpass

# 设置 OpenAI API Key
os.environ["OPENAI_API_KEY"] = getpass.getpass("输入你的 OpenAI API Key: ")

# 生成示例文档
documents = [
    Document(
        page_content="狗是忠诚和友好的伴侣。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是一种独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是新手的理想宠物,照顾起来相对简单。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是一种社交性很强的动物,喜欢有足够的空间活动。",
        metadata={"source": "mammal-pets-doc"},
    ),
]

# 初始化向量存储(使用 OpenAI 的嵌入模型)
vectorstore = Chroma.from_documents(
    documents,
    embedding=OpenAIEmbeddings(),
)

# 执行基于相似性的查询
print("执行基于相似性的查询:")
results = vectorstore.similarity_search("猫")
for i, doc in enumerate(results, 1):
    print(f"文档 {i} 内容: {doc.page_content}\n")

# 创建一个检索器并进行批量查询
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 1})
print("执行批量查询:")
batch_results = retriever.batch(["猫", "鲨鱼"])
for i, res in enumerate(batch_results, 1):
    for doc in res:
        print(f"批量查询 {i} 结果: {doc.page_content}\n")

# 检索增强生成(RAG)示例
llm = ChatOpenAI(model="gpt-4o-mini")

message = """
请根据提供的上下文回答问题。

问题:
{question}

上下文:
{context}
"""

prompt = ChatPromptTemplate.from_messages([("human", message)])

rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm

# 执行 RAG 查询
response = rag_chain.invoke("告诉我关于猫的事情")
print(f"RAG 生成结果: {response.content}")

总结

通过本教程,你应该已经了解了 LangChain 的向量存储与检索器的基本概念和使用方法。这些工具为构建复杂的基于 LLM 的应用提供了强大的数据检索能力。你可以进一步结合这些概念开发更加智能的应用。

附1:Chroma存储位置

Chroma.from_documents 是 LangChain 中使用 Chroma 向量数据库的一个方法,它将文档内容转换为嵌入向量并存储在 Chroma 中。具体存储位置取决于 Chroma 的配置。如果没有特别指定,默认情况下数据是存储在内存中,即会在程序运行期间保存在 RAM 中,并在程序结束时丢失。

然而,Chroma 也可以配置为将数据存储到磁盘或其他数据库持久化存储。默认情况下,它有以下几种存储选项:

  1. 内存存储(In-memory):默认情况下,Chroma 将数据存储在内存中,这非常适合测试或小规模实验。数据不会被持久化,程序结束后数据会丢失。

  2. 磁盘存储(Persistent Storage on Disk):你可以指定 Chroma 将数据持久化到磁盘中,这样即使程序结束,数据也不会丢失。你可以通过指定 persist_directory 参数来设置存储路径。

    示例:

    vectorstore = Chroma.from_documents(
        documents,
        embedding=OpenAIEmbeddings(),
        persist_directory="./chroma_storage"  # 指定存储目录
    )
    

    这会将数据保存到当前目录下的 chroma_storage 文件夹中。

  3. 其他数据库(Custom Storage Options):Chroma 还可以与其他存储后端集成,比如通过 PostgreSQL 等数据库进行存储。此时你需要配置 Chroma 连接到外部数据库。

持久化向量存储的示例

如果你想要将向量数据持久化到磁盘,可以使用 persist_directory 参数,并在完成插入数据后调用 persist() 方法来保存数据:

vectorstore = Chroma.from_documents(
    documents,
    embedding=OpenAIEmbeddings(),
    persist_directory="./chroma_storage"  # 指定存储路径
)

# 向量存储后调用 persist() 方法进行持久化
vectorstore.persist()
重新加载持久化数据

如果你已经将数据持久化到磁盘并想在下次程序运行时重新加载这些数据,可以直接通过指定 persist_directory 来加载:

vectorstore = Chroma(
    embedding_function=OpenAIEmbeddings(),
    persist_directory="./chroma_storage"  # 指定持久化路径
)

# 现在可以进行查询
results = vectorstore.similarity_search("猫")
for doc in results:
    print(doc.page_content)

这种方式允许你在不同的会话之间保留和使用向量存储的数据。

总结
  • 默认情况下,Chroma.from_documents 将向量数据存储在内存中,不会持久化。
  • 你可以通过设置 persist_directory 将数据存储到磁盘,并通过 persist() 方法确保数据保存。
  • 你还可以配置 Chroma 连接到外部存储后端,如 PostgreSQL,以支持更复杂的存储需求。
### LangChain检索器实现使用 #### 实现细节 LangChain的`runnables`包提供了构建可执行组件的能力,这些组件可以用于创建复杂的流水线和工作流。对于检索器而言,其实现主要依赖于向量数据库和其他索引结构来高效地查找最相似的数据项[^1]。 在具体实践中,检索器通常会集成诸如FAISS、ChromaDB或其他类似的库来进行高效的近似最近邻搜索(Approximate Nearest Neighbor Search, ANN)。这使得即使面对大规模数据集也能保持良好的性能表现。当用户查询到来时,检索器负责将输入转换成适合ANN算法处理的形式,并返回一系列潜在的相关文档或片段给下游模块进一步分析处理[^2]。 #### 使用方法 为了利用LangChain中的检索功能,在编写代码之前应当先准备好要被索引的内容以及相应的元数据。下面给出了一段Python代码作为示例: ```python from langchain.retrievers import Retriever import faiss # 或者其他支持的向量存储引擎 # 初始化Faiss索引并加载已有的向量集合 index = faiss.read_index("path/to/index") retriever = Retriever( index=index, input_transformer=lambda query: transform_query_to_vector(query), ) results = retriever.retrieve("example user query", top_k=5) for result in results: print(f"Document ID: {result['id']}, Score: {result['score']}") ``` 这段脚本展示了如何设置一个基于Faiss的检索实例,并通过简单的lambda表达式定义了从自然语言查询到向量表示之间的映射关系。最后调用了`retrieve()`函数获取前五个匹配度最高的条目。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值