Article
第一章:Milvus 概述与核心概念
1.1 什么是 Milvus?
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Milvus | 一个开源的向量数据库,专为高效存储、索引和搜索大规模向量数据而设计,支持 AI 应用中的相似性检索场景。 | Milvus 本身不处理原始数据(如图像、文本),需由上游模型(如 BERT、ResNet)生成向量后存入。 |
| 开源性质 | 由 Zilliz 发起,现为 LF AI & Data 基金会孵化项目,采用 Apache 2.0 许可证。 | 社区版免费使用;企业版提供额外功能(如 RBAC、审计日志)。 |
| 核心目标 | 解决高维向量的”近似最近邻”(ANN)搜索问题,在海量数据下实现低延迟、高吞吐的相似性查询。 | 不适用于精确匹配或事务型操作(如 ACID)。 |
1.2 向量数据库的基本原理
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 向量化(Embedding) | 将非结构化数据(文本、图像等)通过机器学习模型转换为固定长度的数值向量,保留语义信息。 | 向量质量直接影响检索效果;需选择合适的预训练模型。 |
| 相似性度量 | 衡量两个向量之间”距离”或”相似度”的数学方法,常见包括:欧氏距离(L2)、内积(IP)、余弦相似度(Cosine)。 | Milvus 中 metric_type 需与向量归一化方式匹配(如 Cosine 等价于归一化后的 IP)。 |
| 近似最近邻(ANN) | 在牺牲少量精度的前提下,大幅加速高维空间中最近邻搜索的技术,避免全量暴力计算(Brute-force)。 | ANN 索引需权衡召回率(Recall)、延迟与内存占用。 |
| 向量索引 | 对向量数据构建的数据结构(如 IVF、HNSW),用于加速 ANN 搜索。 | 索引构建是异步过程,插入数据后需调用 create_index 触发。 |
1.3 Milvus 的架构组成
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Proxy | 接收客户端请求的入口节点,负责请求解析、校验、分发和结果聚合。 | 所有 SDK 请求均通过 Proxy 路由,支持负载均衡。 |
| Root Coordinator (RootCoord) | 全局元数据管理与 DDL(如创建集合)协调器。 | 单点主控,集群模式下需高可用部署。 |
| Data Coordinator (DataCoord) | 管理数据节点(DataNode)与日志快照(Binlog),协调数据持久化。 | 负责触发 flush 和 compaction 操作。 |
| Query Coordinator (QueryCoord) | 调度查询任务,管理查询节点(QueryNode)的加载与搜索。 | 控制 segment 加载到内存供搜索使用。 |
| Index Coordinator (IndexCoord) | 调度索引构建任务,管理 IndexNode。 | 索引构建为资源密集型操作,建议独立节点运行。 |
| 存储层(MinIO / S3 + ETCD + Pulsar / Kafka) | 分别用于对象存储(向量/标量数据)、元数据存储(ETCD)、消息队列(日志流)。 | 生产环境必须使用持久化存储,避免数据丢失。 |
| SDK(Python/Java/Go/Node.js) | 客户端开发工具包,封装 gRPC 调用,提供高层 API。 | 推荐使用 pymilvus(Python SDK),文档最完善。 |
1.4 Milvus 与其他向量数据库的对比
| 框架名称 | 所属类别 | 用途 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| Milvus | 开源向量数据库 | 大规模 ANN 搜索、AI 应用集成 | 推荐系统、RAG、图像检索、需要水平扩展的生产环境 | 架构较复杂,适合中大型团队;社区活跃 |
| Pinecone | 托管向量数据库(SaaS) | 快速构建向量搜索应用 | 初创公司、MVP 验证、无运维能力团队 | 闭源,按用量计费,数据出域风险需评估 |
| Weaviate | 向量搜索引擎(含图谱) | 语义搜索 + 知识图谱 | 需要结合本体(schema)与向量的混合场景 | 内置向量化模块(可选),但性能弱于专用 ANN 引擎 |
| Qdrant | Rust 编写的向量数据库 | 高性能 ANN 搜索 | 低延迟要求、边缘部署、Rust 技术栈团队 | 支持 payload 过滤,但集群功能较新,稳定性待验证 |
| FAISS(Facebook) | 向量索引库(非数据库) | 单机向量索引与搜索 | 研究实验、离线批处理、无需持久化场景 | 无服务管理、无并发控制、无标量过滤,不适合在线服务 |
第二章:环境搭建与快速入门
2.1 安装 Milvus(单机版 / 集群版)
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 安装 Docker 和 Docker Compose | 执行官方命令安装 Docker 引擎及 docker-compose 工具(Linux/macOS/Windows WSL2)。 | Windows 用户建议使用 WSL2,避免 Docker Desktop 性能问题。 |
| 下载 Milvus 单机版配置文件 | 运行 wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml -O docker-compose.yml | 版本号(如 v2.4.0)需替换为最新稳定版。 |
| 启动单机版 Milvus | 在配置文件目录执行 docker-compose up -d | 首次启动需拉取多个镜像(约 2–5 GB),确保网络畅通。 |
| 安装集群版(Kubernetes) | 使用 Helm Chart:helm repo add milvus https://milvus-io.github.io/milvus-helm/,然后 helm install my-release milvus/milvus | 需提前部署 Kubernetes 集群(如 Minikube、EKS、ACK),并配置持久化存储。 |
| 验证组件状态 | 单机版:docker-compose ps;集群版:kubectl get pods -l app.kubernetes.io/instance=my-release | 所有组件状态应为 Up(单机)或 Running(集群),若失败需检查日志。 |
2.2 启动与验证服务
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 检查服务端口 | 默认 Proxy 端口为 19530(gRPC),RESTful API 端口为 9091(需启用)。 | 若修改了 docker-compose.yml 中的端口映射,需对应调整客户端连接地址。 |
| 使用 milvus-cli 验证 | 安装 CLI:pip install milvus-cli,然后运行 milvus_cli,执行 connect --host localhost --port 19530 | CLI 适合交互式调试,但不适用于程序化集成。 |
| 查看健康状态 | 通过 REST API(若启用):curl http://localhost:9091/healthz 应返回 {"status":"ok"} | REST 接口默认未开启,需在配置文件中设置 rest.enable: true。 |
| 检查依赖服务 | 确认 etcd、minio、pulsar/kafka 容器正常运行(单机版)或 Pod 就绪(集群版)。 | 任一依赖服务异常将导致 Milvus 不可用。 |
2.3 使用 Python SDK 连接 Milvus
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
connections.connect | connections.connect(alias="default", host="localhost", port=19530) | 建立与 Milvus 服务的连接 | from pymilvus import connectionsconnections.connect("default", host="localhost", port=19530) | alias 用于多连接管理;默认使用 “default”;host 可为 IP 或域名 |
connections.list_connections | connections.list_connections() | 列出当前所有已建立的连接 | print(connections.list_connections()) | 返回元组列表 |
connections.get_connection | connections.get_connection(alias="default") | 获取指定别名的连接对象 | client = connections.get_connection("default") | 若连接未建立,返回 None |
connections.disconnect | connections.disconnect(alias="default") | 断开指定连接 | connections.disconnect("default") | 程序退出前建议显式断开,释放资源 |
2.4 Hello Milvus:第一个向量插入与查询示例
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Collection | Collection(name, schema=None, using="default", shards_num=2) | 创建或获取集合实例 | from pymilvus import Collection, FieldSchema, CollectionSchema, DataTypefields = [ FieldSchema("id", DataType.INT64, is_primary=True, auto_id=True), FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=128)]schema = CollectionSchema(fields, "hello milvus")collection = Collection("hello_milvus", schema) | 首次创建需定义 schema;后续可直接通过名称获取已有集合 |
insert | collection.insert(data) | 向集合插入向量数据 | import randomvectors = [[random.random() for _ in range(128)] for _ in range(10)]collection.insert([vectors]) | data 为列表的列表,顺序需与 schema 字段一致;标量字段可省略(若 auto_id=True) |
create_index | collection.create_index(field_name, index_params) | 为向量字段构建索引 | index_params = {"index_type": "IVF_FLAT", "metric_type": "L2", "params": {"nlist": 128}}collection.create_index("embedding", index_params) | 必须在搜索前调用;否则会触发 FLAT 暴力搜索(性能差) |
load | collection.load() | 将集合数据加载到内存供查询 | collection.load() | 插入后必须 load 才能搜索;首次 load 可能较慢 |
search | collection.search(data, anns_field, param, limit) | 执行向量相似性搜索 | search_vectors = [[random.random() for _ in range(128)]]search_params = {"metric_type": "L2", "params": {"nprobe": 10}}results = collection.search(search_vectors, "embedding", search_params, limit=5) | data 为查询向量列表;anns_field 指定向量字段;limit 控制返回 top-k 结果 |
drop_collection | utility.drop_collection(name) | 删除集合(含数据和索引) | from pymilvus import utilityutility.drop_collection("hello_milvus") | 操作不可逆,谨慎使用 |
第三章:数据模型与集合管理
3.1 Collection(集合)的概念与创建
| 概念/方法名称 | 说明 / 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| Collection | Milvus 中存储向量和标量数据的基本单元,类似关系数据库中的”表”。 | 组织和管理结构化向量数据 | — | 一个 Milvus 实例可包含多个 Collection,彼此隔离 |
| 创建 Collection | Collection(name, schema, using="default", shards_num=2, ...) | 显式定义并创建新集合 | from pymilvus import Collection, CollectionSchemacollection = Collection("my_collection", schema) | 集合名需全局唯一;若已存在同名集合且 schema 不同,将报错 |
| 获取已有 Collection | Collection(name) | 获取已存在的集合实例(无需重复传 schema) | collection = Collection("my_collection") | 若集合不存在,会抛出异常 |
has_collection | utility.has_collection(collection_name, using="default") | 检查集合是否存在 | from pymilvus import utilityexists = utility.has_collection("my_collection") | 返回布尔值,用于安全判断 |
drop_collection | utility.drop_collection(collection_name, using="default") | 删除集合及其所有数据和索引 | utility.drop_collection("my_collection") | 不可逆操作,删除后无法恢复 |
shards_num | 创建时指定分片数量(默认为 2) | 控制数据写入并行度,影响吞吐 | Collection("my_col", schema, shards_num=4) | 分片数一旦设定不可修改;建议根据写入负载调整 |
3.2 Schema(模式)定义与字段类型
| 字段属性 / 方法 | 语法 / 说明 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
FieldSchema | FieldSchema(name, dtype, description="", is_primary=False, auto_id=False, dim=None, max_length=None, ...) | 定义集合中单个字段的元信息 | FieldSchema("id", DataType.INT64, is_primary=True, auto_id=True) | 必须指定 dtype;主键字段必须为 INT64 或 VARCHAR |
CollectionSchema | CollectionSchema(fields, description="", enable_dynamic_field=False) | 聚合多个字段,构成完整集合结构 | CollectionSchema([id_field, vec_field], "user embeddings") | fields 列表顺序无强制要求,但需包含一个主键 |
DataType.INT64 | 整型标量字段 | 存储用户 ID、时间戳等 | FieldSchema("user_id", DataType.INT64) | 可作为主键(is_primary=True) |
DataType.VARCHAR | 可变长字符串 | 存储文本标签、名称等 | FieldSchema("name", DataType.VARCHAR, max_length=64) | 必须指定 max_length(1–65535) |
DataType.FLOAT_VECTOR | 浮点向量字段 | 存储嵌入向量(如 BERT 输出) | FieldSchema("emb", DataType.FLOAT_VECTOR, dim=768) | 必须指定 dim(正整数),所有向量维度必须一致 |
DataType.BINARY_VECTOR | 二值向量字段 | 存储哈希编码等二进制向量 | FieldSchema("hash", DataType.BINARY_VECTOR, dim=256) | dim 表示比特数,实际字节数为 dim/8 |
is_primary | 字段参数,标记为主键 | 唯一标识实体 | FieldSchema("id", DataType.VARCHAR, is_primary=True, max_length=32) | 仅支持一个主键字段;VARCHAR 主键不支持 auto_id |
auto_id | 自动递增主键(仅 INT64) | 自动生成唯一 ID | FieldSchema("id", DataType.INT64, is_primary=True, auto_id=True) | 若设为 True,插入时不能提供该字段值 |
enable_dynamic_field | CollectionSchema 参数 | 允许插入未在 schema 中声明的字段(类似 JSON 扩展) | CollectionSchema([...], enable_dynamic_field=True) | Milvus 2.4+ 支持;动态字段仅支持标量类型 |
3.3 索引类型与参数配置
| 索引类型 | 适用场景 | 参数说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| FLAT | 小数据集(< 10K 向量)或需要 100% 精确结果 | 无额外参数 | {"index_type": "FLAT", "metric_type": "L2"} | 不建索引也可搜索(默认回退到 FLAT),但性能差 |
| IVF_FLAT | 中等规模数据,平衡速度与精度 | nlist(聚类中心数) | {"index_type": "IVF_FLAT", "metric_type": "L2", "params": {"nlist": 100}} | nlist 建议为 sqrt(N),N 为向量总数 |
| IVF_SQ8 | 内存受限场景,量化压缩向量 | nlist | {"index_type": "IVF_SQ8", "metric_type": "L2", "params": {"nlist": 100}} | 向量被量化为 1 字节/维度,精度略有损失 |
| HNSW | 低延迟、高召回场景(推荐) | M(每节点连接数),efConstruction(建图时候选数) | {"index_type": "HNSW", "metric_type": "IP", "params": {"M": 16, "efConstruction": 100}} | 内存占用较高;M 越大精度越高但内存越大 |
| ANNOY | 轻量级、可持久化索引 | n_trees(树数量) | {"index_type": "ANNOY", "metric_type": "L2", "params": {"n_trees": 50}} | 构建快,适合静态数据集 |
| DISKANN | 超大规模(十亿级)、磁盘索引 | search_cache_budget_gb, build_dram_budget_gb | {"index_type": "DISKANN", "metric_type": "L2", "params": {"search_cache_budget_gb": 10}} | 仅企业版支持;需 SSD 存储 |
| metric_type | 距离度量方式 | L2(欧氏距离)、IP(内积)、COSINE(余弦) | 必须与向量生成方式匹配 | 使用 COSINE 时,向量应预先 L2 归一化;否则等效于 IP |
3.4 Partition(分区)与 Alias(别名)管理
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
create_partition | collection.create_partition(partition_name) | 在集合内创建逻辑分区 | collection.create_partition("part_2024") | 分区名在同一集合内唯一;可用于按时间/业务分片 |
has_partition | collection.has_partition(partition_name) | 检查分区是否存在 | if collection.has_partition("part_2024"): ... | 返回布尔值 |
drop_partition | collection.drop_partition(partition_name) | 删除指定分区及其数据 | collection.drop_partition("old_part") | 不可逆操作 |
insert into partition | collection.insert(data, partition_name=...) | 向指定分区插入数据 | collection.insert([ids, vectors], partition_name="part_2024") | 若未指定,默认插入 _default 分区 |
search with partition | collection.search(..., partition_names=[...]) | 限定搜索范围到特定分区 | collection.search(vectors, "embedding", params, limit=5, partition_names=["part_2024"]) | 可提升查询效率,减少扫描数据量 |
create_alias | utility.create_alias(collection_name, alias, using="default") | 为集合创建别名 | utility.create_alias("my_collection_v2", "user_emb") | 便于版本切换或蓝绿部署 |
drop_alias | utility.drop_alias(alias, using="default") | 删除别名 | utility.drop_alias("user_emb") | 不影响原集合 |
alter_alias | utility.alter_alias(collection_name, alias, using="default") | 将别名指向另一个集合 | utility.alter_alias("my_collection_v3", "user_emb") | 实现零停机切换;原集合不再被别名引用 |
第四章:向量操作与检索
4.1 插入向量数据
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Collection.insert | collection.insert(data, partition_name=None, timeout=None) | 向集合(或指定分区)插入结构化数据 | import randomvectors = [[random.random() for _ in range(128)] for _ in range(5)]ids = [1, 2, 3, 4, 5]collection.insert([ids, vectors]) | data 必须是列表的列表,顺序与 schema 字段一致;若主键 auto_id=True,则无需提供 ID |
| 批量插入建议 | 单次插入 1K–10K 条 | 提高写入吞吐,减少 gRPC 开销 | collection.insert([large_id_list, large_vector_list]) | 避免单次插入过大(>100K),可能触发 gRPC 消息大小限制(默认 100MB) |
| 动态字段插入 | 当 enable_dynamic_field=True 时,可传入字典 | 插入 schema 未定义的标量字段 | collection.insert([ids, vectors, {"tag": ["A", "B", ...]}]) | 动态字段仅支持标量类型(INT64、VARCHAR 等),不支持向量 |
4.2 查询与删除实体
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Collection.query | collection.query(expr, output_fields=None, partition_names=None, timeout=None) | 基于主键或标量条件查询实体 | results = collection.query(expr="id in [1, 2, 3]", output_fields=["id", "embedding"]) | expr 使用类似 SQL 的布尔表达式;必须包含主键或已建标量索引的字段 |
Collection.delete | collection.delete(expr, partition_names=None, timeout=None) | 删除满足条件的实体 | collection.delete(expr="id in [1, 2]") | 删除操作异步生效;数据在 compaction 后才真正释放空间 |
| 标量过滤字段要求 | 字段需为标量类型(非向量) | 支持等值、范围、IN 等操作 | expr="age > 25 and city == 'Beijing'" | 若字段未建标量索引,全表扫描性能差;建议对高频过滤字段建索引 |
flush | utility.flush([collection_name], using="default") | 强制将缓冲区数据持久化到存储 | utility.flush(["my_collection"]) | 默认每秒自动 flush;手动 flush 可确保刚插入数据可被 query 查到 |
4.3 向量相似性搜索(Search)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Collection.search | collection.search(data, anns_field, param, limit, expr=None, output_fields=None, partition_names=None, timeout=None) | 执行 ANN 向量相似性搜索 | search_vec = [[0.1, 0.2, ..., 0.128]]res = collection.search(search_vec, "embedding", {"metric_type": "L2", "params": {"nprobe": 10}}, limit=3) | data 为二维列表([[vec1], [vec2], ...]);anns_field 必须是向量字段名 |
| 返回结果结构 | 每个查询返回一个 Hit 对象列表 | 包含 id、distance、entity(若 output_fields 指定) | for hit in res[0]: print(hit.id, hit.distance) | distance 值含义取决于 metric_type(L2 越小越相似,IP 越大越相似) |
| 必须 load | 搜索前需调用 collection.load() | 将 segment 加载到 QueryNode 内存 | collection.load() | 未 load 会报错:“collection not loaded” |
| 多向量搜索 | 一次传入多个查询向量 | 批量处理查询请求 | collection.search([vec1, vec2, vec3], ...) | 每个向量独立搜索,结果按输入顺序返回 |
4.4 混合查询(Scalar + Vector)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Collection.search with expr | 在 search 中使用 expr 参数 | 先按标量条件过滤,再在子集中做向量搜索 | collection.search(vectors, "embedding", params, limit=5, expr="category == 'electronics'") | expr 过滤发生在向量搜索之前,显著缩小搜索范围 |
| 支持的标量操作符 | ==, !=, >, <, >=, <=, in, not in | 构建布尔过滤条件 | expr="price >= 100 and price <= 500" | 不支持 LIKE、正则等复杂文本操作;不支持 between,需改写为范围表达式 |
| 标量字段索引 | 对高频过滤字段创建标量索引 | 加速 expr 过滤 | collection.create_index("category", {"index_type": "Trie"}) | VARCHAR 推荐 Trie 索引,INT64 可用 Sorted 或 Inverted |
| 性能影响 | 混合查询性能 ≈ 标量过滤速度 + 向量搜索速度 | 过滤后数据量越小,向量搜索越快 | — | 若 expr 过滤后无数据,返回空结果,不报错 |
4.5 搜索参数详解(top_k, metric_type, params 等)
| 参数名称 | 说明 | 取值示例 | 用途 | 注意事项 |
|---|---|---|---|---|
limit (top_k) | 返回最相似的前 k 个结果 | limit=10 | 控制返回数量 | 受服务端 max_top_k 限制(默认 16384) |
metric_type | 相似性度量方式 | "L2", "IP", "COSINE" | 定义距离计算方法 | 必须与建索引时的 metric_type 一致;COSINE 要求向量已 L2 归一化 |
params.nprobe (IVF) | 搜索时探测的聚类中心数 | {"nprobe": 10} | 平衡精度与速度(越大越准越慢) | 建议 nprobe ≈ sqrt(nlist);最大不超过 nlist |
params.ef (HNSW) | 搜索时的候选队列大小 | {"ef": 100} | 控制 HNSW 搜索深度 | ef ≥ limit;越大召回率越高,延迟越高 |
params.search_length (ANNOY) | 遍历的树节点数 | {"search_k": -1} | ANNOY 中 search_k = search_length * n_trees | search_k=-1 表示自动设为 n_trees * limit |
round_decimal | 距离值保留小数位数 | round_decimal=3 | 控制返回 distance 精度 | 仅影响显示,不影响排序 |
ignore_growing | 是否忽略正在插入的增量数据 | ignore_growing=True | 避免读到未完成写入的数据 | 默认 False;高一致性场景可设为 True |
第五章:索引与性能优化
5.1 支持的索引类型(FLAT, IVF_FLAT, HNSW, ANNOY 等)
| 索引类型 | 向量类型支持 | 精度 | 内存占用 | 构建速度 | 搜索速度 | 适用场景 | 注意事项 |
|---|---|---|---|---|---|---|---|
| FLAT | FLOAT_VECTOR, BINARY_VECTOR | 100%(精确) | 高(原始向量) | 快(无构建) | 慢(O(N)) | 小数据集(<10K)、测试验证 | 无需显式建索引;搜索时自动使用 |
| IVF_FLAT | FLOAT_VECTOR | 高(可调) | 高(原始向量 + 聚类中心) | 中 | 快(O(sqrt(N))) | 中等规模(10K–10M) | 需设置 nlist;nprobe 控制精度/速度 |
| IVF_SQ8 | FLOAT_VECTOR | 中(量化损失) | 低(1字节/维度) | 中 | 快 | 内存受限、大规模部署 | 不支持 BINARY_VECTOR;精度略低于 IVF_FLAT |
| IVF_PQ | FLOAT_VECTOR | 中低(乘积量化) | 极低(可配置压缩比) | 慢 | 快 | 超大规模、内存极度受限 | 需调参 m(子空间数),m 必须整除 dim |
| HNSW | FLOAT_VECTOR | 高 | 高(图结构) | 慢 | 极快(O(log N)) | 低延迟、高召回生产场景 | M 和 efConstruction 影响内存与精度;不支持动态插入后高效更新 |
| ANNOY | FLOAT_VECTOR | 中高 | 中(树结构) | 快 | 快 | 静态数据集、轻量部署 | 构建后不可增量更新;适合只读场景 |
| DISKANN | FLOAT_VECTOR | 高 | 低(主存缓存+磁盘) | 慢 | 快(SSD 加速) | 十亿级向量、成本敏感 | 仅 Milvus 企业版支持;需 NVMe SSD |
| BIN_FLAT | BINARY_VECTOR | 100% | 中(原始比特) | 快 | 慢 | 小规模二值向量 | metric_type 仅支持 JACCARD、HAMMING、TANIMOTO |
| BIN_IVF_FLAT | BINARY_VECTOR | 高 | 中 | 中 | 快 | 中等规模二值向量 | nlist 参数同样适用 |
5.2 索引构建与管理
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Collection.create_index | collection.create_index(field_name, index_params, index_name=None, timeout=None) | 为指定字段创建索引 | index_params = {"index_type": "HNSW", "metric_type": "IP", "params": {"M": 16, "efConstruction": 100}}collection.create_index("embedding", index_params) | 同一字段只能有一个索引;重复调用会覆盖(异步生效) |
Collection.has_index | collection.has_index(index_name=None) | 检查是否已为向量字段建索引 | if not collection.has_index(): print("No index") | 若未指定 index_name,检查默认向量字段索引 |
Collection.index | collection.index | 获取当前索引对象 | idx = collection.indexprint(idx.params) | 返回 Index 对象,包含 params、field_name 等属性 |
Collection.drop_index | collection.drop_index(index_name=None) | 删除现有索引 | collection.drop_index() | 删除后搜索将回退到 FLAT(暴力搜索) |
| 异步构建 | create_index 为异步操作 | 不阻塞主线程 | collection.create_index(...)print("Indexing started") | 可通过 IndexCoord 日志或监控观察进度 |
| 多字段索引 | 分别对不同向量字段建索引 | 支持多向量集合 | collection.create_index("vec1", params1)collection.create_index("vec2", params2) | 每个向量字段独立索引;搜索时指定 anns_field |
5.3 搜索性能调优策略
| 调优方向 | 策略 | 说明 | 注意事项 |
|---|---|---|---|
| 索引选型 | 根据数据规模选择:小数据用 FLAT,中等用 IVF_FLAT/HNSW,超大用 DISKANN/PQ | 平衡精度、延迟、内存 | HNSW 适合低延迟;IVF 系列适合高吞吐 |
| nlist / nprobe(IVF) | nlist ≈ sqrt(N),nprobe ≈ sqrt(nlist) | 提高召回率同时控制延迟 | nprobe 越大越准但越慢;线上建议固定 nprobe |
| HNSW 参数 | M=8~64,efConstruction=100~500,ef≥limit | M 控制图密度,ef 控制搜索深度 | M 越大内存越高;efConstruction 过大会导致构建慢 |
| 分区过滤 | 使用 partition_names 限定搜索范围 | 减少扫描数据量 | 适用于时间分区、业务隔离场景 |
| 标量索引 | 对高频过滤字段(如 category、status)建标量索引 | 加速 expr 过滤阶段 | VARCHAR 用 Trie,INT64 用 Sorted 或 Inverted |
| load 前预热 | 在低峰期执行 load | 避免首次搜索冷启动延迟 | 可结合 Kubernetes readiness probe |
| 批量搜索 | 一次 search 传入多个查询向量 | 提高 QPS,降低网络开销 | 单次 batch size 建议 10–100 |
| 避免频繁建索引 | 数据写入完成后统一建索引 | 减少 I/O 和 CPU 开销 | 流式写入场景可定期重建索引 |
5.4 资源配置与硬件建议
| 组件 | 最小配置(单机开发) | 推荐生产配置(10M 向量) | 说明 | 注意事项 |
|---|---|---|---|---|
| Proxy | 2 vCPU, 4 GB RAM | 4–8 vCPU, 8–16 GB RAM | 处理客户端请求 | 高并发时需水平扩展多个 Proxy |
| QueryNode | 4 vCPU, 8 GB RAM | 16–32 vCPU, 64–128 GB RAM | 加载 segment 并执行搜索 | 内存必须 ≥ 向量数据 + 索引大小;HNSW 内存需求高 |
| DataNode | 2 vCPU, 4 GB RAM | 8 vCPU, 32 GB RAM | 处理数据 flush 和 compaction | 写入吞吐高时需更多资源 |
| IndexNode | 4 vCPU, 8 GB RAM | 16 vCPU, 64 GB RAM | 异步构建索引 | 索引构建为 CPU/内存密集型,建议独占节点 |
| MinIO/S3 | 本地磁盘 50 GB | 云存储(≥1 TB,高吞吐) | 存储原始向量和标量 | 建议使用 SSD;IOPS 影响 flush 性能 |
| ETCD | 2 vCPU, 4 GB RAM | 4 vCPU, 8 GB RAM(集群模式) | 存储元数据 | 必须持久化;避免单点故障 |
| Pulsar/Kafka | 2 vCPU, 4 GB RAM | 8+ vCPU, 32+ GB RAM | 消息队列,解耦写入 | Pulsar 更适合 Milvus;需配置足够 ledger 空间 |
| 网络 | 1 Gbps | 10 Gbps | 节点间通信 | QueryNode 与 MinIO 之间带宽影响 load 速度 |
| 向量规模估算 | — | 10M × 768 维 FLOAT ≈ 30 GB 原始数据 | 内存需求 ≈ 原始数据 × 1.5(含索引) | HNSW 索引可能达原始数据 2–3 倍内存 |
第六章:高级功能
6.1 动态 Schema 与多向量支持
| 功能名称 | 说明 | 语法 / 操作 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 动态 Schema(Dynamic Schema) | 允许插入未在 CollectionSchema 中显式声明的标量字段,类似 JSON 扩展 | 创建集合时设置 enable_dynamic_field=True | from pymilvus import CollectionSchema, FieldSchema, DataTypeschema = CollectionSchema([ FieldSchema("id", DataType.INT64, is_primary=True), FieldSchema("vec", DataType.FLOAT_VECTOR, dim=128)], enable_dynamic_field=True)col = Collection("dyn_col", schema) | 仅 Milvus 2.4+ 支持;动态字段必须为标量类型(不支持向量) |
| 插入动态字段 | 在 insert 时传入字典作为额外字段 | collection.insert([ids, vectors, {"tag": [...], "score": [...]}]) | col.insert([[1], [[0.1]*128], {"category": ["tech"], "price": [299]}]) | 字段名不能与 schema 中已定义字段冲突 |
| 多向量字段 | 一个集合包含多个向量字段(如文本向量 + 图像向量) | 在 schema 中定义多个 FLOAT_VECTOR / BINARY_VECTOR 字段 | fields = [ FieldSchema("id", DataType.INT64, is_primary=True), FieldSchema("text_emb", DataType.FLOAT_VECTOR, dim=768), FieldSchema("img_emb", DataType.FLOAT_VECTOR, dim=512)]schema = CollectionSchema(fields) | 每个向量字段可独立建索引、独立搜索 |
| 多向量搜索 | 指定 anns_field 进行不同向量的搜索 | collection.search(..., anns_field="text_emb", ...) | res1 = col.search([query_text_vec], "text_emb", params, limit=5)res2 = col.search([query_img_vec], "img_emb", params, limit=5) | 一次 search 只能指定一个 anns_field;如需融合,需客户端后处理 |
6.2 Time Travel 与数据版本控制
| 概念/方法名称 | 说明 | 语法 / 操作 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| Time Travel | Milvus 自动保留最近一段时间(默认 1 小时)的数据快照,支持按时间点查询或恢复 | 通过 guarantee_timestamp 或 travel_timestamp 参数实现 | from pymilvus import utilityts = utility.mkts_from_hybridts(utility.current_time(), milliseconds=30*60*1000) # 30分钟前results = collection.query(expr="id > 0", travel_timestamp=ts) | 依赖 Pulsar/Kafka 的消息保留策略;若消息被清理,则无法回溯 |
| 获取当前时间戳 | utility.current_time() | 获取当前 Hybrid Timestamp | current_ts = utility.current_time() | 返回 hybrid timestamp(64位整数),非 Unix 时间 |
| 转换时间戳 | utility.mkts_from_unixtime(unix_time, milliseconds=0)utility.mkts_from_hybridts(hybrid_ts, milliseconds=delta) | 将 Unix 时间或相对时间转换为 Milvus 时间戳 | ts_1h_ago = utility.mkts_from_hybridts(utility.current_time(), milliseconds=-3600000) | milliseconds 为负表示过去时间 |
| 查询历史数据 | 在 query 或 search 中使用 travel_timestamp | collection.search(..., travel_timestamp=ts_1h_ago) | — | 仅能回溯 retention window 内的数据(由配置 msg_retention_time_minutes 控制,默认 60 分钟) |
| 数据不可变性 | 插入/删除操作不会覆盖原数据,而是追加日志 | — | — | Time Travel 基于日志重放实现,非传统数据库的 MVCC |
6.3 RBAC 权限控制(企业版)
| 功能名称 | 说明 | 操作方式 | 示例 / 说明 | 注意事项 |
|---|---|---|---|---|
| 角色(Role) | 预定义或自定义权限集合,如 admin、reader、writer | 通过 Milvus Enterprise REST API 或 CLI 管理 | CREATE ROLE analyst; | 仅企业版支持;社区版无此功能 |
| 用户(User) | 绑定角色,用于身份认证 | CREATE USER alice IDENTIFIED BY 'password';GRANT ROLE analyst TO alice; | — | 支持 LDAP 集成(企业版高级特性) |
| 权限粒度 | 支持集合级(Collection)、全局级(Global)权限 | GRANT SELECT ON COLLECTION my_col TO analyst; | 权限类型:SELECT(查询)、INSERT、DELETE、CREATE_INDEX、ALL | 不支持字段级(列级)权限控制 |
| 启用认证 | 在 milvus.yaml 中设置 milvus.authn.enabled: true | 配置后所有 SDK 连接需提供用户名密码 | connections.connect(user="alice", password="xxx", ...) | 若未启用 authn,RBAC 无效 |
| 审计日志 | 记录用户操作(企业版) | 日志输出到指定文件或 Kafka | — | 需额外配置审计日志路径和格式 |
⚠️ 注意:RBAC 为 Milvus 企业版专属功能,社区版(开源版)不支持。开发测试请使用企业版试用镜像或联系 Zilliz。
6.4 备份与恢复机制
| 方法/工具名称 | 说明 | 操作步骤 | 代码 / 命令示例 | 注意事项 |
|---|---|---|---|---|
| Milvus Backup(开源工具) | 社区维护的备份工具,基于 MinIO 快照和元数据导出 | 1. 安装 milvus-backup 2. 配置源和目标 MinIO 3. 执行备份/恢复 | milvus-backup backup --backup_name=my_backupmilvus-backup restore --backup_name=my_backup | GitHub 项目:milvus-io/milvus-backup;需停写或容忍备份期间数据不一致 |
| 企业版备份(Zilliz Cloud / Enterprise) | 支持在线一致性快照、增量备份 | 通过管理控制台或 API 触发 | — | 提供 SLA 保障;支持 PITR(Point-in-Time Recovery) |
| 手动备份(不推荐) | 直接复制 MinIO 数据 + ETCD 快照 | 1. 停止 Milvus 2. 备份 MinIO bucket 3. 导出 ETCD 数据 | etcdctl snapshot save snap.db | 极易出错;恢复时版本必须完全一致;仅用于紧急恢复 |
| 恢复流程 | 1. 清空目标集群存储 2. 恢复 MinIO 数据 3. 恢复 ETCD(若需要) 4. 启动 Milvus | — | — | 恢复后需重新 load 集合才能搜索 |
| 限制 | 不支持跨版本恢复 | — | — | 备份与恢复必须使用相同 Milvus 主版本(如 2.4.x → 2.4.y) |
💡 建议:生产环境优先使用
milvus-backup工具或企业版备份方案,避免手动操作。
第七章:生产部署与运维
7.1 Milvus 集群部署(基于 Kubernetes)
| 步骤名称 | 操作细节 | 命令 / 配置示例 | 注意事项 |
|---|---|---|---|
| 准备 Kubernetes 集群 | 使用 EKS、ACK、GKE 或自建 K8s(≥v1.20) | kubectl version | 确保集群有足够资源(CPU ≥ 16 核,内存 ≥ 64 GB) |
| 安装 Helm | Helm 是 Milvus 官方推荐的部署工具 | curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash | Helm v3+,无需 Tiller |
| 添加 Milvus Helm 仓库 | 获取官方 Chart | helm repo add milvus https://milvus-io.github.io/milvus-helm/helm repo update | 确保网络可访问 GitHub Pages |
| 创建 values.yaml 配置文件 | 自定义存储、副本数、资源限制等 | yaml\nminio:\n enabled: true\netcd:\n replicaCount: 3\nkafka:\n enabled: false\npulsar:\n enabled: true\ncomponents:\n proxy:\n replicas: 2\n queryNode:\n replicas: 3\n resources:\n limits:\n memory: "32Gi"\n | 生产环境必须启用持久化存储(MinIO/S3 + ETCD + Pulsar);禁用 standalone 模式 |
| 部署 Milvus | 使用 Helm 安装 | helm install my-milvus milvus/milvus -f values.yaml --namespace milvus --create-namespace | 首次部署需 5–10 分钟拉取镜像 |
| 验证部署状态 | 检查所有 Pod 是否 Running | kubectl get pods -n milvus | 所有组件应为 Running 状态;若 CrashLoopBackOff,检查日志 |
| 暴露服务(可选) | 通过 LoadBalancer 或 Ingress 暴露 Proxy | kubectl expose deployment my-milvus-milvus-proxy --port=19530 --type=LoadBalancer -n milvus | 默认 ClusterIP 仅集群内访问;外部访问需显式暴露 |
7.2 监控与日志(Prometheus + Grafana)
| 组件 | 配置方式 | 关键指标 | 注意事项 |
|---|---|---|---|
| Prometheus | Milvus 内置 metrics 接口(/metrics),默认端口 9091 | query_node_search_latencyinsert_row_countindex_request_success | 在 values.yaml 中启用:metrics: enabled: true serviceMonitor: enabled: true |
| ServiceMonitor(Prometheus Operator) | 自动发现 Milvus Pod 并抓取指标 | — | 需已部署 Prometheus Operator |
| Grafana Dashboard | 导入官方 JSON 模板(ID: 13749) | 可视化 QPS、延迟、资源使用率 | 下载地址:https://grafana.com/grafana/dashboards/13749 |
| 日志收集 | 使用 Filebeat / Fluentd / Loki 收集容器日志 | 错误日志关键词:ERROR, panic, timeout | 日志级别可通过 log.level 配置(debug/info/warn/error) |
| 告警规则(Alertmanager) | 配置关键告警(如 QueryNode OOM、Pulsar backlog 积压) | 示例规则:rate(milvus_query_node_search_latency_seconds_sum[5m]) > 1.0 | 建议监控: - 组件存活 - 搜索延迟 > 1s - 插入队列积压 |
| 启用 RESTful 监控接口 | 在 Proxy 配置中开启 | yaml\nproxy:\n restful:\n enabled: true\n port: 9091\n | /healthz 和 /metrics 依赖此配置 |
7.3 扩容与高可用方案
| 扩容目标 | 操作方式 | 命令 / 配置 | 注意事项 |
|---|---|---|---|
| 扩容 Proxy | 增加入口节点,提升连接处理能力 | 修改 values.yaml:yaml\ncomponents:\n proxy:\n replicas: 3\n然后 helm upgrade my-milvus milvus/milvus -f values.yaml -n milvus | 所有 Proxy 共享元数据,无状态,可任意扩缩 |
| 扩容 QueryNode | 提升搜索吞吐与并发能力 | 增加 replicas 并分配足够内存 | 新 QueryNode 自动从 MinIO 加载 segment;需确保内存 ≥ 数据集大小 |
| 扩容 DataNode | 提升写入吞吐 | 增加 DataNode 副本数 | 写入吞吐 ≈ DataNode 数量 × 单节点能力 |
| 扩容 IndexNode | 加速索引构建 | 增加 IndexNode 副本 | 索引任务自动分发到空闲 IndexNode |
| 高可用保障 | ETCD 集群(3 节点) Pulsar BookKeeper 多副本 MinIO 分布式模式(4 节点起) | Helm Chart 默认启用多副本 | 单点组件(如 RootCoord)通过 K8s Deployment 自愈 |
| 负载均衡 | 在 Proxy 前部署 HAProxy / Nginx / 云 SLB | 配置 TCP 透传 19530 端口 | gRPC 不支持 HTTP 层负载均衡,需 L4 转发 |
| 故障转移 | K8s 自动重启失败 Pod;Pulsar/ETCD 自动选举 | — | 确保 PodDisruptionBudget 配置合理,避免批量驱逐 |
7.4 常见问题排查指南
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 连接被拒绝(Connection refused) | Proxy 未运行或端口未暴露 | 1. kubectl get pods -n milvus2. kubectl get svc -n milvus | 检查 Proxy Pod 状态;若为 ClusterIP,需 port-forward 或暴露服务 |
| 搜索返回空结果 | 1. 未 load 集合 2. 向量未插入 3. metric_type 不匹配 | 1. collection.num_entities2. collection.load()3. 检查建索引和搜索的 metric_type | 插入后必须 load;确认向量维度与 schema 一致 |
| 插入速度慢 | 1. DataNode 资源不足 2. Pulsar 磁盘 IO 慢 3. 单次 batch 太小 | 1. 查看 DataNode CPU/Mem 2. 检查 Pulsar ledger disk latency 3. 增大 batch size(1K–10K) | 升级 DataNode;使用 SSD;批量插入 |
| 内存溢出(OOMKilled) | QueryNode 内存不足 | kubectl describe pod -n milvus <querynode-pod> | 增加 QueryNode 内存 limit;减少单集合数据量;改用 IVF_SQ8 降低内存 |
| 索引构建卡住 | IndexNode 资源不足或参数不合理 | 1. kubectl logs -n milvus <indexnode-pod>2. 检查 IndexCoord 日志 | 增加 IndexNode 内存;避免 dim 过大时使用 HNSW(M 过高) |
| Time Travel 无法回溯 | Pulsar 消息已过期 | 检查 msg_retention_time_minutes 配置(默认 60) | 增大 retention 时间;但会增加存储成本 |
| 权限错误(企业版) | 用户未授权或认证未启用 | 1. 检查 milvus.yaml 中 authn.enabled2. 确认用户角色权限 | 启用认证;为用户授予对应集合权限 |
第八章:应用场景与案例
8.1 图像/视频相似检索
| 步骤名称 | 操作细节 | 代码示例 | 注意事项 |
|---|---|---|---|
| 特征提取 | 使用 CNN(如 ResNet、ViT)将图像/视频帧转换为向量 | from torchvision.models import resnet50model = resnet50(pretrained=True)model.eval()with torch.no_grad(): embedding = model(img_tensor).flatten().tolist() | 向量维度通常为 512/2048;建议 L2 归一化后使用 IP 或 COSINE |
| 构建集合 Schema | 定义 ID、图像路径、向量字段 | fields = [ FieldSchema("img_id", DataType.INT64, is_primary=True), FieldSchema("img_path", DataType.VARCHAR, max_length=256), FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=2048)]schema = CollectionSchema(fields) | 可添加标量字段(如类别、拍摄时间)用于混合查询 |
| 批量插入图像向量 | 遍历图像库,提取并插入向量 | img_ids = list(range(len(embeddings)))paths = [f"/imgs/{i}.jpg" for i in img_ids]collection.insert([img_ids, paths, embeddings]) | 建议每批 5K–10K 条;避免内存溢出 |
| 构建索引 | 选择 HNSW 或 IVF_FLAT | index_params = {"index_type": "HNSW", "metric_type": "IP", "params": {"M": 16, "efConstruction": 100}}collection.create_index("embedding", index_params) | 若向量已归一化,IP 等价于余弦相似度 |
| 相似图检索 | 对查询图提取向量并搜索 | query_vec = extract_feature(query_img)results = collection.search([query_vec], "embedding", {"metric_type": "IP", "params": {"ef": 64}}, limit=10) | 返回结果包含 img_id 和 img_path,可用于展示 |
| 视频帧处理 | 对关键帧或均匀采样帧分别建库 | 每帧视为独立图像,或对视频聚合(如平均池化) | 聚合向量可代表整个视频,但丢失时序信息 |
8.2 推荐系统中的向量召回
| 步骤名称 | 操作细节 | 代码示例 | 注意事项 |
|---|---|---|---|
| 用户/物品向量化 | 使用双塔模型(DSSM)、GraphSAGE 等生成 user/item 向量 | user_emb = user_model(user_features)item_emb = item_model(item_features) | 向量需在同一语义空间;训练时使用对比学习或协同过滤目标 |
| 分别建库 | 创建 user_collection 和 item_collection | item_fields = [ FieldSchema("item_id", DataType.INT64, is_primary=True), FieldSchema("category", DataType.VARCHAR, max_length=64), FieldSchema("emb", DataType.FLOAT_VECTOR, dim=128)]item_col = Collection("items", CollectionSchema(item_fields)) | 物品库通常静态更新;用户向量可实时生成或缓存 |
| 实时召回 | 在线服务中,用用户向量搜索物品库 | user_vec = get_user_embedding(user_id)recs = item_col.search([user_vec], "emb", {"metric_type": "IP", "params": {"nprobe": 20}}, limit=100) | 搜索延迟需 < 50ms;建议使用 HNSW 或 IVF_SQ8 |
| 混合过滤 | 结合业务规则(如地域、库存) | expr = "category == 'electronics' and in_stock == true"recs = item_col.search(..., expr=expr, ...) | 标量字段需建索引(如 Trie for VARCHAR) |
| 多路召回融合 | Milvus 仅负责向量召回,与其他路(如热门、协同过滤)在排序层融合 | — | Milvus 输出作为粗排候选集,送入精排模型 |
| 增量更新 | 新商品上线时插入向量 | item_col.insert([[new_id], [new_cat], [new_emb]]) | 插入后需触发索引重建(或使用流式索引策略) |
8.3 大模型 RAG 架构集成
| 步骤名称 | 操作细节 | 代码示例 | 注意事项 |
|---|---|---|---|
| 文本分块 | 将文档切分为语义完整的小段(chunk) | from langchain.text_splitter import RecursiveCharacterTextSplittersplitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50)chunks = splitter.split_text(doc) | 避免跨句子切分;保留元数据(如 source、page) |
| 向量化 | 使用 embedding 模型(如 bge-large、text-embedding-ada-002) | from sentence_transformers import SentenceTransformermodel = SentenceTransformer('BAAI/bge-large-en')embeddings = model.encode(chunks).tolist() | 选择与任务匹配的 embedding 模型;bge 系列在中文场景表现优异 |
| 构建知识库 | 存储 chunk + 向量 + 元数据 | fields = [ FieldSchema("id", DataType.INT64, is_primary=True, auto_id=True), FieldSchema("content", DataType.VARCHAR, max_length=2048), FieldSchema("source", DataType.VARCHAR, max_length=256), FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=1024)]kb_col = Collection("rag_kb", CollectionSchema(fields, enable_dynamic_field=True)) | content 长度需 ≤ max_length;可启用 dynamic_field 存 page 等额外信息 |
| 查询重写(可选) | 将用户问题改写为检索友好形式 | 使用 LLM 生成关键词或假设答案 | 提升召回率,但增加延迟 |
| 向量检索 | 用问题向量搜索 top-k 相关 chunks | query_emb = model.encode([question]).tolist()results = kb_col.search(query_emb, "embedding", {"metric_type": "IP", "params": {"ef": 128}}, limit=5, output_fields=["content", "source"]) | limit 通常设为 3–5;output_fields 返回原文用于 prompt |
| 构造 Prompt | 将检索结果拼接进 LLM prompt | prompt = f"基于以下资料回答问题:\n{results[0].entity.content}\n\n问题:{question}" | 需控制总 token 数不超过 LLM 上限 |
| 动态更新知识库 | 支持新增/删除文档 | kb_col.delete(expr="source == 'old_doc.pdf'")kb_col.insert([...]) | 删除后需 flush 并等待 compaction 生效 |
8.4 语义搜索与问答系统
| 步骤名称 | 操作细节 | 代码示例 | 注意事项 |
|---|---|---|---|
| 构建 QA 对库 | 存储 (question, answer) 对及其向量 | fields = [ FieldSchema("id", DataType.INT64, is_primary=True), FieldSchema("question", DataType.VARCHAR, max_length=512), FieldSchema("answer", DataType.VARCHAR, max_length=2048), FieldSchema("q_emb", DataType.FLOAT_VECTOR, dim=768)]qa_col = Collection("faq_db", CollectionSchema(fields)) | question 用于检索,answer 用于返回 |
| 问题向量化 | 对用户输入问题编码 | q_emb = encoder.encode([user_question]).tolist() | 使用与入库相同的 encoder 模型 |
| 语义匹配搜索 | 检索最相似的历史问题 | hits = qa_col.search(q_emb, "q_emb", {"metric_type": "COSINE", "params": {"ef": 100}}, limit=1) | COSINE 要求向量 L2 归一化;若未归一化,改用 IP |
| 置信度过滤 | 判断是否命中有效答案 | if hits[0][0].distance > 0.7: # cosine similarity answer = hits[0][0].entity.answerelse: answer = "未找到相关答案" | 阈值需根据数据分布校准;避免低质量匹配 |
| 支持多轮上下文 | 将对话历史拼接进当前问题 | full_query = f"{history} {current_question}" | 需控制长度;可使用 sliding window |
| 标量增强 | 添加领域标签、时效性字段 | expr = "domain == 'finance' and valid_until >= 20260203"hits = qa_col.search(..., expr=expr, ...) | 提高答案相关性;避免返回过期政策 |
| 性能优化 | 预加载高频 QA 集合 | qa_col.load() at startup | 确保首次查询无冷启动延迟 |