从关键词到语义检索:Deep Optica矿产勘探场景的 Milvus实践分享
很多团队第一次做语义检索,会先拿一批资料做个 Demo:抽取文本、切 chunk、生成 Embedding、写进 Milvus,再输入几个问题看 Top-K。
我们团队(Deep Optica)也是这样。但真正接到生产以后,问题就会从“能否找到相似内容”升级成:
数据发生变化后,向量索引能否及时、正确地同步?
一份资料被拆成多个片段后,分页和排序是否仍然符合用户直觉?
模型、向量维度或距离度量改变时,旧索引如何平滑迁移?
向量服务变慢或不可用时,主业务是否会被拖垮?
检索结果如何遵守状态、范围和权限约束?
一次“搜不到”究竟是模型问题、过滤问题,还是数据尚未进入索引?
接下来,本篇文章会深度复盘Deep Optica 的语义检索能力从验证阶段进入生产环境的过程展开,解读:为什么最终选择 Milvus,以及选型之后,围绕索引同步、结果组织、模型升级、故障隔离和可观测性,我们最后形成了怎样的工程方案。
01
我们为什么要在关系数据库基础上增加向量检索?
Deep Optica 是一家专注于矿产勘探智能化与数据工程的科技公司。公司原有的地质勘探数据库已经具备结构化检索能力。用户可以按名称、类型、时间和其他元数据过滤资料,这部分由关系数据库完成。
但同一个地质概念可能存在缩写、同义表达、跨语言表述或上下位关系;一份长资料的标题可能很宽泛,但真正相关的信息只出现在正文某个段落里。传统关键词检索,通常无法覆盖以下几种情况:
表达不一致。用户描述的是意图,资料记录的是另一种写法,两者没有足够的字面重合。
长文本稀释主题。把整份资料当作一个检索单元,局部细节很容易被整体语义淹没。
结构化条件与语义相关性难以兼顾。只做关键词匹配会漏召回,只做向量相似度又可能返回状态或范围不符合要求的内容。
对这种情况,我们引入了语义检索链路:对资料进行文本抽取和切分,再生成 Embedding 写入向量数据库。
02
为什么最终选择 Milvus,而不是把向量继续留在原有系统里?
做语义检索数据库的选型,我们没有直接比较单次查询耗时,而是先列出系统进入生产环境后必须具备的能力:
能够私有化部署,数据边界清晰;
支持向量检索与标量过滤组合;
具备明确的集合、Schema、索引和数据生命周期;
能通过成熟 SDK 接入现有服务;
支持按场景选择一致性策略;
能够在模型升级时重建、校验和切换索引;
有持续维护的开源生态和完整文档;
随着数据量与并发增长,仍保留独立扩展和调优空间。
我们重点评估了三类方案:
其中,进程内索引适合验证,但数据量和实例数增加以后,索引如何持久化、多实例如何共享、数据如何分片、节点故障后如何恢复,都会逐渐变成应用团队自己的基础设施工作。
关系数据库向量扩展则可以继续沿用原有技术栈,也不用额外维护一套存储。但 Deep Optica 原有的关系数据库已经承担业务事务,向量索引则有不同的索引体积、内存占用、重建方式和查询负载。随着数据规模和并发增长,两类 workload 长期共享一套资源,会限制后续独立扩容和调优。
向量数据库方面,我们最终选择了 Milvus。
因为它不仅符合前面列出的所有需求,还把向量检索领域最重的一部分复杂度收敛到了清晰的基础设施边界内。应用层仍然要负责业务一致性、权限和结果组织,但不再需要从零实现向量索引的持久化、检索执行与扩展机制。同时,Milvus 的集合与 Schema 能承载稳定的数据契约,标量过滤可以把部分业务约束前推到召回阶段,SDK 便于接入现有服务,集合级别的管理能力也为后续的影子索引迁移留下了空间。
落地后的整体结构如下:
关系数据库保存业务事实,包括对象状态、元数据和权限;
Milvus 保存由业务数据派生出来的语义索引;
应用服务和后台任务负责两者之间的索引生成、同步、聚合与校验。
flowchart LR
A[原始资料] --> B[文本抽取与切分]
B --> C[Embedding 服务]
C --> D[(Milvus 语义索引)]
A --> E[(关系数据库)]
Q[用户查询] --> F[查询编排]
F --> C
F --> D
F --> E
D --> G[对象级聚合与排序]
E --> G
G --> H[结果与可解释信息]
关系数据库是事实源,Milvus 是派生索引。这样后续无论是 Embedding 模型更新、切分规则变化,还是归一化方式与距离度量发生改变,索引重建都不会影响原始业务数据的完整性。
03
上线过程中最棘手的六个问题
1. Embedding 不只是一个模型,也是一份需要版本化的检索协议
原型阶段最容易被忽略的问题,是写入和查询虽然调用了同一个模型,却未必遵守同一套向量协议。
如果文档侧修改了输入模板,查询侧没有同步;或者模型升级以后仍然沿用旧 collection,只要向量维度没变,查询仍然能够运行,但相关性可能已经发生变化。
因此,我们会把以下信息统一版本化:
模型及其版本;
向量维度;
是否归一化;
文档侧与查询侧的输入模板;
文本截断与切分规则;
距离度量;
协议版本。
我们把这一整套配置称为向量协议,并将其当作集合元数据的一部分。服务启动、批量写入和在线查询都要校验协议;只要一次修改会改变向量空间,新旧数据就不能继续混用,直接进入一次新的索引迁移。
2. 一份资料拆成多个 chunk 后,业务对象与向量记录不是一一对应
对长资料只生成一个向量,实现简单,但局部信息的召回效果通常不理想。
更合理的方式是同时保留对象级摘要向量和片段级向量,让标题、摘要与正文细节都有机会命中。
这样可以改善局部内容召回,但也带来了一个新的问题:数据库返回的是向量记录,但用户需要的却是业务对象。
假设一份报告有 6 个 chunk 同时进入 Top-20,那么 20 条向量结果可能只对应十几份实际资料。
如果直接展示,用户会反复看到同一份资料;如果先取固定 Top-K 再去重,一页可能不足额;如果继续按向量记录本身做分页,分页语义也不再等同于第几份业务资料。
我们当时的处理方式是在应用层增加对象级聚合:
从 Milvus 多召回一部分候选向量;
按业务对象 ID 分组;
使用组内最佳分数,或者经过验证的融合策略,计算对象级得分;
保留实际命中的 chunk,用于摘要和命中解释;
再进行对象级排序、去重和分页。
这里没有固定的超采样倍数。不同资料的 chunk 数量不同,Top-K 中的重复比例也会随数据分布变化,需要通过离线样本和线上结果持续校准。
不过,这部分也是 Milvus 后续变化比较大的地方。
Milvus 2.6.4 引入 Struct in ARRAY,使一个文档 entity 可以原生包含多个带标量字段和向量字段的 chunk;2.6.18 又增加了 StructArray 的 element-level vector search。当前 StructArray 可以在父 entity 内保存多个 chunk,对 chunk 向量直接搜索,并结合 primary-key grouping 将多个 element hit 折叠回父文档。Milvus 3.0 又进一步扩展了 StructArray 的 group-by、hybrid search 和 per-entity collapse 等能力。
如果今天重新设计这一部分,我们可能会优先评估 StructArray,把“一份文档包含多个 chunk”的关系直接交给 Milvus 建模,并利用 element-level search 与 grouping 减少应用层的去重和过采样逻辑。
应用层仍然保留自己的 ranking 逻辑——例如业务需要特殊的多 chunk 融合评分——但“多个 chunk 如何归属于同一个业务对象”已经不再必须完全由应用层模拟。
3. 双存储无法依靠“同时写入”获得强事务
引入 Milvus 后,一次资料更新通常涉及两套存储:
关系数据库更新业务事实;
Milvus 删除旧向量并写入新向量。
二者之间没有天然的跨存储事务。如果应用简单地顺序调用,任何一次超时、重试或进程退出都可能留下半完成状态,例如数据库已更新但向量仍是旧版本,或者对象已经删除但索引中还存在残留向量。
我们的处理方式是“事实先行、索引补偿”。每个需要进入语义索引的业务对象都会记录:
当前业务版本;
预期索引版本;
实际索引状态;
最近一次错误;
重试次数与更新时间。
业务事务提交后,对象进入待索引状态。后台任务再执行文本处理、Embedding 和 Milvus 写入,全部完成后推进索引版本。任务失败时会区分可重试和不可重试错误,避免同一种错误无限循环。
删除同样需要显式建模。业务对象已经删除,但 Milvus 中仍然存在对应向量,这个对象的索引生命周期仍然没有结束。因此,系统会定期检查缺失索引、孤儿向量和版本不一致,避免偶发故障长期积累成数据漂移。
这套机制牺牲了毫秒级的跨存储强一致性,换来了可恢复、可审计的最终一致性。对语义索引这种可重建的派生数据而言,这是更实际的取舍。
4. 过滤条件属于召回正确性,而不只是展示逻辑
语义相似不代表结果可以返回。资料的可见范围、生命周期状态和其他业务约束,必须参与检索。
一种常见错误是先从 Milvus 取固定数量的 Top-K,再在应用层过滤无效对象。这样做虽然简单,却可能让过滤后的结果严重不足;更重要的是,如果把权限过滤完全留到召回之后,系统的安全边界会变得脆弱。
我们的原则是:
能够安全表达为标量条件的约束,尽量在 Milvus 检索阶段过滤;
复杂或实时变化的权限,仍以事实源为准进行二次校验;
返回结果前必须执行最终授权判断;
过滤前候选数、过滤后结果数和拒绝原因都要进入观测。
标量过滤不是为了替代业务授权,而是为了减少无效召回;最终权限判断仍然不能依赖向量数据库中的派生字段。
5. 延迟预算必须覆盖整条链路
向量查询本身很快,并不意味着语义检索接口一定稳定。一次请求通常还包括输入处理、查询向量生成、Milvus 检索、事实源回查、对象聚合与结果组装。
如果每个步骤都设置独立且宽松的超时,最坏情况下总耗时会不断叠加。高并发时,Embedding 推理还可能占满共享线程池,进一步拖慢与检索无关的接口。
上线前我们做了三项改造:
使用统一截止时间。请求进入系统时生成总预算,每个下游调用只能使用剩余时间。
隔离并发资源。Embedding 这类计算任务使用独立的并发控制,避免挤占通用请求资源。
采用小窗口合批。在不明显增加排队时间的前提下合并推理请求,提高吞吐并限制队列长度。
同时,取消信号要能够向下传播。调用方已经超时的请求,不应继续占用推理或检索资源。经过这些处理后,延迟不再只是一个平均值,而成为可以分段定位、可以主动保护的系统预算。
6. 模型升级不能原地覆盖索引
模型、维度、归一化方式、距离度量或切分策略发生变化时,最危险的操作是直接在现有集合上重建数据。迁移过程中一旦失败,线上检索可能同时面对新旧数据,回滚也缺少明确边界。
我们采用影子集合迁移:
根据新向量协议创建独立集合;
从事实源分批回填;
记录检查点,使任务可以断点续跑;
校验数量、覆盖率、协议和抽样检索结果;
先小流量验证,再切换活动集合;
保留旧集合一段观察期,确认稳定后再清理。
切换动作只改变“当前活动集合”的指向,不修改业务数据。出现异常时,可以快速切回旧集合,而不必等待再次全量构建。
这套机制让模型升级从一次高风险操作,变成了一个有状态、有校验、有回滚路径的标准流程。
04
Milvus 带来了哪些实际收益,我们又如何衡量?
1. 用户从“猜关键词”转向“描述意图”
最直接的变化是,用户不再必须复现资料中的原始措辞。缩写、近义表达、跨语言描述和局部细节,都可以通过语义邻近进入候选集。
片段级召回还改善了长资料的可发现性:即使标题没有出现查询词,只要正文片段表达了相关含义,资料仍有机会被找到。命中片段随结果返回后,用户也能理解“为什么搜到它”,降低语义检索的黑盒感。
2. 检索能力与业务事实解耦
关系数据库继续承担事实与事务,Milvus 专注于语义召回。两者通过稳定标识和索引登记关联后,索引可以删除、重建和迁移,而不会反向污染业务数据。
这种边界也降低了后续演进成本。调整切分策略、升级模型或新增向量表示时,不需要重写业务数据模型,只需要按照新协议构建新的派生索引。
3. 故障从“偶尔搜不到”变成可定位状态
在引入索引状态、版本和分段指标之前,“搜不到”很难定位。现在可以区分:
原始资料尚未进入索引;
索引任务失败或正在重试;
查询被标量条件过滤;
事实源二次校验拒绝返回;
Embedding 或 Milvus 调用超时;
当前集合与向量协议不匹配。
这不仅缩短了排障路径,也让数据新鲜度和一致性可以被持续监控,而不是依靠用户反馈发现问题。
4. 升级与回滚变得可控
影子集合把模型升级和线上流量解耦。新索引可以在不影响当前服务的情况下构建和验证,切换后仍保留明确的回退路径。
对团队而言,最大的收益不是“以后不再出错”,而是错误不再不可逆。模型试验、索引调整和容量变化因此可以更频繁地进行。
5. 应用团队减少了自建基础设施工作
选择 Milvus 后,团队可以把精力放在真正具有业务差异的部分:文本如何切分、结果如何聚合、权限如何执行、质量如何评估,而不必自行维护底层向量索引的持久化、加载和扩展逻辑。
此外,随着 Milvus 增加StructArray、element-level search 等能力,一部分原本需要应用层实现的数据建模和结果整理逻辑,也可以继续下沉到检索系统中。
但衡量这些收益的价值,不能只看几个搜索案例是否感觉更准。上线后,我们把质量、服务和数据健康度分开度量:
离线评测集用于比较模型、切分和聚合策略,线上指标用于发现真实流量中的长尾问题。两者缺一不可:离线结果好不代表线上链路稳定,线上点击高也不一定代表召回本身准确。
结语
Deep Optica 最初引入 Milvus,是为了补足关系数据库不擅长的语义召回。但真正进入生产以后,我们花时间最多的,却不是把向量搜得更快,而是:业务数据变了,索引怎么及时跟上;一份资料拆成多个 chunk 后,怎么重新组织成用户真正看到的结果;状态、范围和权限怎么参与检索;模型升级以后,如何安全迁移?
为此,我们也把一致性、迁移、延迟和可观测性设计成了一等能力。
但它们也不会永远由应用层自己解决。我们早期需要自己处理的一些问题,随着 Milvus 增加 Grouping Search、StructArray 等能力,已经可以逐步下沉到数据库。以后还会有更多类似的变化。
不过无论底层能力怎么演进,有一点不会变:只有索引同步、结果组织、权限、迁移和可观测性这些基础工作真正做扎实,语义检索的价值才能持续体现出来。
用户才能更自然地表达自己想找什么,长资料里的局部信息和过去容易漏掉的内容也更容易被发现;工程团队得到的,也不再只是一个能跑 Top-K 的 Demo,而是一套可以长期可运行、可重建、可解释、可迁移、可升级的检索系统。
关于 Deep Optica
Deep Optica 是一家专注于矿产勘探智能化与数据工程的科技公司,致力于构建面向矿业的“世界模型”(Mining World Model)。公司以人工智能框架融合地质、地球物理、地球化学、遥感和钻探等多源数据,构建具备地质约束、可解释并能表达不确定性的二维找矿远景图与三维矿体模型,为勘探决策提供科学依据。
依托科学文献、公共地质调查和历史勘探档案,Deep Optica 已构建决策级数据库,覆盖从绿地找矿到棕地扩展的勘探流程,提供靶区圈定与排序、三维建模、数据工程、证据链分析及钻探优化等服务。相关方案已在澳大利亚、中东及北非、蒙古和非洲等地区验证,服务铜、金、锂、稀土、镍铜及金刚石等矿种,帮助客户降低成本、量化风险、提高钻探成功率,优先锁定高潜力目标。







