Milvus:向量检索与运维¶
Milvus 是向量数据库的一个具体实现。它负责从大量向量中找出与问题最相近的候选片段,并按元数据条件缩小范围;它不负责切分文档、生成 Embedding、重排结果,也不负责让大模型组织最终答案。
原始文档 → 清洗与切片 → Embedding 模型 → 向量 + 元数据写入 Milvus
用户问题 → 同一 Embedding 模型 → Milvus 过滤检索 → 候选片段 → Reranker → LLM 回答
它在知识系统中解决什么问题¶
当文档被切成几十万甚至更多片段后,系统需要在可接受的时间内找出语义相近、权限允许、且属于当前场景的内容。Milvus 的职责就是这一段“候选召回”。
例如用户问“某站点链路中断后如何排查”:查询向量表达问题语义;site_id、设备厂商、文档类型和权限范围作为过滤条件;Milvus 返回最可能有关的 20~50 个片段。之后再由重排模型挑出最可靠的 3~8 段,交给 LLM 基于证据作答。
因此,Milvus 检索结果不好时,问题不一定出在数据库:也可能是切片切坏了、Embedding 模型不适合、元数据缺失、过滤条件错误,或没有重排。
核心对象:一张可检索的知识片段表¶
Milvus 中最重要的对象是 Collection,可以理解为一张面向向量检索的数据表。它需要先定义 Schema;每条记录至少包含一个主键、一个向量字段,以及支撑过滤与回溯的标量字段。
| 对象 | 作用 | 知识库中的建议 |
|---|---|---|
| Collection | 一类可共同检索的数据集合 | 按业务域或数据生命周期拆分,例如 ops_knowledge_chunks |
| Primary Key | 唯一标识一条记录 | 使用稳定的 chunk_id,不要用会随重新切片变化的序号 |
| Vector Field | 保存 Embedding,用于近邻搜索 | embedding;维度必须与所用模型完全一致 |
| Scalar Fields | 字符串、数字、布尔等元数据 | 文档、章节、站点、设备、权限、更新时间、模型版本 |
| Index | 加速近似最近邻检索 | 依据规模、内存和召回目标选择 HNSW、IVF 等 |
| Partition | 物理或逻辑分组手段 | 只在有明确隔离或生命周期需求时使用,不能替代通用元数据过滤 |
一个实用的知识片段 Schema 可以从下面这些字段开始:
| 字段 | 示例 | 为什么需要 |
|---|---|---|
chunk_id |
linux-ssh-001-03 |
回写、删除和去重的稳定主键 |
embedding |
[0.012, -0.083, ...] |
片段的语义表示 |
document_id |
linux-ssh-guide-v2 |
将多个片段归回同一份文档 |
title / section_path |
SSH 加固 / 权限 > SSH |
结果展示、引用与上下文拼接 |
source_url |
文档来源地址 | 让答案可以追溯到原文 |
site_id / device_vendor |
sh-a01 / huawei |
让检索贴合现场对象 |
permission_scope |
ops-team-a |
防止跨团队或跨客户召回 |
updated_at |
时间戳 | 支持优先使用较新的内容 |
embedding_model_version |
bge-m3-2026-01 |
模型升级时区分旧、新向量 |
text 正文可以一并保存,方便直接取回;也可以只保存内容定位信息,把全文放在对象存储或原始文档库中。无论采用哪种方式,检索结果都必须能稳定取得原文与来源。
写入链路:先把数据准备对¶
- 确定边界与权限:明确哪些文档能进入知识库,给每份文档补齐来源、业务域、站点和权限等元数据。
- 清洗、切片、保留上下文:按语义段落切片,并保留标题路径、章节、前后片段关联。只把孤立的一小段文字塞进向量库,往往会造成“召回到了但看不懂”。
- 统一生成向量:同一 Collection 内的向量必须使用兼容的模型、维度和相似度度量。切换模型时应做版本隔离或全量重建,不能新旧向量混写。
- 写入向量和标量字段:写入时同时保存文本定位与过滤字段,不能只存
id + embedding。 - 建索引并加载服务:数据写入后建立索引,再将可检索数据加载到查询服务;用一组真实问题验证结果后才接入应用。
查询链路:Milvus 只负责候选召回¶
问题
→ 查询改写(可选)
→ Query Embedding
→ Milvus:权限 / 站点 / 文档状态等元数据过滤 + Top K 近邻搜索
→ Reranker:从候选中重新排序
→ LLM:带引用生成答案,无法支持时明确说明
先做标量过滤,再在符合条件的数据中进行近邻搜索,能减少无关候选,也避免把本不应出现的数据送给后续模型。过滤条件必须来自可信的登录身份或业务上下文,不能只相信用户在问题中写的“我是某某团队”。
Top K 不是越大越好。过小会漏召回;过大则会增加重排成本并给 LLM 带入噪声。可以先以 20~50 作为候选数、以 3~8 作为最终上下文的起点,再通过题库评测调整。
最小可用的召回代码¶
下面的代码刻意把 Embedding 生成留在 Milvus 外部:embed_query() 应调用你统一选定的 Embedding 模型,且返回维度必须等于 Collection 的维度。site_id 与 permission_scope 应由后端根据登录身份写入,不能让前端任意传入。
from pymilvus import MilvusClient
client = MilvusClient(
uri="http://milvus.internal:19530",
token="${MILVUS_TOKEN}",
)
question = "华为交换机配置 OSPF 后邻居没有建立,如何排查?"
query_vector = embed_query(question) # 例如返回 1024 个 float
results = client.search(
collection_name="ops_knowledge_chunks",
data=[query_vector],
filter="site_id == 'sh-a01' and permission_scope in ['netops']",
limit=30, # Milvus 的候选召回数,不是最终喂给 LLM 的段数
output_fields=["chunk_id", "text", "title", "section_path", "source_url"],
)
candidates = results[0]
top_context = rerank(question, candidates)[:5]
answer = llm_answer(question, top_context)
search() 返回的是距离/相似度和指定的 output_fields。应用服务应记录问题、过滤条件、候选 ID、分数、重排后结果与最终引用;这样出现错误答案时,才能判定是“没召回”还是“召回后没用好”。
召回效果应该怎样验收¶
准备 30~100 个真实问题,每题标记至少一段正确证据。对每次模型、切片或索引变更,比较下面三层,而不是只看一个相似度分数:
| 层次 | 要问的问题 | 例子 |
|---|---|---|
| Milvus 候选层 | 正确片段是否进入 Top 30? | Top-30 中有正确 OSPF 故障处理步骤 |
| 重排层 | 正确片段是否进入 Top 5? | 设备厂商、场景最贴近的步骤排到前面 |
| 回答层 | LLM 是否依据证据回答并给出引用? | 不把其他厂商命令混入答案;无证据时拒答 |
候选层漏了正确内容,优先查切片、Embedding、过滤和 Top K;候选层命中了但答案错,才重点查 Reranker、Prompt 和 LLM。
索引与调优:先看质量,再看参数¶
HNSW 常用于低延迟、高召回的在线检索,但会占用更多内存。三个容易遇到的参数是:
| 参数 | 主要影响 | 调整方向 |
|---|---|---|
M |
图连接数、召回与内存 | 提高通常有利于召回,但会增加内存与构建成本 |
efConstruction |
建索引时的搜索深度 | 提高通常改善索引质量,但建索引更慢 |
ef |
查询时的候选探索量 | 提高通常改善召回,但延迟会上升 |
调优顺序建议如下:先固定切片规则、Embedding 模型、度量方式和评测集;再确认是否需要元数据过滤;最后才用同一批问题对比 Top K、索引与查询参数。直接调 ef,无法修复“切片没有保留故障现象”或“文档版本已经过期”这类数据问题。
部署:开发能跑与生产可用不是一回事¶
1. 单机验证:Docker Compose¶
适合个人学习、开发联调和小规模 PoC。先创建专用目录,再下载与当前 Milvus 版本匹配的官方 Compose 文件:
mkdir -p /opt/milvus-standalone
cd /opt/milvus-standalone
wget https://github.com/milvus-io/milvus/releases/download/v3.0.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
docker compose up -d
docker compose ps
官方 Standalone Compose 会启动 milvus-standalone、milvus-etcd 与 milvus-minio:Milvus 对外提供 gRPC/API 端口 19530,etcd 保存元数据,MinIO 保存对象数据。确认容器健康后,可在服务器本机打开 http://127.0.0.1:9091/webui/ 查看状态。
不要为了方便把 19530、MinIO 管理端口或 WebUI 直接暴露到公网;应用与 Milvus 应处于同一内网或容器网络,通过安全组、防火墙和认证限制访问。Compose 文件里的 volumes/ 保存持久化数据,升级、迁移或清理前必须先备份;docker compose down 只停服务,删除 volumes/ 才会删除数据。
安装 Python 客户端并做连接验证:
python3 -m pip install -U pymilvus
python3 - <<'PY'
from pymilvus import MilvusClient
client = MilvusClient(uri="http://127.0.0.1:19530", token="root:Milvus")
print(client.list_collections())
PY
root:Milvus 是官方示例的初始凭据,只能用于本地验证;接入任何共享环境前应建立专用应用账户或令牌、改掉默认凭据,并将密钥放入 Secret/环境变量,而不是 Markdown、代码仓库或镜像中。
2. Kubernetes:生产集群的起点¶
适合需要高可用、容量扩展或多个应用共享检索服务的场景。前置条件是 Kubernetes、Helm 和可用的 StorageClass;生产环境还应先确定持久卷类别、对象存储、备份位置、网络策略和资源配额。
helm repo add zilliztech https://zilliztech.github.io/milvus-helm/
helm repo update
helm install knowledge-milvus zilliztech/milvus \
--namespace milvus --create-namespace \
--set image.all.tag=v3.0.0 \
--set woodpecker.enabled=true \
--set woodpecker.image.tag=v0.1.37 \
--set streaming.enabled=true \
--set streaming.woodpecker.embedded=false \
--set indexNode.enabled=false
kubectl get pods -n milvus
上面的命令用于建立当前官方推荐架构的基线,不是生产最终配置。正式上线应把所有覆盖项写入受版本控制的 values.yaml,至少明确 PVC 的 StorageClass/容量、对象存储、资源 requests/limits、认证与 TLS、网络策略、监控、备份和版本固定。不要用 latest 标签,也不要在没有快照和回滚方案时直接升级 Chart。
3. 部署后的验收顺序¶
- 容器或 Pod 全部就绪,日志没有反复重启、磁盘或对象存储报错。
- 应用网络能连通
19530,但未授权网络不能连通。 - 用一个已知维度的 Embedding 创建 Collection,写入 3~10 条测试片段。
- 用同一模型生成查询向量,验证 Top K 能返回预期片段和所需
output_fields。 - 再测试权限过滤、文档更新删除、备份恢复与节点重启,而不是只测试“服务端口已监听”。
| 场景 | 建议 | 关注点 |
|---|---|---|
| 本地学习、功能验证 | 使用单机或 Standalone | 方便验证 Schema、写入和检索闭环,数据可随时重建 |
| 小规模内部知识库 | 独立服务部署,做好持久化与备份 | 容量、内存、索引构建时间、权限字段 |
| 生产级检索服务 | 按当前发行版的集群架构部署 | 元数据存储、对象存储、查询与数据组件、监控、扩缩容与故障恢复 |
Milvus 的组件会随发行版与部署方式变化。生产部署应以当前官方 Helm Chart 或运维文档为准,不要直接照搬旧版本教程中的组件名和端口。无论哪种方式,都要把数据目录、对象存储、元数据和访问凭据纳入备份与恢复演练。
日常运维与验收¶
上线前,不要只测“接口返回 200”。至少准备一组覆盖同义问法、缩写、站点限定、权限边界和无答案问题的评测集,并持续观察:
- 召回质量:Recall@K、重排后命中率、无依据回答比例。
- 性能:查询 P50/P95、过滤后的延迟、索引构建耗时、写入积压。
- 资源:查询节点内存、索引与原始向量占用、对象存储容量、加载状态。
- 数据健康度:向量维度、模型版本、重复片段、失效文档、缺失元数据。
- 安全性:过滤条件是否由服务端强制注入,日志中是否泄露正文或敏感检索词。
常见问题定位¶
| 现象 | 优先检查 |
|---|---|
| 写入失败或查询报维度错误 | Embedding 模型、向量维度、Collection Schema 是否一致 |
| 能搜到相似话题,却不是正确步骤 | 切片是否包含完整上下文;是否需要 Reranker;Top K 是否过小 |
| 跨站点、跨客户返回了内容 | site_id、permission_scope 等服务端过滤是否遗漏 |
| 更新文档后仍回答旧内容 | 旧 chunk 是否删除或标记失效;索引与缓存是否刷新;版本是否可追溯 |
| 延迟突然升高 | 过滤条件命中范围、ef、Top K、索引加载状态、并发与内存是否变化 |
| 召回越来越差 | 是否混入不同 Embedding 模型的向量;评测集是否已经失去代表性 |
何时选择 Milvus¶
如果向量检索会成为独立的长期服务,数据量、并发、过滤条件或运维要求逐渐增长,Milvus 是合适的候选。若只是少量文档和已有 PostgreSQL 系统,可先评估 pgvector;本地原型也可以用 Chroma 一类更轻的方案。关键不是产品名,而是能否把“数据质量、权限过滤、召回评测、可恢复运维”这条链路做好。