ElasticSearch 全文检索:倒排索引 + IK 分词 + BM25
示例代码:
examples/es-test/
为什么有了 Milvus 还要 ES
ES 和 Milvus 解决的是两类根本不同的问题:
| Milvus(语义检索) | ES(词条检索) | |
|---|---|---|
| 匹配逻辑 | 「意思差不多」 | 「字面差不多」 |
| 适合输入 | 自然语言问句、模糊概念 | 术语、编号、代码、人名、地名 |
| 典型成功 | 搜「杭州旅游」命中「西湖攻略」 | 搜 errorCode=5001 只命中 5001 |
| 典型失败 | 搜 i7-13700K 可能漂到 i7-12700K | 搜「西湖攻略」搜不到「杭州旅游」 |
ES 管理论关键词匹配,Milvus 管语义相似度——组合起来才是混合检索。
倒排索引 vs 正向索引
MySQL(正向索引):文档 → 关键词。搜「跑步」需要遍历每一行匹配 content 字段,O(n)。
ES(倒排索引):关键词 → 文档列表。
"今天" → [1, 2, 3]
"跑步" → [1, 2]
"骑车" → [3]搜「跑步」→ 直接定位 [1, 2],O(1) 找词 + BM25 排序。
IK 中文分词器
ES 默认 standard 分词器按空格切——对英文好用,对中文是灾难:
| 分词器 | 「混合检索知识库」 |
|---|---|
standard | 混 合 检 索 ...(逐字拆) |
ik_max_word | 混合 检索 知识 知识库 库(穷举) |
ik_smart | 混合 检索 知识库(语义单元) |
ik_max_word:尽可能多切——召回率高ik_smart:最少切分——精准度高
项目实践:索引时用 ik_max_word(词条全),查询时用 ik_smart(精准匹配)。
BM25 打分
BM25 是 ES 默认的相关性打分算法,核心看两件事:
- 词频(TF):查询词在文档里出现多少次?有上限,不会无限涨
- 逆文档频率(IDF):这个词在整个语料库有多「稀有」?越稀有贡献越大
搜「LangGraph Agent」→「Agent」在很多文档都有(IDF 低),「LangGraph」较稀有(IDF 高),包含「LangGraph」的文档排前面。
BM25 不关心语义,只关心「词出现没、多不多、稀不稀有」。
混合检索 + Rerank:完整流水线
纯关键词 + 纯语义到达瓶颈后,下一步是:
┌─→ ES BM25 关键词检索 ─→ hits_es ─┐
用户问题 ──┤ ├─→ 合并去重 ─→ Rerank 精排 ─→ LLM
└─→ Milvus Embedding 语义检索 ─→ hits_milvus ─┘Rerank 是精排质检员——粗召保证「不漏」,精排保证「不噪」。用更强的模型(如 qwen3-rerank)对每条文档和原问题做精确相关性打分,取 top 3 送 LLM。
| Embedding 模型 | Rerank 模型 | |
|---|---|---|
| 输入 | 一段文本 | 一个问题 + 一条文档 |
| 输出 | 向量 | 相关性分数 |
| 做什么 | 粗筛 | 精排 |
| 速度 | 快(向量索引) | 慢(逐对计算) |
核心收获
- 倒排索引 = 搜索引擎的「书末索引」——先有它再有 BM25
- IK 分词器 = ES 中文检索的入口——没有它,ES 搜中文基本废了
- 混合检索 = 两条腿走路——ES 关键词兜底专有名词,Milvus 语义兜底自然语言
- Rerank 是最后一关——把 20 条粗召结果精筛到 3 条,Token 省了、噪声降了