Agent 工程
Milvus 实操
用一套一致的集合名和真实文本 Embedding,跑通写入、检索与删除;代码假定本地 Milvus 已在 19530 端口运行。
先按 Milvus 官方 Docker Standalone 指南启动服务,再安装
pymilvus 与 sentence-transformers。示例使用公开文本模型并从实际输出推导维数,不用随机向量伪装语义效果。1. 连接、Embedding、Schema 与批量写入
from pymilvus import MilvusClient, DataType from sentence_transformers import SentenceTransformer COLLECTION = "support_kb" client = MilvusClient(uri="http://localhost:19530") print(client.list_collections()) # 先确认服务可达;健康检查常见于 9091/healthz RESET_LAB = False # 只有确认可丢弃旧练习数据时才改为 True if client.has_collection(collection_name=COLLECTION): if not RESET_LAB: raise RuntimeError("support_kb 已存在;请换名称,或确认后启用 RESET_LAB") client.drop_collection(collection_name=COLLECTION) # 删除整个集合 encoder = SentenceTransformer("BAAI/bge-m3") docs = [ {"id": 1, "text": "退款通常在三个工作日内到账", "category": "refund"}, {"id": 2, "text": "修改密码后需要重新登录", "category": "account"}, ] vectors = encoder.encode([d["text"] for d in docs], normalize_embeddings=True).tolist() dim = len(vectors[0]) schema = MilvusClient.create_schema(auto_id=False, enable_dynamic_field=False) schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True) schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=dim) schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000) schema.add_field(field_name="category", datatype=DataType.VARCHAR, max_length=64) client.create_collection(collection_name=COLLECTION, schema=schema) rows = [{**d, "vector": v} for d, v in zip(docs, vectors)] client.insert(collection_name=COLLECTION, data=rows) # 数据较多时分批写,并记录失败批次
2. 建索引、Load、Top-K Search 与 Filter
index = client.prepare_index_params() index.add_index(field_name="vector", index_type="HNSW", metric_type="COSINE", params={"M": 16, "efConstruction": 200}) client.create_index(collection_name=COLLECTION, index_params=index) client.load_collection(collection_name=COLLECTION) query_vector = encoder.encode(["退的钱多久能回来?"], normalize_embeddings=True).tolist() hits = client.search( collection_name=COLLECTION, data=query_vector, anns_field="vector", limit=3, filter='category == "refund"', output_fields=["text", "category"], search_params={"metric_type": "COSINE", "params": {"ef": 64}}, ) for hit in hits[0]: print(hit["id"], hit["distance"], hit["entity"]["text"])
3. Query 与 Delete
# Query 不做向量相似度计算 rows = client.query(collection_name=COLLECTION, filter='category == "account"', output_fields=["id", "text"]) client.delete(collection_name=COLLECTION, filter="id == 2") # ⚠️ 仅用于可丢弃的练习环境;drop 会删除整个集合 # client.drop_collection(collection_name=COLLECTION)
写入策略
批量 insert;使用稳定主键,并用 upsert 或业务去重实现幂等(insert 本身不会自动去重);保留模型名、模型版本与内容版本。更新内容时重新生成向量。
删除策略
需要可撤销删除时,可先用 active=false 做逻辑删除,并在搜索 filter 中排除;物理删除后空间回收依赖 Compaction,不会立刻缩小文件。
检查点 同一模型、同一维数、同一 COSINE metric、真实文本向量、先 load 后 search。先用 FLAT 做小规模正确性基线,再比较 HNSW 的 Recall 与延迟。