混合检索到底在混什么:为什么纯向量检索会认错人——Hybrid RAG 系列之一

0 阅读4分钟

一句话价值:RAG 的检索不能只靠向量。向量能抓住「意思像不像」,却抓不住「词对不对」——专业术语、人名、订单号这类精确实体,纯语义检索会认错人。混合检索就是让「关键词检索」和「语义检索」互补,用两套索引各管一半,再融合成一份结果。这是检索从「能查到」到「查得准」的关键一步。


先看纯向量检索的三个翻车现场

前面系列提过「高血糖 / 低血糖」语义相近、向量分不清。其实这类翻车每天都在发生,而且更隐蔽:

  • 精确实体:问「第 40 回讲了什么」,向量可能返回「第 39 回」或「第 41 回」——三者的语义都是「某个回目的剧情」,向量距离很近,但它分不清数字。
  • 专有名词:问「段誉」,向量可能优先返回「乔峰」相关段落——两人都是主角、都活跃在江湖,语义上「近」,但用户要的是精确命中「段誉」两个字。
  • 型号 / 订单号:问「PO-20250409-K9 这个订单」,向量对这类精确串几乎无能为力——它记的是「含义」,不是「字符」。

根子在哪? 向量检索回答的是「哪段话和问题意思最像」,而很多查询要的是「哪段话包含这个词」。这是两件不同的事,一个模型干不了两个活。

三个存储,各管一件事

聊混合检索之前,先把三种存储的定位摆正——很多人把 ES 当 MySQL 用、把 Milvus 当「又一个数据库」,这是混乱的开始:

MySQLMilvusElasticsearch
类型关系型数据库向量数据库搜索引擎
底层索引B+ 树向量索引(HNSW)倒排索引
核心动词
擅长精确查询、事务、关联语义相似度关键词精确命中

记住最后一行:MySQL 是「存」、Milvus 是「比」、ES 是「找」。它们不冲突,反而互补。混合检索就是让「比」(Milvus)和「找」(ES)各干各的,再合到一起。

ES 的杀手锏:倒排索引

ES 为什么能毫秒级在几亿条文档里「找词」?因为它用倒排索引把「搜内容」变成了「查表」。

正排 vs 倒排。 正排是「文档 → 词」(MySQL 的思路),要查「哪些文档包含『杭州』」,只能一条条扫描正文。倒排把它反着存——「词 → 文档列表」:

杭州 → [doc1, doc3]
西湖 → [doc1]
城市 → [doc2]

查询「杭州」时,直接查词典找到「杭州」,立刻拿到 [doc1, doc3]——不扫任何文档

三层结构。 真正的倒排索引比上面这张图更细:

  1. 词典(Term Dictionary):所有不重复的词,按字典序排好,是「入口」。
  2. 倒排列表(Posting List):每个词对应的一串文档 ID。
  3. 附加信息:每个 doc ID 后面还跟着 TF(词频)Position(位置)——前者算相关度,后者做短语匹配和高亮。

写入与查询两段式。 写入时,文档经过 analyzer 分词(比如 IK 中文分词),把词逐个登记进倒排索引;查询时,query 分词后查词典拿列表、取交集/并集,再用 BM25 打分排序——BM25 的核心是「词越罕见(IDF 高)、出现越频繁(TF 高)、文档越短,越相关」。

中文检索的关键是 IK 分词器:写入用 ik_max_word(最细粒度,保证召回),查询用 ik_smart(最粗粒度,保证精度)。没有它,中文会被切成一个个单字,倒排索引直接失效。

落地到 create.mjs,就是建索引时给正文字段配上这套分词策略:

note_body: {
  type: 'text',
  analyzer: 'ik_max_word',      // 写入:切得细,召回多
  search_analyzer: 'ik_smart'   // 查询:切得准,噪声少
}

混合检索的骨架:两条腿走路

于是答案浮出水面:

flowchart LR
    A[chunk 内容] --> B[embeddings 向量] --> C[Milvus 语义 Top-K]
    A --> D[Elasticsearch 倒排] --> E[关键词 Top-K]
    C --> F[RRF 融合]
    E --> F
    F --> G[rerank 精排]
    G --> H[生成]
  • ES 管「词对不对」:精确名词、编号、术语,倒排索引精确命中。
  • Milvus 管「意思像不像」:换种说法、语义相近但用词不同,向量能捞回来。
  • 两路召回后,还要融合(RRF)和精排(rerank)——这是后面两篇的事。

本篇小结

  1. 纯向量检索抓「意思」,抓不住「词」——精确实体、专有名词、型号会认错人。
  2. 三种存储定位:MySQL 、Milvus 、ES ,各管一件事。
  3. ES 的倒排索引把「搜内容」变成「查表」,靠「词 → 文档列表」毫秒级命中。
  4. 混合检索 = ES 关键词 + Milvus 语义,两路互补,再融合精排。

系列预告

  • 下一篇:Milvus 向量 + RRF 融合——语义侧怎么召回,两路「量纲不可比」的分数怎么合成一份。
  • 之后:rerank 精排——为什么融合完还要再「拧一遍」,完整三段式怎么落地到代码。