Skip to main content
Milvus 是一个数据库,用于存储、索引和管理由深度神经网络及其他机器学习(ML)模型生成的海量嵌入向量。
本笔记本展示了如何使用与 Milvus 向量数据库相关的功能。

设置

您需要安装 langchain-milvus 才能使用此集成,命令为 pip install -qU langchain-milvus
pip install -qU langchain-milvus

凭据

使用 Milvus 向量存储不需要任何凭据。

初始化

# | output: false
# | echo: false
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model="text-embedding-3-large")

Milvus Lite

原型设计最简单的方法是使用 Milvus Lite,所有数据都存储在本地向量数据库文件中。仅支持使用 Flat 索引。
from langchain_milvus import Milvus

URI = "./milvus_example.db"

vector_store = Milvus(
    embedding_function=embeddings,
    connection_args={"uri": URI},
    index_params={"index_type": "FLAT", "metric_type": "L2"},
)

Milvus 服务器

如果您有大量数据(例如超过一百万个向量),我们建议通过 DockerKubernetes 部署性能更佳的 Milvus 服务器。 Milvus 服务器支持多种 索引。利用这些不同的索引可以根据您的特定需求显著提升检索能力并加快检索过程。 作为示例,考虑 Milvus Standalone 的情况。要启动 Docker 容器,您可以运行以下命令:
!curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh

!bash standalone_embed.sh start
Password:
在此我们创建一个 Milvus 数据库:
from pymilvus import Collection, MilvusException, connections, db, utility

conn = connections.connect(host="127.0.0.1", port=19530)

# Check if the database exists
db_name = "milvus_demo"
try:
    existing_databases = db.list_database()
    if db_name in existing_databases:
        print(f"Database '{db_name}' already exists.")

        # Use the database context
        db.using_database(db_name)

        # Drop all collections in the database
        collections = utility.list_collections()
        for collection_name in collections:
            collection = Collection(name=collection_name)
            collection.drop()
            print(f"Collection '{collection_name}' has been dropped.")

        db.drop_database(db_name)
        print(f"Database '{db_name}' has been deleted.")
    else:
        print(f"Database '{db_name}' does not exist.")
        database = db.create_database(db_name)
        print(f"Database '{db_name}' created successfully.")
except MilvusException as e:
    print(f"An error occurred: {e}")
Database 'milvus_demo' does not exist.
Database 'milvus_demo' created successfully.
注意下方 URI 的变化。实例初始化后,请导航至 127.0.0.1:9091/webui 查看本地 Web UI。 以下是使用 Milvus 数据库服务创建向量存储实例的示例:
from langchain_milvus import BM25BuiltInFunction, Milvus

URI = "http://localhost:19530"

vectorstore = Milvus(
    embedding_function=embeddings,
    connection_args={"uri": URI, "token": "root:Milvus", "db_name": "milvus_demo"},
    index_params={"index_type": "FLAT", "metric_type": "L2"},
    consistency_level="Strong",
    drop_old=False,  # set to True if seeking to drop the collection with that name if it exists
)
如果您想使用 Zilliz Cloud(Milvus 的全托管云服务),请调整 uri 和 token,它们分别对应 Zilliz Cloud 中的 公共端点API 密钥

使用 Milvus 集合隔离数据

您可以在同一个 Milvus 实例的不同集合中存储不相关的文档。 以下是创建新集合的方法:
from langchain_core.documents import Document

vector_store_saved = Milvus.from_documents(
    [Document(page_content="foo!")],
    embeddings,
    collection_name="langchain_example",
    connection_args={"uri": URI},
)
以下是检索该存储集合的方法:
vector_store_loaded = Milvus(
    embeddings,
    connection_args={"uri": URI},
    collection_name="langchain_example",
)

管理向量存储

创建向量存储后,我们可以通过添加和删除不同项来与其交互。

向向量存储添加项

我们可以使用 add_documents 函数将项添加到我们的向量存储中。
from uuid import uuid4

from langchain_core.documents import Document

document_1 = Document(
    page_content="I had chocolate chip pancakes and scrambled eggs for breakfast this morning.",
    metadata={"source": "tweet"},
)

document_2 = Document(
    page_content="The weather forecast for tomorrow is cloudy and overcast, with a high of 62 degrees.",
    metadata={"source": "news"},
)

document_3 = Document(
    page_content="Building an exciting new project with LangChain - come check it out!",
    metadata={"source": "tweet"},
)

document_4 = Document(
    page_content="Robbers broke into the city bank and stole $1 million in cash.",
    metadata={"source": "news"},
)

document_5 = Document(
    page_content="Wow! That was an amazing movie. I can't wait to see it again.",
    metadata={"source": "tweet"},
)

document_6 = Document(
    page_content="Is the new iPhone worth the price? Read this review to find out.",
    metadata={"source": "website"},
)

document_7 = Document(
    page_content="The top 10 soccer players in the world right now.",
    metadata={"source": "website"},
)

document_8 = Document(
    page_content="LangGraph is the best framework for building stateful, agentic applications!",
    metadata={"source": "tweet"},
)

document_9 = Document(
    page_content="The stock market is down 500 points today due to fears of a recession.",
    metadata={"source": "news"},
)

document_10 = Document(
    page_content="I have a bad feeling I am going to get deleted :(",
    metadata={"source": "tweet"},
)

documents = [
    document_1,
    document_2,
    document_3,
    document_4,
    document_5,
    document_6,
    document_7,
    document_8,
    document_9,
    document_10,
]
uuids = [str(uuid4()) for _ in range(len(documents))]

vector_store.add_documents(documents=documents, ids=uuids)

从向量存储删除项

vector_store.delete(ids=[uuids[-1]])
(insert count: 0, delete count: 1, upsert count: 0, timestamp: 0, success count: 0, err count: 0, cost: 0)

查询向量存储

一旦创建了向量存储并添加了相关文档,您很可能希望在运行链或代理时对其进行查询。

直接查询

相似度搜索

执行带有元数据过滤的简单相似度搜索可以如下所示:
results = vector_store.similarity_search(
    "LangChain provides abstractions to make working with LLMs easy",
    k=2,
    expr='source == "tweet"',
)
for res in results:
    print(f"* {res.page_content} [{res.metadata}]")
* Building an exciting new project with LangChain - come check it out! [{'pk': '9905001c-a4a3-455e-ab94-72d0ed11b476', 'source': 'tweet'}]
* LangGraph is the best framework for building stateful, agentic applications! [{'pk': '1206d237-ee3a-484f-baf2-b5ac38eeb314', 'source': 'tweet'}]

带分数的相似度搜索

您也可以带分数进行搜索:
results = vector_store.similarity_search_with_score(
    "Will it be hot tomorrow?", k=1, expr='source == "news"'
)
for res, score in results:
    print(f"* [SIM={score:3f}] {res.page_content} [{res.metadata}]")
* [SIM=21192.628906] bar [{'pk': '2', 'source': 'https://example.com'}]
若要查看使用 Milvus 向量存储时可用的所有搜索选项的完整列表,请访问 API 参考

转换为检索器进行查询

您还可以将向量存储转换为检索器,以便在链中更轻松地使用。
retriever = vector_store.as_retriever(search_type="mmr", search_kwargs={"k": 1})
retriever.invoke("Stealing from the bank is a crime", filter={"source": "news"})
[Document(metadata={'pk': 'eacc7256-d7fa-4036-b1f7-83d7a4bee0c5', 'source': 'news'}, page_content='Robbers broke into the city bank and stole $1 million in cash.')]

混合搜索

最常见的混合搜索场景是稠密 + 稀疏混合搜索,其中候选项同时使用语义向量相似性和精确关键词匹配进行检索。这些方法的结果被合并、重排序,并传递给大语言模型以生成最终答案。这种方法平衡了精度和语义理解,使其在各种查询场景中都非常有效。

全文搜索

Milvus 2.5 起,通过稀疏-BM25 方法原生支持全文搜索,即将 BM25 算法表示为稀疏向量。Milvus 接受原始文本作为输入,并自动将其转换为存储在指定字段中的稀疏向量,从而无需手动生成稀疏嵌入。 对于全文搜索,Milvus VectorStore 接受 builtin_function 参数。通过此参数,您可以传入 BM25BuiltInFunction 的实例。这与通常将稠密嵌入传递给 VectorStore 的语义搜索不同, 以下是 Milvus 中混合搜索的一个简单示例,使用 OpenAI 稠密嵌入进行语义搜索,使用 BM25 进行全文搜索:
from langchain_milvus import BM25BuiltInFunction, Milvus
from langchain_openai import OpenAIEmbeddings

vectorstore = Milvus.from_documents(
    documents=documents,
    embedding=OpenAIEmbeddings(),
    builtin_function=BM25BuiltInFunction(),
    # `dense` is for OpenAI embeddings, `sparse` is the output field of BM25 function
    vector_field=["dense", "sparse"],
    connection_args={
        "uri": URI,
    },
    consistency_level="Strong",
    drop_old=True,
)
  • 当您使用 BM25BuiltInFunction 时,请注意全文搜索在 Milvus Standalone 和 Milvus Distributed 中可用,但在 Milvus Lite 中不可用,尽管它已在未来纳入计划的路线图之中。它也将在 Zilliz Cloud(全托管 Milvus)中很快可用。如需更多信息,请联系 support@zilliz.com
在上述代码中,我们定义了一个 BM25BuiltInFunction 实例并将其传递给 Milvus 对象。BM25BuiltInFunction 是 Milvus 中 Function 的轻量级包装类。我们可以将其与 OpenAIEmbeddings 一起使用来初始化合并稠密 + 稀疏混合搜索的 Milvus 向量存储实例。 BM25BuiltInFunction 不需要客户端传递语料库或训练数据,所有内容都在 Milvus 服务器端自动处理,因此用户无需关心任何词汇表和语料库。此外,用户还可以自定义 分析器 以实现 BM25 中的自定义文本处理。

对候选项重排序

在第一阶段检索之后,我们需要对候选项进行重排序以获得更好的结果。您可以参考 重排序 获取更多信息。 以下是加权重排序的示例:
query = "What are the novels Lila has written and what are their contents?"

vectorstore.similarity_search(
    query, k=1, ranker_type="weighted", ranker_params={"weights": [0.6, 0.4]}
)

检索增强生成 (RAG) 用法

关于如何使用此向量存储进行检索增强生成 (RAG) 的指南,请参阅以下部分:

按用户检索

构建检索应用时,您通常需要考虑多个用户。这意味着您可能不仅为一个用户存储数据,而是为许多不同用户存储数据,且他们不应能够看到彼此的数据。 Milvus 建议使用 partition_key 来实现多租户。这里有一个示例:
Partition key 功能在 Milvus Lite 中不可用,如果您想使用它,需要如上所述启动 Milvus 服务器。
from langchain_core.documents import Document

docs = [
    Document(page_content="i worked at kensho", metadata={"namespace": "harrison"}),
    Document(page_content="i worked at facebook", metadata={"namespace": "ankush"}),
]
vectorstore = Milvus.from_documents(
    docs,
    embeddings,
    connection_args={"uri": URI},
    drop_old=True,
    partition_key_field="namespace",  # Use the "namespace" field as the partition key
)
要使用分区键进行搜索,您应在搜索请求的布尔表达式中包含以下任一内容: search_kwargs={"expr": '<partition_key> == "xxxx"'} search_kwargs={"expr": '<partition_key> == in ["xxx", "xxx"]'} 请将 <partition_key> 替换为指定为分区键的字段名称。 Milvus 根据指定的分区键切换到分区,根据分区键筛选实体,并在筛选后的实体中进行搜索。
# This will only get documents for Ankush
vectorstore.as_retriever(search_kwargs={"expr": 'namespace == "ankush"'}).invoke(
    "where did i work?"
)
[Document(page_content='i worked at facebook', metadata={'namespace': 'ankush'})]
# This will only get documents for Harrison
vectorstore.as_retriever(search_kwargs={"expr": 'namespace == "harrison"'}).invoke(
    "where did i work?"
)
[Document(page_content='i worked at kensho', metadata={'namespace': 'harrison'})]

API 参考

有关所有 Milvus VectorStore 功能和配置的详细文档,请前往 [API 参考]nce.langchain.com/python/langchain-milvus/vectorstores/milvus/Milvus)