Milvus:高性能的向量数据库

Milvus简介

Milvus背景

在人工智能与大模型迅速发展的当下,向量数据库作为支撑语义搜索、图像识别、多模态理解等关键应用的底层基础设施,正扮演着越来越重要的角色。Milvus,作为当前最主流的开源向量数据库之一,专为处理大规模、高维向量的相似性检索而设计,具备高性能、高可扩展性和丰富的索引支持。无论是构建基于文本 embedding 的知识问答系统,还是处理亿级图像的相似性匹配任务,Milvus 都能以其强大的索引能力和灵活的接口,成为 AI 应用中的“记忆引擎”。它的出现,不仅填补了传统数据库在向量检索领域的空白,也为构建下一代智能应用提供了坚实的技术基石。

Milvus特点

Milvus支持的场景包括检索增强生成(RAG)、图像搜索、多模态搜索、混合搜索、GraphRAG等方向。非结构化数据(如文本、图像和音频)格式各异,蕴含丰富的潜在语义,因此分析起来极具挑战性。为了处理这种复杂性,Embeddings被用来将非结构化数据转换成能够捕捉其基本特征的数字向量。然后将这些向量存储在向量数据库中,从而实现快速、可扩展的搜索和分析。在这个背景下,Milvus提供强大的数据建模功能,使用户能够将非结构化或多模式数据组织成结构化的Collections。它支持多种数据类型,适用于不同的属性模型,包括常见的数字和字符类型、各种向量类型、数组、集合和 JSON,为您节省了维护多个数据库系统的精力。

Milvus 提供三种部署模式,涵盖各种数据规模–从 Jupyter Notebooks 中的本地原型到管理数百亿向量的大规模 Kubernetes 集群:

  1. Milvus Lite是一个 Python 库,可以轻松集成到您的应用程序中。作为 Milvus 的轻量级版本,它非常适合在 Jupyter Notebooks 中进行快速原型开发,或在资源有限的边缘设备上运行。对于新人学习而言,可以直接使用Milvus的Lite版本,轻松集成并使用。
  2. Milvus Standalone是单机服务器部署,所有组件都捆绑在一个 Docker 镜像中,方便部署。
  3. Milvus Distributed可部署在Kubernetes集群上,采用云原生架构,专为十亿规模甚至更大的场景而设计。该架构可确保关键组件的冗余。
Milvus的特点详细解释
向量搜索支持亿级向量的ANN(近似最近邻)TopK检索
混合搜索基于多个向量场进行 ANN 搜索。
范围搜索查找查询向量指定半径范围内的向量。
全文搜索基于 BM25 的全文搜索。
重排序(Rerankers)根据附加标准或辅助算法调整搜索结果顺序,完善初始 ANN 搜索结果。
获取根据主键检索数据。
查询使用特定的表达式检索数据。
多模型支持支持不同维度的向量(text/image embedding)
存储引擎支持多种存储后端(如本地磁盘、S3、MinIO)
横向扩展分布式架构,支持大规模扩容
高效搜索算法Milvus支持多种内存和磁盘索引/搜索算法,包括 IVF、HNSW、DiskANN 等
插件生态与Faiss、HNSW、ANN等索引算法对接
访问接口支持RESTful API、SDK(Python/Go/Java/Nodejs)/C#(微软提供)/gRPC API
向量管理支持批量导入、删除、版本管理等操作

Milvus的云原生和高度解耦的系统架构确保了系统可以随着数据的增长而不断扩展。可以借助 Kubernetes 或公共云轻松扩展。此外,Milvus 的各个组件都有很好的解耦,其中最关键的三项任务–搜索、数据插入和索引/压实–被设计为易于并行化的流程,复杂的逻辑被分离出来。这确保了相应的查询节点、数据节点和索引节点可以独立地向上和向下扩展,从而优化了性能和成本效率。

Milvus 高度解耦的系统架构

Milvus与AI的集成路径

Milvus与AI的结合方式主要集中在Embedding(嵌入)、Rerank(重排序)、RAG(检索增强生成)这三个方向上。

  1. Embeddings 模型集成:Embedding 模型将非结构化数据转换为其在高维数据空间中的数字表示,以便您能将其存储在 Milvus 中。目前,PyMilvus(Python SDK)集成了多个嵌入模型,以便您能快速将数据准备成向量嵌入。
  2. Reranker 模型集成:在信息检索和生成式人工智能领域,Reranker 是优化初始搜索结果顺序的重要工具。PyMilvus 也集成了几种 Rerankers 模型,以优化初始搜索返回结果的顺序。
  3. LangChain和其他人工智能工具集成:在 GenAI 时代,LangChain 等工具受到了应用程序开发人员的广泛关注。作为核心组件,Milvus 通常在此类工具中充当向量存储。

Milvus使用

Milvus的安装

开始之前,请确保本地环境中有Python 3.8+可用。安装pymilvus,其中包含 python 客户端库和 Milvus Lite。同时还可以把Milvus的model模型都安装上。

pip install -U pymilvus
pip install "pymilvus[model]"

安装好了之后可以通过pip list看到下面两个package安装成功。

如果有torch安装错误的提示,可以使用命令重新安装

conda install pytorch::pytorch torchvision torchaudio -c pytorch

并且可以使用代码来判断pytorch是否安装正常:

import torch
print(torch.__version__)
# 2.5.1

Milvus的数据库

在 Milvus 中,数据库是组织和管理数据的逻辑单元。为了提高数据安全性并实现多租户,你可以创建多个数据库,为不同的应用程序或租户从逻辑上隔离数据。例如,创建一个数据库用于存储用户 A 的数据,另一个数据库用于存储用户 B 的数据。它支持Python、Go、Java、NodeJS等语言去操作数据库。

可以使用 Milvus RESTful API 或 SDK 列出所有现有数据库并查看其详细信息。同时,还可以管理数据库的属性,包括更改、删除等操作。

Milvus 数据库中,collection 和 schema 是用于组织和管理数据的两个核心概念,主要用于处理和存储高维向量数据。

Collection

在 Milvus 中,collection 是一个数据的集合,可以理解为一个数据库表格的概念,用来存储和管理向量数据及相关的元数据。它的作用是:

  1. 一个 collection 由多个向量数据(如图像、文本等的向量表示)构成;
  2. 每个 collection 可以包含多个字段(例如,ID、向量、标签等)。
  3. 你可以创建、插入、查询、删除和更新集合中的数据。

Collection 是一个二维表,具有固定的列和变化的行。每列代表一个字段,每行代表一个实体。下图显示了一个有 8 列和 6 个实体的 Collection。

通常在创建集合时,需要定义向量的维度(比如 128 维、256 维等)以及其他属性(如数据类型)。例如:

from pymilvus import Collection

# 创建一个名为 "example_collection" 的集合
collection = Collection("example_collection")

Schema

schema 是定义一个 collection 中各个字段的结构和数据类型的描述。它类似于传统数据库中的表结构(如字段名和数据类型)。它的作用是:

  1. schema 确定了 collection 中各个字段的数据类型(例如整数、浮动小数、向量等),以及这些字段是否为可查询的或索引字段。
  2. 对于向量数据,schema 还定义了向量字段的维度和索引方式。向量字段(如 embedding 或 vector)是存储向量数据的核心字段。其他常规字段(如整型、浮动小数、字符串等),用于存储与向量相关的附加信息(例如文本、标签等)。

例子:

from pymilvus import MilvusClient, DataType, Collection

# Step 1️⃣ 创建 Milvus 客户端并初始化 Collection
# MilvusClient 是与 Milvus 服务进行交互的客户端,连接到指定的数据库。
client = MilvusClient("milvus_rag_zh_demo_2.db")  # 假设使用一个本地数据库文件作为存储

# COLLECTION_NAME 是我们想要操作的集合名
COLLECTION_NAME = "zh_rag_demo_2"

# Step 2️⃣ 删除已有的 Collection(如果存在)
# 如果该集合已经存在,我们删除旧的集合以重新创建
if client.has_collection(COLLECTION_NAME):
    print(f"Collection '{COLLECTION_NAME}' already exists. Dropping it.")
    client.drop_collection(COLLECTION_NAME)  # 删除集合

# Step 3️⃣ 定义 Collection 的 Schema
# Collection Schema 用于定义集合中的字段,包括数据类型、维度等。
schema = MilvusClient.create_schema(
    auto_id=False,  # 不自动生成 ID(我们手动指定 ID)
    enable_dynamic_field=True,  # 启用动态字段支持
)

# Step 4️⃣ 添加字段到 Schema
# 每个字段需要指定字段名称、数据类型,以及其他一些属性(如是否是主键)
schema.add_field(field_name="my_id", datatype=DataType.INT64, is_primary=True)  # 主键字段
schema.add_field(field_name="my_vector", datatype=DataType.FLOAT_VECTOR, dim=5)  # 向量字段,维度为5
schema.add_field(field_name="my_varchar", datatype=DataType.VARCHAR, max_length=512)  # 字符串字段,最大长度为512

Milvus Lite 目前仅支持 Ubuntu 和 MacOS 操作系统。如果在 Windows 系统上运行上述代码,将会遇到此错误。另外,使用Milvus的时候,要确保Milvus已经在本地或者远程服务器运行,否则会报错。Milvus的启动和运行建议使用Docker的方式执行。

一个 Collections Schema 有一个主键、最多四个向量字段和几个标量字段。下图说明了如何将文章映射到模式字段列表。

搜索系统的数据模型设计包括分析业务需求,并将信息抽象为模式表达的数据模型。例如,搜索一段文本必须 “索引”,通过 “嵌入 “将字面字符串转换为向量,并启用向量搜索。除了这一基本要求外,可能还需要存储出版时间戳和作者等其他属性。有了这些元数据,就可以通过过滤来完善语义搜索,只返回特定日期之后或特定作者发表的文本。您还可以检索这些标量与主文本,以便在应用程序中呈现搜索结果。每个标量都应分配一个唯一标识符,以整数或字符串的形式组织这些文本片段。这些元素对于实现复杂的搜索逻辑至关重要。

Milvus与Embedding

在当今的信息检索和智能问答系统中,Embedding(向量表示)技术成为了连接自然语言与高效计算的桥梁。通过将文本、图像等非结构化数据转化为高维向量,我们可以实现更精确的语义匹配和内容理解。而Milvus作为一款开源的向量数据库,专为此类场景而生。它支持大规模向量的高效存储与相似度检索,为构建智能搜索引擎、推荐系统及 AI 应用提供了强大的基础设施。下面将结合实际案例,介绍如何利用 Embedding 技术与 Milvus 搭建一套完整的语义检索流程。

于是,在pymilvus中可以引入embedding的模型工具,用于未来的向量输出和检索:

# 从 pymilvus 中引入用于 embedding 的模型工具
from pymilvus import model

# 如果访问 huggingface 官方地址失败(如网络受限),可以取消下面的注释
# 设置环境变量,切换为 huggingface 镜像地址(例如清华、阿里等镜像)
# import os
# os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'

# 初始化一个默认的 embedding 函数,会自动下载一个小型的句子向量模型:
# paraphrase-albert-small-v2(约 50MB),适合快速测试或演示用途。
embedding_fn = model.DefaultEmbeddingFunction()

# 定义待向量化的文本列表,每条文本都是一个独立的文档或知识片段。
docs = [
    "Artificial intelligence was founded as an academic discipline in 1956.",
    "Alan Turing was the first person to conduct substantial research in AI.",
    "Born in Maida Vale, London, Turing was raised in southern England.",
]

# 使用 embedding 函数将文本转化为向量(embedding)
# 得到的结果是一个 list,每条向量为一个 numpy 数组,维度为 768
vectors = embedding_fn.encode_documents(docs)

# 输出向量
print("vectors:", vectors)

# 输出向量维度信息(用于对齐 Milvus 中 Collection 的字段设计)
# embedding_fn.dim 表示当前模型生成的向量维度
# vectors[0].shape 表示第一条向量的维度
print("Dim:", embedding_fn.dim, vectors[0].shape)  # 预期输出 Dim: 768 (768,)

# 组织向量数据为结构化的格式,准备后续插入 Milvus:
# 每条记录包含:
# - id:唯一编号
# - vector:生成的 embedding 向量
# - text:原始文本内容(可用于展示/检索返回)
# - subject:人为指定的分类标签(这里用作 metadata 过滤的演示)
data = [
    {"id": i, "vector": vectors[i], "text": docs[i], "subject": "history"}
    for i in range(len(vectors))
]

# 打印生成数据的基本信息:
# - 实体数量
# - 每条记录包含哪些字段
# - 向量维度是多少
print("Data has", len(data), "entities, each with fields: ", data[0].keys())
print("Vector dim:", len(data[0]["vector"]))

除此之外,在模型选择方面,还可以选择 Sentence Transformers、BGE M3、SPLADE、instructor、nomic、mGTE、Model2Vec,另外OpenAI、gemini、Voyage、Jina AI、Cohere、Ministral AI的模型需要相应的API-KEY才能使用。

嵌入函数类型API 或开源
openai密集API
sentence transformer密集开源
SPLADE稀疏开源
bge-m3混合开源
远航密集型应用程序接口
jina密集API
cohere密集API
指导员密集开源
Mistral AI密集应用程序接口
Nomic密集API
mGTE混合型开源
Model2Vec混合型开源
双子座混合型私有

可以参考代码进行修改,在model这个部分进行修改即可:

# 从 pymilvus 中引入用于 embedding 的模型工具
from pymilvus import model

# 如果访问 huggingface 官方地址失败(如网络受限),可以取消下面的注释
# 设置环境变量,切换为 huggingface 镜像地址(例如清华、阿里等镜像)
# import os
# os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'

# 初始化一个默认的 embedding 函数,会自动下载一个小型的句子向量模型:
# paraphrase-albert-small-v2(约 50MB),适合快速测试或演示用途。
# embedding_fn = model.DefaultEmbeddingFunction()

# sentence transformers 模型
embedding_fn = model.dense.SentenceTransformerEmbeddingFunction(
    model_name='all-MiniLM-L6-v2',  # Specify the model name
    device='cpu'  # Specify the device to use, e.g., 'cpu' or 'cuda:0'
)

# # BGE M3 模型
# embedding_fn = model.hybrid.BGEM3EmbeddingFunction(
#     model_name='BAAI/bge-m3',  # Specify the model name
#     device='cpu',  # Specify the device to use, e.g., 'cpu' or 'cuda:0'
#     use_fp16=False  # Specify whether to use fp16. Set to `False` if `device` is `cpu`.
# )

# # splade 模型
# embedding_fn = model.sparse.SpladeEmbeddingFunction(
#     model_name="naver/splade-cocondenser-selfdistil",
#     device="cpu"
# )

# 定义待向量化的文本列表,每条文本都是一个独立的文档或知识片段。
docs = [
    "Artificial intelligence was founded as an academic discipline in 1956.",
    "Alan Turing was the first person to conduct substantial research in AI.",
    "Born in Maida Vale, London, Turing was raised in southern England.",
]

# 使用 embedding 函数将文本转化为向量(embedding)
# 得到的结果是一个 list,每条向量为一个 numpy 数组,维度为 768
vectors = embedding_fn.encode_documents(docs)

# 输出向量
print("vectors:", vectors)

# 输出向量维度信息(用于对齐 Milvus 中 Collection 的字段设计)
# embedding_fn.dim 表示当前模型生成的向量维度
# vectors[0].shape 表示第一条向量的维度
print("Dim:", embedding_fn.dim, vectors[0].shape)  # 预期输出 Dim: 768 (768,)

# 组织向量数据为结构化的格式,准备后续插入 Milvus:
# 每条记录包含:
# - id:唯一编号
# - vector:生成的 embedding 向量
# - text:原始文本内容(可用于展示/检索返回)
# - subject:人为指定的分类标签(这里用作 metadata 过滤的演示)
data = [
    {"id": i, "vector": vectors[i], "text": docs[i], "subject": "history"}
    for i in range(len(vectors))
]

# 打印生成数据的基本信息:
# - 实体数量
# - 每条记录包含哪些字段
# - 向量维度是多少
print("Data has", len(data), "entities, each with fields: ", data[0].keys())
print("Vector dim:", len(data[0]["vector"]))

下面代码的整体流程是:连接 Milvus 客户端并创建集合,初始化一个嵌入模型并将文本转化为向量,组织数据结构并插入到 Milvus,使用一个问题进行语义查询,获取最相似的文本内容。

# 引入必要的库:pymilvus 中的模型工具和客户端类
from pymilvus import model
from pymilvus import MilvusClient

# 初始化一个 Milvus 客户端,连接到本地或指定路径的数据库文件
client = MilvusClient("milvus_demo.db")

# 如果已存在名为 "demo_collection" 的集合,先删除(避免重复创建时报错)
if client.has_collection(collection_name="demo_collection"):
    client.drop_collection(collection_name="demo_collection")

# 创建一个新的集合(Collection),用于存储向量数据
# 参数说明:
# - collection_name:集合名称
# - dimension:向量的维度(本例使用的是 768 维)
client.create_collection(
    collection_name="demo_collection",
    dimension=768,
)

# ⚠️ 如果无法连接 huggingface 官方模型仓库(比如被墙),可以使用国内镜像站点:
# 设置 HF_ENDPOINT 环境变量来更改默认的 huggingface 地址,例如使用清华镜像:
# import os
# os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'

# 初始化一个默认的 embedding 函数,会自动下载 paraphrase-albert-small-v2 模型
# 模型体积小,加载快,适合用于快速测试/演示
embedding_fn = model.DefaultEmbeddingFunction()

# (可选)你也可以使用更强的模型,例如 SentenceTransformer、BGE M3、Splade 等
# 下面是一些替代方案(按需取消注释):

# 使用 sentence-transformers 模型
# embedding_fn = model.dense.SentenceTransformerEmbeddingFunction(
#     model_name='all-MiniLM-L6-v2',
#     device='cpu'  # 或使用 'cuda:0' 来使用 GPU
# )

# 使用 BGE M3 多任务嵌入模型
# embedding_fn = model.hybrid.BGEM3EmbeddingFunction(
#     model_name='BAAI/bge-m3',
#     device='cpu',
#     use_fp16=False
# )

# 使用稀疏向量模型 Splade(适用于稀疏向量检索场景)
# embedding_fn = model.sparse.SpladeEmbeddingFunction(
#     model_name="naver/splade-cocondenser-selfdistil",
#     device="cpu"
# )

# 准备文本数据:这些将作为示例文档被嵌入为向量
docs = [
    "Artificial intelligence was founded as an academic discipline in 1956.",
    "Alan Turing was the first person to conduct substantial research in AI.",
    "Born in Maida Vale, London, Turing was raised in southern England.",
]

# 使用嵌入函数将文本转换为向量
# 返回值为一个列表,每个元素是一个 numpy 数组(维度为 768)
vectors = embedding_fn.encode_documents(docs)

# 打印生成的向量(可以注释掉以避免打印过多内容)
print("vectors:", vectors)

# 打印嵌入函数维度和实际向量维度(用于确认维度一致性)
print("Dim:", embedding_fn.dim, vectors[0].shape)

# 将向量数据组织为结构化格式,便于后续插入到 Milvus 中
# 每个字典代表一条记录,包括:
# - id:唯一编号
# - vector:对应的嵌入向量
# - text:原始文本
# - subject:可选分类标签(可用于后续过滤查询)
data = [
    {"id": i, "vector": vectors[i], "text": docs[i], "subject": "history"}
    for i in range(len(vectors))
]

# 打印数据结构信息:
print("Data has", len(data), "entities, each with fields: ", data[0].keys())
print("Vector dim:", len(data[0]["vector"]))

# 将数据插入到 Milvus 中的 demo_collection 集合
res = client.insert(collection_name="demo_collection", data=data)

# 打印插入结果(通常包含成功插入的数量等信息)
print("res:", res)

# 接下来执行向量搜索(即:语义检索)
# 使用同样的嵌入函数对查询语句进行编码,得到向量
query_vectors = embedding_fn.encode_queries(["Who is Alan Turing?"])

# ⚠️ 如果没有 embedding 函数,也可以使用随机向量模拟查询(仅限演示用)
# import random
# query_vectors = [ [ random.uniform(-1, 1) for _ in range(768) ] ]

# 在 demo_collection 中执行向量检索
res_search = client.search(
    collection_name="demo_collection",  # 检索目标集合
    data=query_vectors,  # 查询向量
    limit=2,  # 返回最相似的 2 条记录
    output_fields=["text", "subject"],  # 指定返回的字段(原始文本和标签)
)

# 打印检索结果(包括匹配文本和相似度等信息)
print("res_search:", res_search)

# 提取并打印搜索结果中的具体条目
print(list(res_search[0])[0])  # 第一个查询返回的第一条结果
print(list(res_search)[0][1])  # 第一个查询返回的第二条结果

同时,文件夹下会出现一个milvus_demo.db,用于保存向量数据。

如果画一个流程图的话,是按下面的流程来运行的。

由于Milvus Lite的所有数据都存储在本地文件中,因此即使在程序终止后,你也可以通过创建一个带有现有文件的MilvusClient ,将所有数据加载到内存中。例如,这将恢复 “milvus_demo.db “文件中的 Collections,并继续向其中写入数据。

from pymilvus import MilvusClient
client = MilvusClient("milvus_demo.db")

如果想删除某个 Collections 中的所有数据,可以通过以下方法丢弃该Collections:

# Drop collection
client.drop_collection(collection_name="demo_collection")

Milvus与Rerankers

在现代语义检索系统中,检索流程通常被划分为两个阶段:初始召回(First-Stage Retrieval)与重排序(Re-ranking)。Milvus 作为一个开源、高性能的向量数据库,专为处理大规模高维向量检索任务而设计,广泛应用于初始召回阶段。其核心能力在于高效构建和查询向量索引(如 IVF、HNSW、DiskANN 等),支持近似最近邻(ANN)算法,以实现对查询向量在大规模语料中的快速相似度搜索。通过将文本、图像等非结构化数据嵌入为向量,Milvus 能够在亚秒级时间内返回与输入语义最接近的候选项,为后续精排阶段提供基础支撑。

而 Rerankers(重排序模型)主要作用于检索系统的第二阶段。它通常采用更精细的模型架构(如基于 Transformer 的 Cross-Encoder),以逐对方式对候选文本与查询进行交互式建模,从而输出更精准的相关性评分。尽管计算开销相较于向量检索更高,Rerankers 在精度上的提升对于提高系统整体性能具有关键意义。因此,将 Milvus 用于高效召回,再通过 Rerankers 对候选结果进行精细排序,已成为构建高质量语义检索系统的主流范式。

常见的Rerankers功能包括以下五种,有开源模型和应用程序接口。

Rerankers 功能应用程序接口或开源开源地址
BGE开源https://ollama.com/zyw0605688/bge-reranker-v2-m3
交叉编码器开源https://huggingface.co/cross-encoder/ms-marco-MiniLM-L6-v2
Voyage应用程序接口 
Cohere应用程序接口 
Jina AIAPI 

如果使用BGE的重排序模型来构建的话,可以使用以下代码的案例:

# 从 pymilvus.model.reranker 模块中引入 BGE 重排序模型函数
from pymilvus.model.reranker import BGERerankFunction

# ----------------------------------------------------------
# 初始化 Reranker(重排序器):
# 使用 BAAI(智源研究院)提供的 BGE-Reranker-v2-M3 模型。
# 该模型基于 Cross-Encoder 架构,通过对 query 和文档对进行语义交互建模,
# 输出相关性打分,用于对初始检索结果进行排序提升精度。
# ----------------------------------------------------------
bge_rf = BGERerankFunction(
    model_name="BAAI/bge-reranker-v2-m3",  # 模型名称,默认即为该模型
    device="cpu"  # 计算设备,可改为 'cuda:0' 使用 GPU 加速
)

# ----------------------------------------------------------
# 定义一个查询(query),用于检索历史相关信息。
# ----------------------------------------------------------
query = "What event in 1956 marked the official birth of artificial intelligence as a discipline?"

# ----------------------------------------------------------
# 定义候选文档列表(documents):
# 模拟从 Milvus 检索返回的初步候选文本片段(Top-K),
# 接下来将使用 Reranker 进一步对它们进行精排。
# 文档集合里面有四个元素,从0到3编号。
# ----------------------------------------------------------
documents = [
    "In 1950, Alan Turing published his seminal paper, 'Computing Machinery and Intelligence,' proposing the Turing Test as a criterion of intelligence, a foundational concept in the philosophy and development of artificial intelligence.",
    "The Dartmouth Conference in 1956 is considered the birthplace of artificial intelligence as a field; here, John McCarthy and others coined the term 'artificial intelligence' and laid out its basic goals.",
    "In 1951, British mathematician and computer scientist Alan Turing also developed the first program designed to play chess, demonstrating an early example of AI in game strategy.",
    "The invention of the Logic Theorist by Allen Newell, Herbert A. Simon, and Cliff Shaw in 1955 marked the creation of the first true AI program, which was capable of solving logic problems, akin to proving mathematical theorems."
]

# ----------------------------------------------------------
# 执行重排序操作:
# 将 query 与每个文档组合进行相关性评分,输出按分值降序排列的 Top-K 文档。
# top_k 参数指定只保留得分最高的前 K 条(这里设为 3)。
# 返回结果为一个包含 RerankResult 对象的列表,每个对象包含:
# - index:原文档在输入列表中的索引位置
# - score:query 与该文档的语义相关性打分(越高越相关)
# - text:文档原文内容
# ----------------------------------------------------------
results = bge_rf(
    query=query,
    documents=documents,
    top_k=3,  # 返回得分最高的前 3 条
)

# ----------------------------------------------------------
# 遍历输出精排结果:
# 展示每条候选文档的原始索引、得分(保留 6 位小数)、文本内容。
# 注意结果已按 score 排序,score 越高代表与 query 越匹配。
# ----------------------------------------------------------
for result in results:
    print(f"Index: {result.index}")  # 文本在原始 documents 中的位置
    print(f"Score: {result.score:.6f}")  # 重排序得分
    print(f"Text: {result.text}\n")  # 文本内容


如果将query和document修改成中文,同样可以找到结果。查询是:

query = "1956年标志着人工智能正式诞生的事件是什么?"

候选文档是:

documents = [
    "1950年,图灵发表了著名论文《计算机器与智能》,提出了图灵测试,这是人工智能哲学的重要基石。",
    "1956年达特茅斯会议被广泛认为是人工智能作为一个学科诞生的标志,在这次会议上,“人工智能”这一术语首次被提出,并确立了研究目标。",
    "1951年,英国数学家图灵开发了一个可以下棋的程序,展示了早期的人工智能应用。",
    "1955年,艾伦·纽厄尔与赫伯特·西蒙等人发明了“逻辑理论家”程序,这是第一个能够自动证明定理的人工智能系统。"
]

其搜索的结果是:

如果需要使用交叉编码器,那么可以参考HuggingFace上面的链接(https://huggingface.co/cross-encoder/ms-marco-MiniLM-L6-v2),输入以下代码即可计算两个文档之间的相似度。

from sentence_transformers import CrossEncoder

model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L6-v2')
scores = model.predict([
    ("How many people live in Berlin?", "Berlin had a population of 3,520,031 registered inhabitants in an area of 891.82 square kilometers."),
    ("How many people live in Berlin?", "Berlin is well known for its museums."),
])
print(scores)

# [ 8.607141 -4.320079]

还可以用以下多种模型进行选择:

Model-NameNDCG@10 (TREC DL 19)MRR@10 (MS Marco Dev)Docs / Sec
Version 2 models   
cross-encoder/ms-marco-TinyBERT-L2-v269.8432.569000
cross-encoder/ms-marco-MiniLM-L2-v271.0134.854100
cross-encoder/ms-marco-MiniLM-L4-v273.0437.72500
cross-encoder/ms-marco-MiniLM-L6-v274.339.011800
cross-encoder/ms-marco-MiniLM-L12-v274.3139.02960
Version 1 models   
cross-encoder/ms-marco-TinyBERT-L267.4330.159000
cross-encoder/ms-marco-TinyBERT-L468.0934.52900
cross-encoder/ms-marco-TinyBERT-L669.5736.13680
cross-encoder/ms-marco-electra-base71.9936.41340
Other models   
nboost/pt-tinybert-msmarco63.6328.82900
nboost/pt-bert-base-uncased-msmarco70.9434.75340
nboost/pt-bert-large-msmarco73.3636.48100
Capreolus/electra-base-msmarco71.2336.89340
amberoad/bert-multilingual-passage-reranking-msmarco68.435.54330
sebastian-hofstaetter/distilbert-cat-margin_mse-T2-msmarco72.8237.88720

下面我们使用cross-encoder/ms-marco-MiniLM-L12-v2模型来进行相似度的计算:

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
from sentence_transformers import CrossEncoder

# 方法一:
model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L12-v2')
scores = model.predict([
    ("How many people live in Berlin?", "Berlin had a population of 3,520,031 registered inhabitants in an area of 891.82 square kilometers."),
    ("How many people live in Berlin?", "Berlin is well known for its museums."),
])
print(scores)

# 方法二:
model = AutoModelForSequenceClassification.from_pretrained('cross-encoder/ms-marco-MiniLM-L12-v2')
tokenizer = AutoTokenizer.from_pretrained('cross-encoder/ms-marco-MiniLM-L12-v2')

features = tokenizer(['How many people live in Berlin?', 'How many people live in Berlin?'], ['Berlin had a population of 3,520,031 registered inhabitants in an area of 891.82 square kilometers.', 'Berlin is well known for its museums.'],  padding=True, truncation=True, return_tensors="pt")

model.eval()
with torch.no_grad():
    scores = model(**features).logits
    print(scores)

如果要进行重排序的话,可以参考下面的文档:

# 从 pymilvus.model.reranker 模块中导入交叉编码器重排序函数
from pymilvus.model.reranker import CrossEncoderRerankFunction

# ✅ 步骤 1:定义交叉编码器(Cross Encoder)重排序函数
# 该函数内部会自动下载 Hugging Face 上的模型并用于 rerank
ce_rf = CrossEncoderRerankFunction(
    model_name="cross-encoder/ms-marco-MiniLM-L12-v2",  # 使用指定的 cross-encoder 模型(支持语义匹配任务)
    device="cpu"  # 指定模型运行设备,如 'cpu' 或 'cuda:0'(GPU)
)

# ✅ 步骤 2:定义用户查询(Query)
query = "What event in 1956 marked the official birth of artificial intelligence as a discipline?"
# 中文翻译:1956年哪一事件标志着人工智能作为一门学科的正式诞生?

# ✅ 步骤 3:准备待重排序的候选文档(Documents)
# 每个字符串都是一个候选答案,Cross Encoder 会将它们与 Query 组合成一个个句对进行评分
documents = [
    "In 1950, Alan Turing published his seminal paper, 'Computing Machinery and Intelligence,' proposing the Turing Test as a criterion of intelligence, a foundational concept in the philosophy and development of artificial intelligence.",
    # 图灵1950年的论文,提出图灵测试,为AI发展奠定哲学基础

    "The Dartmouth Conference in 1956 is considered the birthplace of artificial intelligence as a field; here, John McCarthy and others coined the term 'artificial intelligence' and laid out its basic goals.",
    # 1956年达特茅斯会议,被广泛认为是AI的诞生标志

    "In 1951, British mathematician and computer scientist Alan Turing also developed the first program designed to play chess, demonstrating an early example of AI in game strategy.",
    # 图灵在1951年开发了下棋程序,展示早期AI在博弈中的应用

    "The invention of the Logic Theorist by Allen Newell, Herbert A. Simon, and Cliff Shaw in 1955 marked the creation of the first true AI program, which was capable of solving logic problems, akin to proving mathematical theorems."
    # 1955年“逻辑理论家”程序是首个能解逻辑问题的AI程序
]

# ✅ 步骤 4:调用重排序函数进行语义匹配排序(Reranking)
# Cross Encoder 会对 (query, document) 成对输入进行语义评分,返回得分最高的 top_k 条
results = ce_rf(
    query=query,
    documents=documents,
    top_k=3,  # 返回得分最高的前 3 个文档
)

# ✅ 步骤 5:遍历结果,输出每条结果的索引、分数、文本内容
# Cross Encoder 输出的是基于语义匹配的相关性打分,越高越相关
for result in results:
    print(f"Index: {result.index}")       # 文档在原始列表中的索引
    print(f"Score: {result.score:.6f}")   # Cross Encoder 计算出的相关性得分
    print(f"Text: {result.text}\n")       # 对应的文档内容

Milvus与RAG

RAG简介

Retrieval-Augmented Generation(RAG)是一种结合了生成模型与外部知识库的问答与文本生成框架,它通过引入检索机制增强了语言模型的上下文感知能力与事实一致性。传统的预训练语言模型(如 GPT、BERT 等)虽然在多种自然语言任务中表现优异,但其生成内容完全依赖于固定参数中的知识,难以动态更新、覆盖长尾信息或应对实时变化。RAG 模型通过“检索-生成”的两阶段流程克服了这一局限:首先基于输入查询从外部文档库中检索相关文本段落(通常以嵌入向量为索引),然后将检索结果与原始查询共同输入生成模型,从而生成更丰富、准确且可溯源的回答。

Milvus 作为一款专注于向量检索的高性能数据库,在 RAG 框架中扮演着关键的知识检索组件。它能够高效地存储和检索大规模嵌入文档,利用近似最近邻(ANN)算法快速定位与查询语义最接近的上下文。通过将文本数据嵌入为高维向量并存入 Milvus,RAG 系统可以在每次生成任务中实时访问结构化外部知识,从而动态增强语言模型的生成能力。

RAG 与 Milvus 的集成,使得模型生成不仅更具上下文相关性,同时具有良好的可扩展性与知识更新能力。该组合为构建高质量的问答系统、智能客服、科研助手等提供了坚实的技术基础,并代表了当前生成式 AI 系统从封闭式向开放式知识访问的演进方向。

一个简单的模式就是如下图所示的案例:

Retrieval-Augmented Generation(RAG)框架通过将外部知识检索与语言模型的生成能力结合,有效增强了模型的事实一致性与动态更新能力。本文所采用的 RAG 系统主要包括三个核心阶段:文档嵌入与索引构建、在线查询与检索生成、以及可选的重排序增强。

一、文档嵌入与索引构建

首先,系统从原始知识库中收集结构化或非结构化的文档内容。通过分段与预处理操作,每个文档被切分为若干语义清晰的片段。这些片段随后输入至预训练的文本嵌入模型(如 Sentence Transfomers、BGE M3、Instructor、Model2Vec等),将文本转化为固定维度的语义向量。所有向量及其对应的文本片段被统一存储于向量数据库 Milvus 中。Milvus 支持高效的向量索引构建(如 HNSW、ANN、DiskANN 等),为大规模语义检索提供底层支撑。

二、在线检索与生成

当用户提交自然语言查询后,系统首先将查询文本通过同样的嵌入模型转化为查询向量。随后,该向量被用于在 Milvus 中进行近似最近邻检索,系统返回与查询语义最接近的 Top-K 文本片段。此阶段主要实现高召回率的候选过滤,是整个生成流程的语义支撑部分。

三、重排序与上下文拼接(可选增强)

为进一步提升检索结果的相关性,系统可引入 Reranker 模块对 Milvus 返回的候选片段进行重排序。Reranker 通常采用基于 Transformer 的 Cross-Encoder 架构逐对计算候选段与查询之间的交互得分,以获得更高精度的排序结果。或者BGE的Reranker模型来获取排序结果。排序后的文本被选取若干条,并与原始用户查询共同拼接为生成模型的输入上下文。

四、语言模型生成回答

最终,上下文与用户查询被输入至大型语言模型(如GPT、LLaMA、ChatGLM、DeepSeek等),模型基于输入内容生成语言自然、语义准确、并融合外部知识的响应文本。该流程实现了生成式AI系统与结构化外部知识库之间的协同融合,兼具生成能力与知识可控性。

RAG与Milvus的示例

用Milvus以及相应的embedding和rerankers可以获得相应知识库的问答功能。现在给一个demo示例,假设我们有一个公司A的组织架构和部门内容介绍(纯属虚构,如有雷同,纯属巧合),我们将其放入知识库中,形成一个docs的文档数据,然后把这些数据放入Milvus的数据库中,再通过Client去检索和重排,得到数据context_text之后将其作为背景知识放入输入的数据prompt中,最后用Ollama的流式输出得到结果。

from pymilvus import MilvusClient
from pymilvus import model
from pymilvus.model.reranker import CrossEncoderRerankFunction
from sentence_transformers import SentenceTransformer
import ollama
import time

# Step 1️⃣ 创建 Milvus 客户端并初始化 Collection
client = MilvusClient("milvus_rag_zh.db")
COLLECTION_NAME = "zh_rag_demo"

# 若已存在,删除原 Collection
if client.has_collection(COLLECTION_NAME):
    client.drop_collection(COLLECTION_NAME)

# 创建新的向量集合,维度要和选用的 embedding 模型一致
client.create_collection(
    collection_name=COLLECTION_NAME,
    dimension=768,  # 使用默认模型生成的是 768 维向量
)

# Step 2️⃣ 中文句向量模型加载,用于对文档和查询编码
embedding_model = model.DefaultEmbeddingFunction()


# 准备中文知识库文档(可视为 RAG 的数据库),伪造的数据,如有雷同,纯属巧合。
docs = [
    "A公司由五个一级部门组成:战略规划部、产品研发部、市场销售部、人力资源部与财务管理部。",
    "战略规划部负责公司整体战略制定与年度业务目标设定,由高级副总裁王蕾领导。",
    "产品研发部下设三个小组:智能算法组、后端架构组、移动客户端组,分别由赵新宇、韩飞和李倩倩负责。",
    "人力资源部目前有6名成员,涵盖招聘、培训、绩效、薪酬等模块,负责人为HR总监陈蓉。",
    "市场销售部主要负责B端客户开拓与服务,目前设有北京、上海、深圳三个销售大区。",
    "财务管理部由CFO黄志明牵头,下设预算组、税务组与资金组,分别管理不同的财务职责。",
    "A公司CEO为沈立峰,CTO为冯启航,两人每季度召开一次高管战略会,协调部门合作。",
    "智能算法组正在负责天眼项目的模型迭代,预计Q3完成V2版本部署。",
    "后端架构组正在推动服务中台的容器化改造,目前已完成40%的微服务迁移。",
    "移动客户端组刚完成v6.2.0版本的迭代,新增了消息推送与报表订阅功能。"
]

# 将中文文档编码为向量(embedding)
vectors = embedding_model.encode_documents(docs)

# 组织结构化数据,插入到 Milvus 中
data_to_insert = [
    {"id": i, "vector": vectors[i], "text": docs[i], "tag": "公司组织架构"}
    for i in range(len(docs))
]

client.insert(collection_name=COLLECTION_NAME, data=data_to_insert)

# Step 3️⃣ 用户输入查询(中文)
query = "谁是A公司的CTO?"

# 查询向量化
query_vector = embedding_model.encode_documents([query])

# Step 4️⃣ 从 Milvus 中基于向量检索 Top-K 最相关文档
search_results = client.search(
    collection_name=COLLECTION_NAME,
    data=query_vector,
    limit=5,
    output_fields=["text"]
)

# print(search_results)
# print(list(search_results[0]))

# 提取原始文本内容
retrieved_texts = [doc["entity"]["text"] for doc in list(search_results[0])]

# Step 5️⃣ 使用 CrossEncoder 进行重排序,提升相关性排名
reranker = CrossEncoderRerankFunction(
    model_name="cross-encoder/ms-marco-MiniLM-L12-v2",
    device="cpu"
)

reranked = reranker(
    query=query,
    documents=retrieved_texts,
    top_k=3  # 可以根据需要调整返回数量
)

# 拼接重排序后的文档,作为 LLM 的上下文
context_text = "\n".join([r.text for r in reranked])

# Step 6️⃣ 构造 prompt,准备传入 LLM 生成回答
prompt = f"""
请根据以下背景知识回答用户提出的问题:

【背景知识】
{context_text}

【问题】
{query}

【回答】
"""

# Step 7️⃣ 使用 Ollama + DeepSeek 模型生成回答(流式输出)
print("🤖 正在生成回答...\n")

# 调用 Ollama SDK,使用 streaming=True 获取流式输出
stream = ollama.chat(
    model="deepseek-r1:32b",
    messages=[{"role": "user", "content": prompt}],
    stream=True
)

# Step 8️⃣ 打印流式输出结果
response_buffer = ""
for chunk in stream:
    delta = chunk.get("message", {}).get("content", "")
    print(delta, end="", flush=True)  # 实时输出
    response_buffer += delta

通过更换query的内容,可以获得以下结论:

  1. 向量嵌入和Rerank存在优化空间,包括模型的选型、参数配置等内容,否则知识库无法精确找出相关知识;
  2. 找出的个性化词条作为输入是不会影响外部公开问题的正常回答的。
查询回答(来自 RAG + LLM)结论
谁是A公司的CTO?冯启航是A公司的CTO。正确
A公司哪个小组负责天眼项目?A公司的天眼项目负责人尚未明确,但根据背景知识,该项目可能由产品研发部的其中一个小组负责。具体来说: 1. 智能算法组:负责AI和数据分析相关的工作。2. 后端架构组:负责应用程序的后台结构。3. 移动客户端组:负责移动端应用开发。 如需确定具体负责人,请提供更多项目细节。错误,原因是搜索的时候没有找到天眼项目这个信息。
A公司中产品研发部下有哪些组?A公司中产品研发部下有智能算法组、后端架构组和移动客户端组三个小组。正确
A公司的预算组是哪位负责?根据提供的背景知识,无法确定A公司预算组的具体负责人。虽然预算管理通常由财务管理部负责,但背景信息中并未提及该部门的负责人或相关细节。因此,基于现有信息,我们不能确定预算组的负责人是谁。 【答案】根据提供的背景知识,无法确定A公司的预算组是由谁负责,因为相关信息未提及。错误,原因是搜索的时候没有找到预算组这个信息。关键字的搜索不够好。 
王蕾负责什么?王蕾是公司的高级副总裁,负责领导战略规划部,主要职责包括制定公司整体战略和设定年度业务目标。正确
什么是数学分析?数学分析是研究函数的性质及其变化规律的一个重要学科,主要涵盖微积分、实变函数、复变函数等内容,广泛应用于物理学、工程学和经济学等领域。正确,没有受到知识库的影响
物理学有哪些研究方向?物理学是一门广泛而深入的科学,涵盖了许多不同的研究方向和分支领域。以下是一些主要的研究方向: 1. 经典力学:研究物体在宏观尺度下的运动和相互作用,包括牛顿力学、拉格朗日力学和哈密顿力学。正确,没有受到知识库的影响

同时,可以查看Collection的数据库信息。

from pymilvus import MilvusClient

# 创建 Milvus 客户端并连接数据库
client = MilvusClient("milvus_rag_zh.db")
COLLECTION_NAME = "zh_rag_demo"

# 获取集合的collection信息。要查看数据库中的所有集合,你可以使用 list_collections() 方法,它将返回当前数据库中的所有集合名称。
collection_list = client.list_collections()
print("List of Collections:", collection_list)

# 查看集合的统计信息,要查看集合中包含的实体数量,你可以使用 get_collection_stats() 方法。它将返回集合的统计信息,其中包括存储的向量数量(即实体数量)。
collection_stats = client.get_collection_stats(COLLECTION_NAME)
print("Collection Stats:", collection_stats)

# 还可以获取特定 Collection 的详细信息
desc_collection = client.describe_collection(collection_name=COLLECTION_NAME)
print("Describe Collection:", desc_collection)

Milvus总结

综上所述,Milvus 是一个开源的高性能向量数据库,专为处理大规模、高维度数据的存储与检索而设计。它支持多种数据类型的高效索引和快速检索,特别适用于机器学习、人工智能和数据分析领域中的相似性搜索任务。Milvus 能够处理从文本、图像到视频等多种格式的向量数据,并且具有出色的扩展性,能够应对大数据量的挑战。通过提供如 IVF、HNSW、Annoy 等多种索引方式,Milvus 能显著提升高维向量检索的速度和效率,是实现高效数据检索和实时分析的重要工具。

参考资料

  1. Milvus的官网:https://milvus.io/zh
  2. Milvus的GitHub:https://github.com/milvus-io/milvus
  3. 用Python操作Milvus向量数据库的简明教程:https://www.dboop.com/ops/用python操作milvus向量数据库的简明教程/#创建索引

Leave a comment