工业级 RAG 为什么需要 Elasticsearch?一篇讲清索引、度量、过滤与 Milvus 选型

过去提到 Elasticsearch,我首先想到的是搜索引擎、倒排索引、BM25,以及大家比较熟悉的 ELK 日志分析体系。

而提到 Milvus,我首先想到的则是向量数据库、Embedding、HNSW 和相似度检索。

因此,我们很容易形成一个简单的认识:

Elasticsearch 负责关键词检索,Milvus 负责向量检索。

但随着我进一步了解两者的能力,我发现这个结论已经不够准确了。

现在的 Elasticsearch 不仅可以做 BM25,还支持稠密向量、稀疏向量、HNSW、元数据过滤和混合检索;而 Milvus 也不再只是单纯存储稠密向量,它同样开始支持稀疏向量、BM25、标量过滤和混合召回。

这就引出了一个更值得讨论的问题:

既然 Elasticsearch 和 Milvus 都能完成关键词检索、向量检索和元数据过滤,那么工业级 RAG 到底应该使用谁?什么时候只使用 Elasticsearch,什么时候只使用 Milvus,什么时候才需要 Elasticsearch 加 Milvus?

在回答这个问题之前,我们首先要把几个经常混在一起的概念彻底拆开:

  • 索引结构
  • 搜索算法;
  • 相似度度量;
  • 元数据过滤;
  • 排序与重排。

只有先理解一次检索到底发生了什么,后面的技术选型才不会停留在产品名称层面。


一、一次完整的检索,到底发生了什么

很多人会把向量检索简单理解为:

把问题转换成向量,再去数据库里找到最相似的几个向量。

这个描述不能说错,但它省略了很多关键步骤。

一次完整的检索,实际上包含下面几个环节:

这里的每一个环节,解决的问题都不一样。

概念 主要解决的问题 常见实现
索引结构 数据提前如何组织 倒排索引、HNSW、IVF
搜索算法 查询时如何快速找到候选 图遍历、聚类探测、全量扫描
度量方式 两个向量如何判断远近 Cosine、IP、L2
元数据过滤 哪些数据有资格参与检索 租户、权限、知识库、状态
召回排序 候选结果如何计算初始得分 BM25、向量相似度
融合与精排 多路候选最终如何排序 RRF、BGE-Reranker

其中最容易混淆的,就是索引方式和相似度度量。


1.1 索引结构:数据提前怎么组织

索引的核心目的,不是直接判断哪一条数据最相似,而是:

提前组织数据,让查询时不需要检查全部数据。

假设数据库里有一百万条向量。

最简单的方法是,让查询向量分别和这一百万条向量计算一次相似度,然后再从中选出 Top 10。

这种方式当然能够找到准确结果,但是每次查询都需要完成一百万次向量比较。随着数据规模增加,查询延迟和计算成本都会线性上升。

所以,我们需要提前把向量组织起来。

例如 HNSW 会把相似的向量连接成一张多层近邻图;IVF 会把整个向量空间划分成多个聚类区域;倒排索引则会提前建立"词项到文档"的映射关系。

可以把索引理解成一张提前修建好的地图。

没有地图时,我们只能把整个城市的道路全部走一遍;有了地图之后,就可以先定位大致区域,再进入附近街道,最终找到目标位置。

因此,索引结构解决的是:

数据放在哪里,以及查询时应该先去哪里找。


1.2 搜索算法:沿着索引寻找候选

索引建立完成后,还需要配套的搜索方法。

以 HNSW 为例。

在建立索引时,HNSW 会把向量组织成多层图结构。上层节点少、跨度大,类似高速公路;下层节点多、连接密集,类似城市道路。

查询时,系统会:

  1. 从图的高层入口开始;
  2. 快速靠近查询向量所在的区域;
  3. 逐层向下;
  4. 在局部邻居中继续搜索;
  5. 返回一批可能相似的候选向量。

这意味着,HNSW 并不是单纯的距离公式。

更准确地说,它同时包含:

  • 建索引阶段的图结构构建;
  • 查询阶段的近似图搜索。

所以我们在工程实践中经常把 HNSW 称为"索引算法",但它解决的核心问题仍然是:

怎样用更少的比较次数,找到尽可能接近真实答案的候选集合。


1.3 度量方式:到底怎样才算相似

当索引算法找到一批候选以后,系统还要判断这些候选和查询向量到底有多相似。

这就需要相似度或者距离函数。

最常见的三种方式是:

  • Cosine:余弦相似度
  • IP:Inner Product,内积;
  • L2:欧氏距离。

可以把三者理解为三种不同的"距离定义"。

假设我要在城市中寻找一家最近的医院,那么"最近"可以有不同含义:

  • 直线距离最近;
  • 驾车距离最近;
  • 驾车时间最短。

它们面对的是同一批医院,但由于"距离"的定义不同,最后得到的结果可能不同。

向量检索也是如此。

Cosine:比较方向

余弦相似度更关注两个向量的方向是否一致,而不太关注向量本身的长度。

在文本 Embedding 中,我们通常更关心两段文本的语义方向是否接近,因此 Cosine 是非常常见的选择。

IP:计算内积

内积的计算方式是:

如果两个向量已经完成 L2 归一化,那么它们的模长都是 1,此时内积和余弦相似度的排序结果基本一致。

BGE-M3 的论文中,稠密检索使用归一化后的表示,并通过内积计算查询与文档的相关性。

L2:计算空间距离

L2 计算的是两个向量在空间中的欧氏距离:

L2 越小,代表两个向量越接近。

因此,我们可以用一句话总结索引和度量的区别:

HNSW、IVF 决定去哪里找;Cosine、IP、L2 决定找到的候选中谁更相似。


1.4 元数据过滤:谁有资格参加检索

工业级 RAG 中还有一个经常被忽略的环节,就是元数据过滤。

真实业务中的检索,通常不是:

从全部文档中找出与问题最相似的内容。

而是:

从当前租户、当前知识库、用户有权限访问、仍处于有效状态的文档中,找到与问题最相关的内容。

例如,一条知识库 Chunk 除了正文和向量,还可能包含:

  • tenant_id:属于哪个租户;
  • kb_id:属于哪个知识库;
  • department:属于哪个部门;
  • doc_type:文档类型;
  • status:文档是否有效;
  • acl_roles:哪些角色可以访问;
  • effective_date:生效时间;
  • expiry_date:失效时间。

用户查询时,真正的检索条件可能是:

元数据过滤解决的是:

哪些数据有资格参与后面的相关性竞争。

它既不是索引算法,也不是相似度计算方式。


1.5 排序、融合和重排

完成初步召回后,还需要决定候选的最终顺序。

例如:

  • BM25 返回一批关键词候选;
  • Dense Vector 返回一批语义候选;
  • Sparse Vector 返回一批学习型稀疏候选。

由于三路检索的分数范围不同,不能简单把它们的原始分数直接相加。

这时可以使用 RRF,也就是 Reciprocal Rank Fusion,根据不同召回通道中的排名进行融合。

Elasticsearch 当前的 Retriever API 可以组合关键词检索、稀疏检索和稠密向量检索,并通过 RRF 统一候选排名。

在 RRF 后面,还可以增加 BGE-Reranker,对查询和候选文本进行更精细的交叉计算。

因此,工业级 RAG 中常见的检索链路是:


二、从传统搜索重新理解 Elasticsearch

理解前面的基础概念以后,我们再回到 Elasticsearch。

Elasticsearch 最初的核心优势不是向量检索,而是全文搜索。

不过在讲全文搜索之前,还需要先解决一个术语问题:

Elasticsearch 中的"索引"到底指什么?


2.1 Elasticsearch 中至少有四种"索引"(重点、重点)

在 Elasticsearch 的语境里,"索引"这个词可能代表不同层次的概念。

第一种:Elasticsearch Index

例如:

  • rag_chunks_v1
  • product_search_v1
  • customer_service_logs_v1

这里的 Index 是一类 JSON 文档的逻辑集合,可以粗略类比关系型数据库中的表。

第二种:倒排索引

倒排索引是全文检索的底层数据结构,负责建立词项和文档之间的映射关系。

第三种:向量索引

例如 HNSW,负责组织 dense_vector 字段中的向量。

第四种:字段索引

例如 keyworddateinteger 字段,也会建立适合精确过滤、范围查询和聚合的数据结构。

所以:

创建一个 Elasticsearch Index,不等于选择了 HNSW 算法。

一个 Elasticsearch Index 内部,可以同时包含:

  • 文本字段的倒排索引;
  • Keyword 字段的精确索引;
  • 日期和数值字段的结构化索引;
  • Dense Vector 字段的 HNSW 索引;
  • Sparse Vector 字段的稀疏索引。

这也是 Elasticsearch 能够把全文检索、向量检索和元数据过滤放在同一份文档中的基础。


2.2 倒排索引解决什么问题

假设有三个文档:

  • 文档一:工业设备故障处理;
  • 文档二:工业设备维护手册;
  • 文档三:医院门诊预约流程。

倒排索引会形成类似下面的结构:

词项 对应文档
工业 文档一、文档二
设备 文档一、文档二
故障 文档一
维护 文档二
医院 文档三
门诊 文档三

当用户查询"工业设备故障"时,Elasticsearch 不需要逐个扫描所有文档,而是可以直接根据词项找到对应的文档列表。

Elasticsearch 会先通过 Analyzer 对文本进行分词和归一化,再建立由词项字典和 Posting List 组成的倒排索引。

因此,倒排索引解决的是:

如何快速找到包含相关词项的文档。


2.3 BM25 不是倒排索引

倒排索引找到候选以后,还需要判断哪些文档更加相关。

这就是 BM25 的工作。

BM25 会综合考虑:

  • 查询词在当前文档中出现了多少次;
  • 这个词在整个文档集合中是否稀有;
  • 当前文档是否过长;
  • 同一个词重复出现后的收益递减。

例如"故障"这个词在所有文档中都很少出现,那么包含"故障"的文档通常更值得关注。

而"设备"如果在大量文档中都出现,它对区分文档的作用就会下降。

所以:

倒排索引负责找到候选文档,BM25 负责给这些候选计算关键词相关性分数。

这和向量检索中的关系很相似:

文本检索 向量检索
倒排索引 HNSW、IVF
BM25 Cosine、IP、L2
Posting List 向量近邻候选
Analyzer Embedding 模型

当然,这只是为了帮助理解,两套体系的底层原理并不完全相同。


三、Elasticsearch 如何完成向量检索

现在的 Elasticsearch 已经能够作为向量数据库使用。

当我们把 Embedding 存入 dense_vectorsparse_vector 字段后,就可以基于向量相似度进行检索。


3.1 Dense Vector 是什么

用户输入一段文本:

设备出现故障以后应该怎样处理?

经过 BGE-M3 后,可以得到一个固定维度的稠密向量。

所谓稠密向量,是指向量的大部分维度都有数值。

例如:

\[0.032,-0.126,0.781,\\dots\]

单个数字本身通常没有直观业务含义,但是整个向量共同表示文本的语义特征。

查询时,用户问题也会被转换成相同维度的向量,再与数据库中的文档向量比较。


3.2 FLAT:最直接的搜索方式

FLAT 可以理解为全量比较。

假设数据库中有十万条向量,那么查询时就分别计算查询向量与十万条向量之间的距离。

它的优点是:

  • 实现简单;
  • 不会因为近似索引而漏掉真实近邻;
  • 适合作为召回率基准。

缺点也非常明显:

  • 数据量越大,计算次数越多;
  • 查询延迟随数据规模增长;
  • 高并发下成本较高。

所以 FLAT 更适合:

  • 小规模数据;
  • 离线评测;
  • 检查近似索引是否损失过多召回率。

3.3 HNSW:用图结构减少搜索范围

在大规模向量检索中,更常见的是 HNSW。

HNSW 会提前建立多层近邻图,查询时沿图搜索附近节点,而不是扫描全部向量。

Elasticsearch 的近似 kNN 搜索主要使用 HNSW;在当前版本中,普通浮点 dense_vector 默认使用量化后的 int8_hnsw,同时也允许根据场景配置其他 HNSW、Flat 和量化方案。

HNSW 中有三个非常重要的参数。

参数 所处阶段 参数增大后的主要影响
m 建索引 图连接更密,召回率可能提高,内存增加
ef_construction 建索引 建图质量提高,索引构建时间增加
num_candidates 查询 搜索候选增多,召回率提高,延迟增加

其中,mef_construction 主要决定索引图的质量;num_candidates 则决定一次查询愿意搜索多大的候选范围。

Elasticsearch 在每个分片中先获取一定数量的近似候选,再根据相似度选出 Top K,并合并各分片结果。

因此,HNSW 本质上是在做三者之间的平衡:

  • 查询速度;
  • 召回率;
  • 内存与索引成本。

3.4 量化不是另一套检索逻辑

在 Elasticsearch 中,我们还会看到:

  • int8_hnsw
  • int4_hnsw
  • bbq_hnsw
  • int8_flat
  • int4_flat

这些名称容易让人误以为它们是完全不同的检索算法。

实际上,它们更多是在回答另一个问题:

向量应该用多高的精度保存和参与检索?

原始浮点向量通常使用 32 位浮点数。量化可以把它压缩成 8 位、4 位甚至二进制表示,从而减少内存和磁盘消耗。

代价是:

  • 向量精度下降;
  • 相似度结果可能发生变化;
  • 召回率可能出现损失。

因此:

HNSW 负责怎样搜索,量化负责向量怎样压缩。

二者可以组合,但不是同一个概念。


3.5 Elasticsearch 支持哪些度量方式

Elasticsearch 的 dense_vector 支持多种相似度方式,包括:

  • cosine
  • dot_product
  • l2_norm
  • max_inner_product

普通浮点向量默认使用 Cosine;具体选择仍然应该以 Embedding 模型的训练方式和官方建议为依据。

对于 BGE-M3 这样的文本 Embedding,工程上不建议随意更换度量函数。

最稳妥的方式是:

  1. 确认模型输出是否已经归一化;
  2. 查看模型推荐的相关性计算方式;
  3. 使用评测集比较 Cosine 和 IP;
  4. 固定模型、向量版本和度量方式。

因为一旦更换 Embedding 模型或者度量方式,原有索引中的分数分布和排序结果都可能发生变化。


四、BGE-M3 为什么适合工业级混合检索

BGE-M3 的价值不只是生成一个稠密向量。

它可以在同一个模型中提供三种检索表示:

  • Dense Retrieval;
  • Sparse Retrieval;
  • Multi-Vector Retrieval。

同时,它支持多语言和长文本输入。


4.1 Dense Vector:解决语义相似

Dense Vector 更擅长理解整体语义。

例如用户问:

孩子晚上一直咳嗽,应该去看哪个科?

文档中写的是:

夜间持续性咳嗽建议优先就诊呼吸内科。

两段文字表面上的词并不完全相同,但是语义高度一致。

Dense Vector 能够把这种语义关系编码到连续向量空间中。

因此,它比较适合:

  • 自然语言问答;
  • 同义表达;
  • 问题改写;
  • 跨语言检索;
  • 用户问题和文档措辞不一致的场景。

4.2 Sparse Vector:保留词项,同时学习权重

BGE-M3 的 Sparse Vector 不是普通的固定长度稠密数组,而是一组非零 Token 与权重的对应关系。

例如:

json 复制代码
{
  "设备": 0.82,
  "故障": 0.91,
  "处理": 0.46
}

真实数据中可能使用 Token ID,而不是直接使用中文词语,但核心思想相同。

Sparse Vector 保留了词项维度,又通过模型学习词项的重要性。

因此,它兼具一些关键词检索和语义检索的特点:

  • 保留较强的词项可解释性;
  • 能强化重要术语;
  • 能弱化无意义的常见词;
  • 可以实现一定程度的词项扩展;
  • 比单纯词频更能反映语义相关性。

Elasticsearch 的 sparse_vector 查询可以使用预计算的 Token---权重,并通过查询和文档稀疏向量的加权点积计算相关性。


4.3 Multi-Vector:更细粒度的 Token 交互

BGE-M3 还可以生成多向量表示。

Dense Retrieval 通常使用一个向量代表整段文本,而 Multi-Vector 会使用多个 Token 级向量表示文档。

查询时,可以让查询 Token 和文档 Token 进行更细粒度的匹配。

它的优势是相关性判断更加精细,但代价也更明显:

  • 向量数量更多;
  • 存储成本更高;
  • 查询计算更加复杂;
  • 不适合直接面对全库完成第一阶段召回。

因此,更常见的方式是:

BGE-M3 官方也推荐在 RAG 中采用混合召回加重排的检索链路。


五、为什么工业级 RAG 不能只依赖 Dense Vector

只使用 Dense Vector,看起来架构最简单:

但在真实业务中,它很容易遇到精确词召回问题。

例如:

  • 设备错误码 E1107
  • 交换机型号 H3C-S12500
  • 合同编号 HT20260725001
  • 国家标准 GB/T 19001-2016
  • 药品名称;
  • 人名;
  • 机构名;
  • 版本号;
  • 接口字段名。

这些信息的共同特点是:

它们的价值主要来自精确匹配,而不是整体语义。

Dense Vector 可能会召回一批"设备故障处理""错误代码说明""设备维修手册"等语义相近的文档,但不一定能保证精确命中 E1107

这正是 BM25 的优势。

另一方面,用户问题和文档可能使用完全不同的表达方式,这又是 Dense Vector 的优势。

因此,工业级 RAG 的重点不是争论 BM25 和 Dense 谁更强,而是让它们互相补足。


5.1 BM25、Dense 和 Sparse 的职责

检索方式 主要优势 典型场景
BM25 精确词项、编号和专有名词 错误码、型号、合同号、法规
Dense 整体语义和同义表达 自然语言问题、语义改写
Sparse 学习型词项权重 术语强化、语义词项扩展
Multi-Vector Token 级细粒度交互 候选精排

一个比较合理的工业检索流程是:

这里每一层都有明确作用:

  • BM25 保证精确术语;
  • Dense 补充语义召回;
  • Sparse 提供学习型词项匹配;
  • RRF 合并不同分数体系;
  • Reranker 做最终相关性判断。

六、元数据过滤和 Milvus Partition 是一回事吗

这也是一个非常容易混淆的问题。

答案是:

元数据过滤和 Partition 不是一回事,但 Partition 可以帮助进一步缩小搜索范围。


6.1 元数据过滤解决业务条件问题

假设 Milvus Collection 或 Elasticsearch Index 中存储了以下字段:

  • vector
  • tenant_id
  • kb_id
  • department
  • doc_type
  • status

用户查询时,可以限定:

只搜索 tenant_001 中、属于 equipment_manual 知识库、状态为 active 的文档。

这就是元数据过滤。

它回答的是:

哪些数据符合业务条件?

Elasticsearch 可以在 kNN 查询中使用 Filter 进行预过滤,保证最终 Top K 候选同时满足元数据条件。

Milvus 同样支持基于标量字段的标准过滤和迭代过滤。


6.2 Milvus Partition 是数据组织范围

Milvus 中,一个 Collection 可以划分成多个 Partition。

例如,可以按业务或租户划分:

  • 租户一;
  • 租户二;
  • 租户三。

查询时,如果明确指定只搜索某个 Partition,就可以避免访问其他 Partition。

Milvus 还支持 Partition Key,根据某个标量字段的值自动组织数据;查询条件中包含 Partition Key 后,可以缩小需要搜索的 Partition 范围。


6.3 Partition 不等于 Filter

二者的区别可以这样理解:

概念 作用
Metadata Filter 根据字段表达式筛选符合条件的数据
Collection Partition 把 Collection 划分为多个数据子集
Partition Key 根据指定字段自动完成分区路由
IVF Cluster 根据向量空间位置进行算法聚类

指定 Partition 后,Partition 内部仍然可以继续使用元数据 Filter。

例如:

此外,还要避免把 Milvus Partition 和 IVF 中的"向量聚类"混为一谈。

  • Collection Partition 按业务组织数据;
  • IVF Cluster 按向量空间位置组织数据;
  • Metadata Filter 按字段条件筛选数据。

三者虽然都在缩小搜索范围,但层次完全不同。


6.4 Elasticsearch 中对应什么

Elasticsearch 的普通元数据过滤,主要通过 Query DSL 中的 filter 完成。

例如:

  • tenant_id = tenant_001
  • status = active
  • department = maintenance

这些字段通常使用 keyworddate 或数值类型。

Elasticsearch 中没有和 Milvus Partition 完全相同的一对一概念。

比较接近的数据组织方式包括:

  • 不同业务使用不同 Index;
  • 通过 Shard 完成底层数据分布;
  • 使用 Custom Routing 将相同租户的数据路由到指定 Shard;
  • 查询时携带 Routing,减少需要访问的 Shard。

但对于大多数中小型企业 RAG,第一阶段通常不需要过早使用复杂 Routing。

更重要的是先保证:

  • tenant_id 过滤正确;
  • ACL 权限过滤正确;
  • 无效文档不会参与检索;
  • 不同知识库的数据不会混召回。

七、Elasticsearch 是不是已经把 Milvus 的能力都做了

从工业 RAG 的常见需求来看,两者的能力确实已经高度重叠。

能力 Elasticsearch Milvus
稠密向量 支持 支持
稀疏向量 支持 支持
BM25 传统强项 已支持
元数据过滤 支持 支持
ANN 检索 支持 支持
精确向量检索 支持 支持
混合检索 支持 支持
多向量 支持 支持
分布式部署 支持 支持

Milvus 当前支持多种稠密向量索引、Sparse Vector、BM25 Function 和混合检索。

所以今天已经不能再简单地说:

Elasticsearch 只能做关键词,Milvus 只能做向量。

更准确的说法应该是:

Elasticsearch 是从搜索引擎向混合检索平台演化;Milvus 是从向量数据库向混合向量检索平台演化。

二者的能力出现了交叉,但它们的能力中心仍然不同。


7.1 Elasticsearch 的核心仍然是搜索

Elasticsearch 的能力体系是从下面这条路线发展而来的:

它天然擅长:

  • 全文检索;
  • 精确词匹配;
  • 短语查询;
  • 多字段加权;
  • 同义词;
  • 模糊查询;
  • 高亮;
  • 时间与数值范围查询;
  • 聚合统计;
  • 文档型数据检索
  • 日志与可观测性数据分析。

因此,Elasticsearch 是:

以文档搜索和复杂查询为中心,逐步增加向量能力。


7.2 Milvus 的核心仍然是向量

Milvus 的能力体系是从另一条路线发展而来的:

Milvus 支持 FLAT、HNSW、IVF_FLAT、IVF_PQ、DiskANN、GPU 索引和多种量化变体,并提供面向不同规模与内存条件的索引选择。

它天然更关注:

  • 大规模向量存储;
  • ANN 性能;
  • 多种向量索引;
  • 内存和磁盘之间的权衡;
  • GPU 加速;
  • 多模态向量;
  • 超大规模向量检索。

因此,Milvus 是:

以向量存储和 ANN 检索为中心,逐步增加文本和混合搜索能力。


八、什么时候只使用 Milvus

并不是所有 RAG 都需要 Elasticsearch。

如果业务本身就是向量优先,那么单独使用 Milvus 是合理的。


8.1 图片、视频和音频相似检索

例如:

  • 以图搜图;
  • 视频片段相似搜索;
  • 人脸特征检索;
  • 音频指纹检索;
  • 商品图片相似度;
  • 多模态推荐。

这些场景的核心不是关键词,而是高维特征向量之间的近邻关系。

即使增加关键词,它通常也只是辅助条件。


8.2 向量规模非常大

如果系统需要存储数亿、十亿甚至更大规模的向量,并且明确需要:

  • DiskANN;
  • IVF_PQ;
  • GPU_CAGRA;
  • GPU_IVF;
  • 更细致的量化策略;
  • 大于内存容量的数据搜索;

Milvus 的向量中心架构会更有优势。

DiskANN 等索引尤其适合数据规模超出内存容量的场景。


8.3 搜索条件相对简单

如果元数据主要是:

  • 租户;
  • 类别;
  • 时间;
  • 语言;
  • 状态;

同时不需要复杂的:

  • 同义词;
  • 短语匹配;
  • 搜索高亮;
  • 多字段权重;
  • 搜索运营规则;
  • 大量聚合分析;

那么 Milvus 加标量过滤可能已经足够。


九、什么时候只使用 Elasticsearch

对于大多数企业文档型 RAG,我更倾向于优先考虑 Elasticsearch。


9.1 企业知识库和文档检索

例如:

  • 医院知识库;
  • 工业设备手册;
  • 企业制度;
  • 智能客服;
  • 工单知识库;
  • 电商规则;
  • 产品说明书;
  • 法律法规;
  • 合同知识库。

这些场景通常同时需要:

  • 精确词匹配;
  • 语义搜索;
  • 元数据过滤;
  • 多租户隔离;
  • 权限过滤;
  • 文档溯源;
  • 多字段查询。

它们的核心仍然是文档搜索,而不只是向量近邻。


9.2 BM25 的价值不可替代

工业知识中经常出现:

  • 型号;
  • 错误码;
  • 接口名;
  • 文件名;
  • 合同号;
  • 人名;
  • 组织名;
  • 法律条款编号;
  • 药品名称。

这些内容通常不能只依赖 Dense Vector。

Elasticsearch 可以在同一个 Document 中保存:

  • 原始 Chunk 文本;
  • Dense Vector;
  • Sparse Vector;
  • 租户和权限字段;
  • 文档位置和来源。

然后一次完成:

这可以显著降低第一版系统的架构复杂度。


9.3 希望一个系统完成检索闭环

假设第一版同时使用 Elasticsearch 和 Milvus,就要面对:

  • 文档双写;
  • 向量双写或拆分写入;
  • 删除一致性;
  • 更新一致性;
  • ID 对齐;
  • 权限条件对齐;
  • 两套备份;
  • 两套监控;
  • 两套索引重建;
  • 跨引擎结果融合。

如果单 Elasticsearch 已经能够满足延迟、召回率和数据规模要求,那么双引擎并不会让系统显得更工业级。

很多时候,它只是让系统更复杂。


十、什么时候需要 Elasticsearch 加 Milvus

Elasticsearch 加 Milvus 并不是错误架构。

问题在于,必须存在明确理由。


10.1 Elasticsearch 的向量性能经过压测后不够

这里的关键词是:

经过压测后。

不能因为 Milvus 是专业向量数据库,就默认 Elasticsearch 的向量能力一定不够。

应该通过真实数据比较:

  • 向量数量;
  • 向量维度;
  • 查询并发;
  • P95、P99 延迟;
  • Recall@K;
  • 内存使用;
  • 索引构建时间;
  • 数据写入速度;
  • 过滤后的检索性能。

只有当测试证明 Elasticsearch 无法满足业务目标时,才需要拆分向量召回。


10.2 明确需要 Milvus 的专业索引能力

例如业务明确依赖:

  • DiskANN;
  • IVF_PQ;
  • GPU_CAGRA;
  • GPU_IVF;
  • 超大规模多模态向量;
  • 特殊的内存压缩方案。

这时可以让:

  • Elasticsearch 负责 BM25、全文检索和部分 Sparse 召回;
  • Milvus 负责大规模 Dense ANN;
  • 应用层统一完成 RRF 或自定义融合。

10.3 企业已经有两套成熟平台

如果企业内部已经存在:

  • 成熟的 Elasticsearch 搜索中台;
  • 成熟的 Milvus 向量平台;
  • 统一文档 ID;
  • 统一写入和删除事件;
  • 完整监控;
  • 备份和容灾;
  • 权限同步机制;

那么继续使用双引擎是合理的。

因为此时系统复杂度已经被平台能力消化。

但对于从零开发的项目,双引擎的建设成本通常不能被忽略。


10.4 双引擎不是免费的

一个文档更新事件,在双引擎架构中可能经历:

如果 Elasticsearch 成功、Milvus 失败,应该怎么办?

  • 重试 Milvus;
  • 回滚 Elasticsearch;
  • 标记文档为部分可用;
  • 暂停该知识库查询;
  • 使用旧版本向量;
  • 进入补偿任务队列。

删除文档时也会遇到同样问题。

所以:

双引擎不是"多部署一个数据库"那么简单,而是引入了分布式数据一致性问题。


十一、我的工业级 RAG 应该怎样设计

结合企业文档、BGE-M3、混合检索和 Agent 工作流,我认为第一阶段可以采用下面的架构。


11.1 MinIO 负责原始文件

MinIO 主要保存:

  • PDF;
  • Word;
  • PPT;
  • 图片;
  • 音频;
  • 原始上传文件;
  • 解析后的 Markdown;
  • 中间 JSON;
  • 页面截图;
  • 表格和图片产物。

不建议把完整 PDF 二进制直接塞进 Elasticsearch。

Elasticsearch 中只需要保存:

  • Chunk 文本;
  • 文档 ID;
  • 页码;
  • 段落位置;
  • MinIO 对象地址;
  • 检索字段;
  • 元数据;
  • 向量。

11.2 PostgreSQL 负责业务事实

PostgreSQL 或 MySQL 主要保存:

  • 用户;
  • 租户;
  • 知识库;
  • 文档任务;
  • 解析状态;
  • 文档版本;
  • 权限关系;
  • 上传记录;
  • 删除状态;
  • 评测任务;
  • Agent 任务信息。

关系型数据库仍然是业务事实来源。

Elasticsearch 是检索副本,不应该成为所有业务状态的唯一来源。


11.3 Elasticsearch 负责可检索数据

Elasticsearch 中的一条 Chunk,可以包含:

字段类别 示例字段
标识 chunk_id、doc_id、version_id
租户 tenant_id、organization_id
知识库 kb_id
文本 title、content
向量 content_dense、content_sparse
位置 page_number、section_title
权限 acl_users、acl_roles
状态 status、effective_date
溯源 source_uri、source_filename
模型版本 embedding_model、parser_name

这样,BM25、Dense、Sparse 和权限过滤可以围绕同一条 Document 完成。


11.4 Redis 负责短期状态

Redis 可以承担:

  • Query Cache;
  • Embedding Cache;
  • 会话上下文;
  • Agent 中间状态;
  • 分布式锁
  • 任务进度;
  • 限流;
  • 热点问题缓存。

但 Redis 不适合作为完整知识库的长期事实来源。


11.5 LangGraph 负责复杂流程编排

当 RAG 进入 Agent 阶段以后,检索不再一定是固定的一次 Query。

一个 Agent 可能需要:

  1. 判断用户意图;
  2. 选择知识库;
  3. 查询业务接口;
  4. 执行多轮检索;
  5. 判断召回结果是否充分;
  6. 改写问题;
  7. 扩展关键词;
  8. 二次检索;
  9. 触发人工确认;
  10. 生成最终回答。

LangGraph 管理的是这条工作流,而 Elasticsearch 负责其中的检索能力。


十二、第一阶段为什么不建议直接加入 Milvus

对于一个企业文档型 RAG,第一阶段最重要的不是组件数量,而是把检索闭环跑通。

我更建议优先完成:

  1. 文档解析;
  2. Chunk 策略;
  3. Metadata 设计;
  4. BM25 召回;
  5. Dense 召回;
  6. Sparse 召回;
  7. RRF 融合;
  8. Reranker 精排;
  9. 权限过滤;
  10. 召回评测。

这套流程单独使用 Elasticsearch 就可以完成。

如果第一阶段直接引入 Milvus,很容易把大量时间消耗在:

  • 环境部署;
  • 双写代码;
  • 数据一致性;
  • 跨引擎融合;
  • 两套 Schema;
  • 两套监控;
  • 两套异常处理。

而真正决定 RAG 效果的内容可能还没有做好:

  • 文档是否正确解析;
  • Chunk 是否合理;
  • 问题是否需要改写;
  • Metadata 是否完整;
  • 权限是否准确;
  • 评测集是否真实;
  • Reranker 是否有效。

所以第一阶段应该优先验证检索方法,而不是优先堆叠基础设施。


十三、什么时候再引入 Milvus

Milvus 不应该因为"感觉更专业"而加入,而应该因为出现了可测量的瓶颈。

可以建立一套评测体系。

评测维度 关注内容
Recall@K 正确文档是否进入前 K 条
MRR 正确文档排名是否足够靠前
NDCG 多条相关文档的整体排序质量
P95 延迟 大多数查询的响应时间
P99 延迟 极端查询延迟
QPS 单位时间可处理请求数
内存 向量索引占用
磁盘 原始向量与量化索引占用
构建时间 全量重建索引需要多久
更新延迟 文档更新多久后可检索

分别测试:

  • 纯 BM25;
  • 纯 Dense;
  • 纯 Sparse;
  • BM25 加 Dense;
  • BM25 加 Dense 加 Sparse;
  • RRF 后结果;
  • Reranker 后结果;
  • 不同 num_candidates
  • 不同 Chunk 长度;
  • 不同 Embedding 模型。

如果测试结果表明:

  • Elasticsearch 的 Dense 延迟无法满足要求;
  • 内存成本过高;
  • 向量规模超出预期;
  • 明确需要 DiskANN 或 GPU;
  • 向量检索已经成为独立平台;

再将 Dense ANN 拆分到 Milvus。

这时,Milvus 的加入是由数据驱动的,而不是由技术名词驱动的。


十四、最后的判断

重新理解 Elasticsearch 以后,我认为最重要的并不是记住 ES 和 Milvus 各自支持多少种索引,而是先把检索过程中的层次分清楚。

第一,索引结构和度量方式不是一回事。

  • 索引结构决定数据如何组织;
  • 搜索算法决定如何快速找到候选;
  • 度量方式决定候选之间谁更相似;
  • 元数据过滤决定谁有资格参与检索;
  • 排序和重排决定最终结果如何呈现。

第二,Elasticsearch 和 Milvus 的能力虽然越来越重叠,但能力中心不同。

  • Elasticsearch 以全文搜索、文档查询和复杂过滤为中心;
  • Milvus 以大规模向量存储和 ANN 检索为中心。

第三,不是所有工业级 RAG 都需要双引擎。

对于大多数企业文档知识库:

Elasticsearch 已经可以完成 BM25、Dense Vector、Sparse Vector、Metadata Filter、RRF 和结果重排前的候选召回。

第四,Milvus 应该在向量问题真正成为独立瓶颈时加入。

例如:

  • 向量规模非常大;
  • ES 向量检索经过压测后不够;
  • 需要 DiskANN、IVF_PQ 或 GPU 索引;
  • 企业已经具备成熟向量平台。

所以,我对工业级 RAG 架构选型的最终理解是:

不要从数据库名称出发设计架构,而要先从业务数据、检索方式、访问权限、性能目标和评测结果出发。

架构并不是组件堆得越多越专业。

真正合理的方式是:

先使用最小系统跑通完整检索闭环,再根据真实数据和压力测试决定是否拆分。

对于企业文档型 RAG,第一阶段从 Elasticsearch 开始,把 BM25、BGE-M3 Dense、BGE-M3 Sparse、元数据过滤、RRF 和 BGE-Reranker 跑通,通常是一条更稳、更容易评测,也更容易演进的路线。

相关推荐
众人皆醒我独醉15 小时前
TGI:HuggingFace 的官方推理服务——不止 PagedAttention,更懂模型生态
面试·llm·ai编程
众人皆醒我独醉15 小时前
vLLM:PagedAttention 如何让 LLM 推理吞吐提升 24 倍
面试·llm·ai编程
神奇小汤圆16 小时前
面试官突然问:“数仓哪一层最适合做RAG?”他一下被问住了…
面试
一只旭宝16 小时前
C++手写shared_ptr共享智能指针|原子引用计数、强弱引用控制块、赋值重载底层深度剖析
开发语言·c++·面试
swipe21 小时前
14|(前端转全栈)商品详情高频访问怎么扛?Redis Cache Aside 实战
前端·后端·面试
swipe21 小时前
15|(前端转全栈)从一个副标题字段看懂后端完整交付链路
前端·后端·面试
胡萝卜术1 天前
「最大子数组和」的 Kadane 算法中回过神来,我们正式踏入一个全新的领域——二叉树
前端·javascript·面试
KaifuZeng1 天前
电源面试问题汇总二
单片机·嵌入式硬件·面试
thesky1234561 天前
27届大模型岗面试准备(十一):模型蒸馏与稀疏化——从软标签到结构化剪枝的压缩全景
面试·大模型·剪枝·知识蒸馏·模型蒸馏·稀疏化