Harness 核心 · 模型周围的 Harness

Milvus 实操

连接、建表、批量写入、索引、搜索、查询和删除

本页解决的问题

先给结论

「Milvus 实操」要解决的关键问题是什么?

连接、建表、批量写入、索引、搜索、查询和删除

判断标准

让这个结论先证明自己值得留下。 把这一页当成决策工具,而不是需要背下来的定义。把概念连到一个真实任务、一个可观察结果,以及一个能改变你判断的失败上。

下一步

写下一个问题:试完这个方法后,你能用什么证据回答它?

常见误区

结论听起来很完整,却没有检查最关键的假设。

先按 Milvus 官方 Docker Standalone 指南启动服务,再安装 pymilvussentence-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 与延迟。

「1. 连接、Embedding、Schema 与批量写入」为什么能找到相关内容

「批量 insert;使用稳定主键,并用 upsert 或业务去重实现幂等(insert 本身不会自动去重);保留模型名、模型版本与内容版本。更新内容时重新生成向量」把检索问题从“把资料存起来”推进到“怎样找到真正相关的资料”。这一步决定了 RAG、推荐和以图搜图最后交给模型的输入质量。

相似度不是答案,召回之后还要核对

在「需要可撤销删除时,可先用 active=false 做逻辑删除,并在搜索 filter 中排除;物理删除后空间回收依赖 Compaction,不会立刻缩小文件」对应的流程里,Embedding 负责把对象放到可比较的语义空间,近邻索引负责减少搜索范围,最终的回答仍然依赖召回片段是否覆盖问题、距离指标是否合适,以及内容有没有过期。

先区分找得到和找得准

把「需要可撤销删除时,可先用 active=false 做逻辑删除,并在搜索 filter 中排除;物理删除后空间回收依赖 Compaction,不会立刻缩小文件」落成一次小测试:准备几条有明确答案的查询,记录召回的相关性、遗漏和无关结果,再决定是否需要换切分方式、索引或重排。

从「1. 连接、Embedding、Schema 与批量写入」走到「2. 建索引、Load、Top-K Search 与 Filter」

「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())…」上;到了「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=COLLECTI…」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。

把这条判断带到下一个场景

检索系统也可以这样看:先确定什么算相关,再观察召回是否覆盖问题,最后检查排序、切分和时效性有没有把真正有用的内容挤出去。

  • 「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())…
  • 「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=COLLECTI…
  • 「删除策略」:需要可撤销删除时,可先用 active=false 做逻辑删除,并在搜索 filter 中排除;物理删除后空间回收依赖 Compaction,不会立刻缩小文件

最后的「删除策略」把讨论落到「需要可撤销删除时,可先用 active=false 做逻辑删除,并在搜索 filter 中排除;物理删除后空间回收依赖 Compaction,不会立刻缩小文件」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。

标记为已学完 阅读进度会自动记录
← 上一篇下一篇 →

继续阅读

同一条线上的下一篇。

文章讨论

读到这里,留下一个判断。

把刚想明白的地方、还没想通的问题,留给下一位一起学习的人。

正在讨论 Milvus 实操 模型周围的 Harness
3条讨论文章讨论 · 与共学社区同步
在共学社区查看
AM
Asha Morgan内容编辑
观点实践记录

我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。

文章讨论7 有帮助
LH
Lin Harper独立开发者
观点观点

读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。

文章讨论5 有帮助
KM
Kiki Moore产品运营
问题问题

如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。

文章讨论4 有帮助