在构建具备长期记忆和私有知识库的AI智能体时,将Milvus向量数据库部署在本地是最直接保障数据隐私的方案。Milvus作为专为 embeddings 检索设计的向量引擎,支持十亿级向量的近似最近邻搜索,而Agent通过调用其接口可以完成文档召回、语义记忆读取等任务。下面从环境准备到代码对接逐步说明落地方式。

本地Docker Compose部署Milvus单机版
Milvus官方提供了standalone版本的docker-compose.yml,适合开发和个人使用。部署前需确认机器已安装Docker与Docker Compose,并且可用内存不低于4GB,因为etcd、minio和milvus核心服务都会占用固定资源。很多部署失败的原因在于未修改默认卷映射路径,导致容器重启后向量数据丢失。
我们应在compose文件中显式声明本地目录挂载。例如将./milvus-data/minio挂到minio容器,./milvus-data/etcd挂到etcd容器,这样即使删除容器,已写入的向量段文件依然保留。同时要注意19530端口是gRPC客户端端口,9091是指标端口,若宿主机已有服务占用需提前更改。
启动命令非常简单,在含有yml的目录执行docker compose up -d即可。等待几十秒后,用docker ps查看milvus-standalone状态为healthy就代表就绪。此时本机可以通过127.0.0.1:19530被Python客户端直连,无需任何公网穿透。
version: '3.5'
services:
etcd:
image: quay.io/coreos/etcd:v3.5.14
volumes:
- ./milvus-data/etcd:/etcd
command: etcd -data-dir /etcd
minio:
image: minio/minio:RELEASE.2024-05-28
volumes:
- ./milvus-data/minio:/minio_data
command: minio server /minio_data
milvus:
image: milvusdb/milvus:v2.4.5
command: milvus run standalone
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
ports:
- "19530:19530"
volumes:
- ./milvus-data/milvus:/var/lib/milvus
使用pymilvus完成集合创建与向量写入
Agent要检索知识,第一步是在Milvus里建好集合(Collection)并插入文本embedding。pymilvus是官方Python SDK,提供了与数据库交互的全部方法。建集合时要明确向量维度,这个维度必须和你的Embedding模型输出一致,比如用bge-small模型就是512维,用text-embedding-3-small则是1536维,写错会在插入时报错。
下面示例创建了一个叫agent_knowledge的表,主键为自增id,向量字段名为embedding,并额外用动态schema存了原文内容。我们为向量字段指定了COSINE相似度类型,适合自然语言语义匹配。索引用HNSW,查询快但占内存,本地部署通常可接受。
插入数据使用insert方法,传入的向量列表和文本列表要一一对应。Milvus写入是异步落盘的,调用后最好用flush确保数据可见。之后Agent的每次提问只要把问题向量化,就能用search取回最相近的几条知识。
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://127.0.0.1:19530")
schema = MilvusClient.create_schema(auto_id=True, enable_dynamic_field=True)
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=512)
index_params = client.prepare_index_params()
index_params.add_index(field_name="embedding", metric_type="COSINE", index_type="HNSW")
client.create_collection(collection_name="agent_knowledge", schema=schema, index_params=index_params)
# 假设embed_func将文本转成512维向量
docs = ["Milvus是向量数据库", "Agent可调用工具"]
vectors = [embed_func(d) for d in docs]
data = [{"embedding": v, "content": d} for v, d in zip(vectors, docs)]
client.insert(collection_name="agent_knowledge", data=data)
client.flush(collection_name="agent_knowledge")
将Milvus检索封装为Agent可调用的工具
让AI智能体真正用上本地Milvus,关键不在模型本身,而在工具函数的定义。主流Agent框架(如LangChain、LlamaIndex或自研循环)都支持把任意Python函数注册为tool。我们应把上面的查询逻辑包成一个接受自然语言问题、返回相关文本的函数,而不是让模型自己拼连接串。
以下代码展示了检索工具的写法。函数内部完成问题向量化、Milvus搜索、结果提取,并返回拼接好的字符串。通过装饰器或框架接口暴露给Agent后,模型在系统提示里会看到这个工具说明,当用户问私有知识时便自动发起调用,整个过程数据库地址和密码都不出现在对话上下文,安全性更好。
实际对接时要注意给工具起清晰的名字,比如search_local_knowledge,描述里写清适用场景。若Agent运行在另一个Docker网络,要把Milvus的19530映射到Agent容器可达的地址,或者把两者放同一compose编排内。这样本地知识库问答的端到端延迟通常只有几十到一百多毫秒,比调用远端向量服务稳定得多。
def search_local_knowledge(question: str, top_k: int = 3) -> str:
q_vec = embed_func(question)
res = client.search(
collection_name="agent_knowledge",
data=[q_vec],
limit=top_k,
output_fields=["content"]
)
hits = [item["entity"]["content"] for item in res[0]]
return "n".join(hits)
# 在Agent框架中注册,伪代码示意
# agent.register_tool(name="search_local_knowledge", func=search_local_knowledge, desc="检索本地Milvus知识库")
常见故障与本地部署调优
本地跑Milvus对接Agent时,最常遇到的是连接拒绝和内存溢出。连接问题多因Milvus还未healthy就启动Agent,建议在部署脚本里加健康检查等待;内存溢出则是因为HNSW索引在千万级向量下很吃RAM,可改IVF_FLAT索引并调低nlist,牺牲一点召回速度换资源占用下降。
另一个容易被忽略的点是Embedding模型必须线上线下同款。如果写入知识库用模型A,Agent提问时用模型B,向量空间不一致,搜出来的内容会完全不相关。因此要把embed_func集中管理,最好写成独立模块供两端引用,并在Milvus集合的别名或备注里记录维度与模型名。
当Agent需要多轮记忆,也可把对话embedding存入另一集合,用用户id过滤。这种用法下本地Milvus比每次把历史全塞进提示词更省token,并且能跨会话保留偏好。只要硬盘够大,minio里的段文件可以一直累积,配合定时compact合并小段即可维持查询效率。