Bootstrap

向量存储与检索器

langchain支持从向量数据库和其他来源检索数据,以便与LLM(大型语言模型)工作流程集成。它们对于应用程序来说非常重要,这些应用程序需要获取数据以作为模型推理的一部分进行推理,就像检索增强生成(RAG)的情况一样

需要安装:pip install langchain-chroma

向量存储

向量存储 是可以高效存储和检索嵌入的数据库。存储和搜索非结构化数据的最常见方法之一是将其嵌入并存储生成的嵌入向量, 然后在查询时嵌入非结构化查询并检索与嵌入查询 ‘最相似’ 的嵌入向量。 向量存储负责为您存储嵌入数据并执行向量搜索。

大多数向量存储还可以存储有关嵌入向量的元数据,并支持在相似性搜索之前对该元数据进行过滤, 让您对返回的文档有更多控制。

如何创建和查询向量存储

使用 chroma 向量数据库,它作为库在本地机器上运行。


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_core.runnables import RunnableLambda

#准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

# 实例化一个向量空间
vector = Chroma.from_documents(documents,embedding=ZhipuAIEmbeddings(api_key="5eb20f1f1-------RdKxlr"))


相似性搜索

所有向量存储都暴露了 similarity_search 方法。 这将接收传入的文档,创建它们的嵌入,然后找到所有具有最相似嵌入的文档。



from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_core.runnables import RunnableLambda

#准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

# 实例化一个向量空间
vector = Chroma.from_documents(documents,embedding=ZhipuAIEmbeddings(api_key="5eb20f1f1975487b9a540bb2f8540732.L45evmXhLNRdKxlr"))
# 相似度查询,返回的相似分数,分数越低相似度越高
print(vector.similarity_search_with_score("咖啡猫"))



按向量进行相似性搜索

可以使用 similarity_search_by_vector 搜索与给定嵌入向量相似的文档,该方法接受嵌入向量作为参数,而不是字符串。


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_core.runnables import RunnableLambda

embeddings = ZhipuAIEmbeddings(api_key="5eb20f1---LNRdKxlr")
# 准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]
# 实例化一个向量空间
db = Chroma.from_documents(documents,embedding=embeddings)

embedding_vector = embeddings.embed_query("咖啡猫")
docs = db.similarity_search_by_vector(embedding_vector)
print(docs)


结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\similarity_search_by_vector案例1.py 
Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3
[Document(metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。'), Document(metadata={'source': '哺乳动物宠物文档'}, page_content='兔子是社交动物,需要足够的空间。'), Document(metadata={'source': '哺乳动物宠物文档'}, page_content='狗是伟大的伴侣,以其忠诚和友好而闻名')]

进程已结束,退出代码为 0


异步操作

向量存储通常作为一个单独的服务运行,需要一些IO操作,因此它们可能会被异步调用。这带来了性能上的好处,因为你不会浪费时间等待外部服务的响应。如果你使用异步框架,例如FastAPI,这也可能很重要。

LangChain支持在向量存储上进行异步操作。所有方法都可以使用其异步对应方法调用,前缀为a,表示async。


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
import asyncio

async def main():
    # 准备测试数据
    documents = [
        Document(
            page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
            metadata={"source": "哺乳动物宠物文档"}
        ),
        Document(
            page_content="猫是独立的宠物,通常喜欢自己的空间。",
            metadata={"source": "哺乳动物宠物文档"}
        ),
        Document(
            page_content="兔子是社交动物,需要足够的空间。",
            metadata={"source": "哺乳动物宠物文档"}
        )
    ]

    # 实例化一个向量空间
    vector = Chroma.from_documents(documents, embedding=ZhipuAIEmbeddings(
        api_key="5eb20f1f---lr"))
    # 相似度查询,返回的相似分数,分数越低相似度越高
    rest = await vector.asimilarity_search_with_score("咖啡猫")
    print(rest)

if __name__ == '__main__':
    asyncio.run(main())


结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\asimilarity_search_demo1.py 
[(Document(metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。'), 0.9849778413772583), (Document(metadata={'source': '哺乳动物宠物文档'}, page_content='兔子是社交动物,需要足够的空间。'), 1.3960810899734497), (Document(metadata={'source': '哺乳动物宠物文档'}, page_content='狗是伟大的伴侣,以其忠诚和友好而闻名'), 1.4630534648895264)]
Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3

进程已结束,退出代码为 0


检索器

如何使用向量存储作为检索器

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

从向量存储创建检索器

实例化一个向量存储。使用一个内存中的 FAISS 向量存储:


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document

#准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

embeddings = ZhipuAIEmbeddings(api_key="5eb20f-----NRdKxlr")
vectorstore = FAISS.from_documents(documents,embeddings)

# 实例化一个检索器:
retriever = vectorstore.as_retriever()
rest = retriever.invoke("咖啡猫")
print(rest)

结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\从向量存储创建检索器.py 
[Document(id='c0d17f26-214c-4743-8625-2cdd7d51cc7a', metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。'), Document(id='f3fc64c4-6e39-4386-8019-4424c9409342', metadata={'source': '哺乳动物宠物文档'}, page_content='兔子是社交动物,需要足够的空间。'), Document(id='497965aa-f342-4e3a-b49a-9bcfba47b0dd', metadata={'source': '哺乳动物宠物文档'}, page_content='狗是伟大的伴侣,以其忠诚和友好而闻名')]

进程已结束,退出代码为 0


最大边际相关性检索

默认情况下,向量存储检索器使用相似性搜索。如果底层向量存储支持最大边际相关性搜索,您可以将其指定为搜索类型。

这有效地指定了在底层向量存储上使用的方法(例如,similarity_search、max_marginal_relevance_search等)。


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document

#准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

embeddings = ZhipuAIEmbeddings(api_key="5eb20----hLNRdKxlr")
vectorstore = FAISS.from_documents(documents,embeddings)

# 实例化一个检索器:
retriever = vectorstore.as_retriever(search_type="mmr")
rest = retriever.invoke("咖啡猫")
print(rest)

结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\最大边际相关性检索.py 
[Document(id='467ac45a-1ae1-4ef3-be17-166aa0a28278', metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。'), Document(id='863cd692-ece6-40d2-994b-bab131025e66', metadata={'source': '哺乳动物宠物文档'}, page_content='狗是伟大的伴侣,以其忠诚和友好而闻名'), Document(id='09056f8d-3e5a-4b8c-849a-91d598d34f42', metadata={'source': '哺乳动物宠物文档'}, page_content='兔子是社交动物,需要足够的空间。')]

进程已结束,退出代码为 0


传递搜索参数

我们可以使用 search_kwargs 将参数传递给底层向量存储的搜索方法。


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document

#准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

embeddings = ZhipuAIEmbeddings(api_key="5eb20f1f1975487b9a540bb2f8540732.L45evmXhLNRdKxlr")
vectorstore = FAISS.from_documents(documents,embeddings)
# 设置搜索参数
search_kwargs = {
    "k": 1  # 返回最相似的 1 个文档
}
# 检索
rest = vectorstore.similarity_search("咖啡猫",**search_kwargs)
print(rest)

结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\传递搜索参数.py 
[Document(id='787dbd53-0d71-4e2a-b084-7c8fa7db9c91', metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。')]

进程已结束,退出代码为 0


相似性得分阈值检索

我们可以设置一个相似性得分阈值,仅返回得分高于该阈值的文档。


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document

# 准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

embeddings = ZhipuAIEmbeddings(api_key="5eb20f1---mXhLNRdKxlr")
vectorstore = FAISS.from_documents(documents, embeddings)

# 实例化一个检索器:设置相似性得分阈值
retriever = vectorstore.as_retriever(
    search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.1}  # 注意这个得分如果设置过大会报错,因为计算出来的得分没比它大
)
rest = retriever.invoke("咖啡猫")
print(rest)


结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\相似性得分阈值检索.py 
E:\learn_work_spaces\PythonProject1\.venv\Lib\site-packages\langchain_core\vectorstores\base.py:1077: UserWarning: Relevance scores must be between 0 and 1, got [(Document(id='26387883-6eee-45d0-ad19-83fd217e0a9f', metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。'), np.float32(0.30351537)), (Document(id='f6cf44b3-90f2-4615-b958-32f695c1f404', metadata={'source': '哺乳动物宠物文档'}, page_content='兔子是社交动物,需要足够的空间。'), np.float32(0.0128214955)), (Document(id='7c7ce927-e68c-42a0-b282-907af8ca3d26', metadata={'source': '哺乳动物宠物文档'}, page_content='狗是伟大的伴侣,以其忠诚和友好而闻名'), np.float32(-0.03453505))]
  self.vectorstore.similarity_search_with_relevance_scores(
[Document(id='26387883-6eee-45d0-ad19-83fd217e0a9f', metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。')]

进程已结束,退出代码为 0


指定前 k

我们还可以限制检索器返回的文档数量 k。


from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document

#准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

embeddings = ZhipuAIEmbeddings(api_key="5eb2----RdKxlr")
vectorstore = FAISS.from_documents(documents,embeddings)

# 实例化一个检索器:设置只返回一个
retriever = vectorstore.as_retriever(search_kwargs={"k":1})
rest = retriever.invoke("咖啡猫")
print(rest)

结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe "E:\learn_work_spaces\PythonProject1\检索器\指定前 k.py" 
[Document(id='e225381e-bfda-470f-922d-a3fbd95a5219', metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。')]

进程已结束,退出代码为 0


案例

案例1

构建向量空间


import os
from langchain_community.chat_models import ChatZhipuAI
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_community.embeddings import ZhipuAIEmbeddings


#准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

# 实例化一个向量空间
vector = Chroma.from_documents(documents,embedding=ZhipuAIEmbeddings(api_key="5eb20---------Kxlr"))

print(vector.similarity_search_with_score("咖啡猫"))


结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\构建文档和向量空间1.py 
Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3
[(Document(metadata={'source': '哺乳动物宠物文档'}, page_content='猫是独立的宠物,通常喜欢自己的空间。'), 0.9849778413772583), (Document(metadata={'source': '哺乳动物宠物文档'}, page_content='兔子是社交动物,需要足够的空间。'), 1.3960810899734497), (Document(metadata={'source': '哺乳动物宠物文档'}, page_content='狗是伟大的伴侣,以其忠诚和友好而闻名'), 1.4630534648895264)]

进程已结束,退出代码为 0


案例2

当用户提问时,让AI使用向量空间的数据来帮助回答



import os

from langchain_community.chat_models import ChatZhipuAI
from langchain_community.embeddings import ZhipuAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda, RunnablePassthrough

"""
当用户提问时,让AI使用向量空间的数据来帮助回答
"""
# 设置智普 AI 的 API 密钥
os.environ["ZHIPUAI_API_KEY"] = "5eb20f1f1975487b9a540bb2f8540732.L45evmXhLNRdKxlr"

# 初始化智普 AI 模型
llm = ChatZhipuAI()

# 准备测试数据
documents = [
    Document(
        page_content="狗是伟大的伴侣,以其忠诚和友好而闻名",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="猫是独立的宠物,通常喜欢自己的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    ),
    Document(
        page_content="兔子是社交动物,需要足够的空间。",
        metadata={"source": "哺乳动物宠物文档"}
    )
]

# 实例化一个向量空间
vector = Chroma.from_documents(documents,
                               embedding=ZhipuAIEmbeddings(api_key="5eb20---NRdKxlr"))

# 用RunnableLambda封装成一个检索器节点,返回相似度最高的那个
retriever = RunnableLambda(vector.similarity_search)

# 提示模版
message = """
使用提供的上下文回答这个问题:
{question}
上下文:
{context}
"""
prompt = ChatPromptTemplate.from_messages([('human', message)])
# RunnablePassthrough循序我们将用户的问题之后再传递给prompt 和 llm
chain = {"question": RunnablePassthrough(), "context": retriever} | prompt | llm
rest = chain.invoke("请介绍下猫")
print(rest)


结果:


E:\learn_work_spaces\PythonProject1\.venv\Scripts\python.exe E:\learn_work_spaces\PythonProject1\检索器.py 
Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3
content='根据提供的上下文,猫是独立的宠物,通常喜欢自己的空间。相比起狗以其忠诚和友好而闻名,猫显得更加自主和独立。然而,具体的介绍内容并没有详细阐述,所以我们只能从这些简洁的描述中了解到猫的这些基本特性。兔子则被提及为社交动物,需要足够的空间,但这与猫的特性不直接相关。' additional_kwargs={} response_metadata={'token_usage': {'completion_tokens': 78, 'prompt_tokens': 96, 'total_tokens': 174}, 'model_name': 'glm-4', 'finish_reason': 'stop'} id='run-9aa17b55-8e51-4373-a130-1ecf8ba91e12-0'

进程已结束,退出代码为 0


参考文档:
langchain如何创建和查询向量存储

检索器

;