Skip to main content

Qdrant

Qdrant(读作:quadrant)是一个向量相似性搜索引擎。它提供了一个方便的API来存储、搜索和管理带有附加有效负载的点-向量的生产就绪服务。Qdrant专为扩展过滤支持而设计,使其适用于各种神经网络或基于语义的匹配、分面搜索和其他应用。

本笔记本展示了如何使用与Qdrant向量数据库相关的功能。

有多种运行Qdrant的模式,根据所选择的模式,会有一些细微的差异。选项包括:

  • 本地模式,无需服务器
  • 在企业内部部署服务器
  • Qdrant云

请参阅安装说明

pip install qdrant-client

我们想要使用OpenAIEmbeddings,所以我们需要获取OpenAI API密钥。

import os
import getpass

os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")
    OpenAI API Key: ········
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Qdrant
from langchain.document_loaders import TextLoader
loader = TextLoader("../../../state_of_the_union.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings()

从LangChain连接到Qdrant

## Connecting to Qdrant from LangChain

本地模式 (Local mode)

Python客户端允许您在本地模式下运行相同的代码,而无需运行Qdrant服务器。这对于测试和调试以及计划仅存储少量向量的情况非常有用。嵌入向量可以完全保存在内存中,也可以持久化到磁盘上。

### Local mode

Python客户端允许您在本地模式下运行相同的代码,而无需运行Qdrant服务器。这对于测试和调试以及计划仅存储少量向量的情况非常有用。嵌入向量可以完全保存在内存中,也可以持久化到磁盘上。

一级标题 (Primary Title)

内存中

对于一些测试场景和快速实验,您可能更喜欢将所有数据仅保存在内存中,因此当客户端被销毁时(通常在脚本/笔记本的末尾),数据将丢失。

qdrant = Qdrant.from_documents(
docs,
embeddings,
location=":memory:", # 仅使用内存存储的本地模式
collection_name="my_documents",
)

一级标题 (Primary Title)

磁盘存储 (On-disk storage)

本地模式,不使用 Qdrant 服务器,也可以将向量存储在磁盘上,以便在运行之间保持持久化。

qdrant = Qdrant.from_documents(
docs,
embeddings,
path="/tmp/local_qdrant",
collection_name="my_documents",
)

在本地服务器上部署

无论您选择使用Docker容器在本地启动Qdrant,还是选择使用官方Helm图表进行Kubernetes部署,连接到这样的实例的方式都是相同的。您需要提供一个指向该服务的URL。

url = "<---在此处填入qdrant的URL--->"
qdrant = Qdrant.from_documents(
docs,
embeddings,
url=url,
prefer_grpc=True,
collection_name="my_documents",
)

Qdrant云

如果您不想忙于管理基础设施,可以选择在Qdrant云上设置一个完全托管的Qdrant集群。试用期间包含一个永久免费的1GB集群。使用托管版本的Qdrant的主要区别在于,您需要提供一个API密钥来保护您的部署不被公开访问。

url = "<---在此处填入Qdrant云集群的URL--->"
api_key = "<---在此处填入API密钥--->"
qdrant = Qdrant.from_documents(
docs,
embeddings,
url=url,
prefer_grpc=True,
api_key=api_key,
collection_name="my_documents",
)

重新创建集合 (Recreating the collection)

使用Qdrant.from_textsQdrant.from_documents方法可以很好地开始使用Langchain的Qdrant。在之前的版本中,每次调用它们时都会重新创建集合。这种行为已经改变。当前,如果集合已经存在,将会重用该集合。将force_recreate设置为True可以删除旧的集合并从头开始。

url = "<---在此处填入qdrant的URL--->"
qdrant = Qdrant.from_documents(
docs,
embeddings,
url=url,
prefer_grpc=True,
collection_name="my_documents",
force_recreate=True,
)

使用 Qdrant 向量存储的最简单场景是执行相似性搜索。在底层,我们的查询将使用 embedding_function 进行编码,并用于在 Qdrant 集合中查找相似的文档。

query = "What did the president say about Ketanji Brown Jackson"
found_docs = qdrant.similarity_search(query)
print(found_docs[0].page_content)
    今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,顺便说一句,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。

作为总统,最重要的宪法责任之一就是提名人选担任美国最高法院法官。

我在4天前做到了这一点,当时我提名了巡回上诉法院法官Ketanji Brown Jackson。她是我们国家顶尖的法律专家之一,将继续延续布雷耶法官的卓越传统。

相似性搜索与分数

有时候我们可能希望进行搜索,同时获取一个相关性分数来了解特定结果的好坏程度。 返回的距离分数是余弦距离。因此,较低的分数表示结果较好。

query = "关于Ketanji Brown Jackson,总统说了什么"
found_docs = qdrant.similarity_search_with_score(query)
document, score = found_docs[0]
print(document.page_content)
print(f"\n分数: {score}")
    今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,顺便说一下,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。

总统最重要的宪法责任之一是提名人选担任美国最高法院法官。

我在4天前做到了这一点,当时我提名了巡回上诉法院法官Ketanji Brown Jackson。她是我们国家顶级的法律智慧之一,将继续延续布雷耶法官的卓越传统。

分数: 0.8153784913324512

元数据过滤

Qdrant拥有一个广泛的过滤系统,支持丰富的类型。在Langchain中,也可以通过向similarity_search_with_scoresimilarity_search方法传递额外的参数来使用过滤器。

from qdrant_client.http import models as rest

query = "总统对Ketanji Brown Jackson说了什么"
found_docs = qdrant.similarity_search_with_score(query, filter=rest.Filter(...))

Metadata filtering (元数据过滤)

最大边际相关性搜索 (MMR) (Maximum marginal relevance search (MMR))

如果您想查找一些相似的文档,但又希望获得多样化的结果,那么您应该考虑使用最大边际相关性搜索方法。最大边际相关性搜索在查询相似性和所选文档之间优化多样性。

query = "关于Ketanji Brown Jackson,总统说了什么"
found_docs = qdrant.max_marginal_relevance_search(query, k=2, fetch_k=10)
for i, doc in enumerate(found_docs):
print(f"{i + 1}.", doc.page_content, "\n")
    1. 今晚。我呼吁参议院:通过《自由投票法案》。通过《约翰·刘易斯选举权法案》。而且,顺便说一下,通过《披露法案》,这样美国人就可以知道谁在资助我们的选举。

今晚,我想向一个致力于为这个国家服务的人表示敬意:司法部长斯蒂芬·布雷耶——一位陆军退伍军人、宪法学者和即将退休的美国最高法院法官。布雷耶法官,感谢您的服务。

总统最重要的宪法责任之一是提名人选担任美国最高法院法官。

我在4天前就提名了一位法院上诉法官Ketanji Brown Jackson。她是我们国家顶尖的法律专家,将继续延续布雷耶法官的卓越传统。

2. 我们无法改变我们的分歧。但是我们可以改变我们共同面对COVID-19和其他问题的方式。

我最近在纽约市警察局访问了警察Wilbert Mora和他的搭档Jason Rivera的葬礼之后的几天。

他们在接到一个9-1-1电话时,一名男子用一把偷来的枪射杀了他们。

Mora警官27岁。

Rivera警官22岁。

他们都是多米尼加裔美国人,他们在后来选择担任警察的同一条街道上长大。

我与他们的家人交谈,并告诉他们我们永远对他们的牺牲感激不尽,我们将继续履行他们恢复每个社区应得的信任和安全的使命。

我已经从事这些问题很长时间了。

我知道什么是有效的:投资于犯罪预防和社区警察,他们会巡逻,了解社区,并恢复信任和安全。

Qdrant作为检索器 (Qdrant as a Retriever)

Qdrant,像其他向量存储一样,是一个使用余弦相似度的LangChain检索器。

retriever = qdrant.as_retriever()
retriever
    VectorStoreRetriever(vectorstore=<langchain.vectorstores.qdrant.Qdrant object at 0x7fc4e5720a00>, search_type='similarity', search_kwargs={})

还可以指定使用MMR作为搜索策略,而不是相似度。

retriever = qdrant.as_retriever(search_type="mmr")
retriever
    VectorStoreRetriever(vectorstore=<langchain.vectorstores.qdrant.Qdrant object at 0x7fc4e5720a00>, search_type='mmr', search_kwargs={})
query = "What did the president say about Ketanji Brown Jackson"
retriever.get_relevant_documents(query)[0]
    Document(page_content='Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. \n\nTonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. \n\nOne of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n\nAnd I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.', metadata={'source': '../../../state_of_the_union.txt'})

自定义 Qdrant (Customizing Qdrant)

在您的 Langchain 应用程序中使用现有的 Qdrant 集合有一些选项。在这种情况下,您可能需要定义如何将 Qdrant 点映射到 Langchain 的 Document

命名向量 (Named vectors)

Qdrant通过命名向量支持每个点的多个向量。Langchain只需要每个文档一个嵌入向量,默认情况下使用单个向量。但是,如果您使用外部创建的集合或者想要使用命名向量,可以通过提供其名称进行配置。

Qdrant.from_documents(
docs,
embeddings,
location=":memory:",
collection_name="my_documents_2",
vector_name="custom_vector",
)

作为Langchain用户,无论您是否使用命名向量,都不会看到任何区别。Qdrant集成将在幕后处理转换。

元数据 (Metadata)

Qdrant将您的向量嵌入与可选的类似JSON的有效负载一起存储。有效负载是可选的,但由于LangChain假设嵌入是从文档生成的,我们保留上下文数据,因此您也可以提取原始文本。

默认情况下,您的文档将以以下有效负载结构存储:

{
"page_content": "Lorem ipsum dolor sit amet",
"metadata": {
"foo": "bar"
}
}

但是,您可以决定使用不同的键来存储页面内容和元数据。如果您已经有一个想要重用的集合,这将非常有用。

Qdrant.from_documents(
docs,
embeddings,
location=":memory:",
collection_name="my_documents_2",
content_payload_key="my_page_content_key",
metadata_payload_key="my_meta",
)
    <langchain.vectorstores.qdrant.Qdrant at 0x7fc4e2baa230>