一句话价值:检索效果不好时,大多数人的第一反应是"换个更强的 embedding"。但真正的问题是——你没分清是哪一类东西没被治住。这篇把"检索要找什么"拆成三类敏感:字面敏感、词义敏感、词汇表敏感,各自对应一种检索。分清之后,选型不再靠感觉。
一个被问反了的问题
经常听到这样的讨论:
"我们是不是该上 Elasticsearch 了?它对词义比较敏感。"
这句话本身就问反了。Elasticsearch 恰恰是三种检索里最不"懂词义"的那个。
它擅长的是字面命中——你查"退款流程",它保证含这四个字的文档一定被捞出来。至于"如何把钱退回来"这种换个说法的问法,它基本无能为力。
问题的根源在于,"敏感"这个词被混用了。检索里其实是三种完全不同的敏感,需要三种完全不同的工具:
| 敏感类型 | 关心的是 | 谁的主场 | 谁无能无力 |
|---|---|---|---|
| 字面敏感 | 这个词必须原样命中 | 倒排索引 / BM25 | 向量 |
| 词义敏感 | 换个说法也要懂 | 向量 | 关键词检索 |
| 词汇表敏感 | 得先对齐到同一个实体 | 别名 / 实体链接 | 两种都不行 |
下面用一个贯穿案例把它们逐层剥开。
案例:那个查不到的"台式奶茶"
我们建了一张奶茶图谱。节点叫 台湾奶茶,用户问的是:
台式奶茶的饮品都有哪些配料?
("台式奶茶"是台湾奶茶的常见俗称,两者指同一个东西。)
先看纯关键词检索这条路——把"台式奶茶"丢进倒排索引,会发生什么?
查不到。
因为倒排索引里压根没有"台式"这个词元。索引里存的是节点名"台湾奶茶"、描述文本里出现的"台湾"。用户说的"台式",系统从没见过。
所以这里的关键结论是:
这不是字面敏感问题,也不是词义敏感问题。这是词汇表敏感问题——用户用的词和你存的词是两个词。
用 ES 裸查,同样漏。指望向量蒙,实测只把 台湾奶茶 排到第 7 位(相似度 0.8031),进不了候选。
这类问题,两种检索都救不了。 必须显式告诉系统:"台式"和"台湾"指同一个东西。
第一类:字面敏感——倒排索引的主场
先补上 Elasticsearch 的真实价值。它不可替代的场景是精确命中:
-
专有名词、编号、型号 订单号
SO20260901-8842、iPhone 15 Pro Max、药品通用名、SKU 编码。 这类 token 是低频的,embedding 模型在训练语料里几乎没见过——在向量空间里它基本是个随机点。BM25 反而稳如老狗。 -
长尾词、新词、造出来的词 同理,模型没见过的词,向量给不出有意义的距离,倒排索引照样一次命中。
-
精确过滤与排序 时间范围、价格区间、类目、状态。
-
可解释性 命中了哪个词、为什么命中,一目了然。向量只能给你一个
0.83,说不清为什么。
一句话:ES 的价值是"保证字面不漏",不是"理解语义"。 把它当"更懂词义"的工具就错位了。
第二类:词义敏感——向量的主场
反过来说,向量治的是另一类病。看这个真实提问:
喝了会上瘾、甜甜的那种奶茶,用了什么工艺?
没有任何一个字是图谱里的节点名。"会上瘾""甜甜"这些词,在库里一个都搜不到(字面检索会返回空)。但向量能接住——它把这句话编码后,召回:
冲泡(0.854) 牛奶(0.852) 珍珠奶茶(0.847) 果糖(0.846) ...
珍珠奶茶 排到了第三。 因为它描述的"口感香甜浓郁、喝了就上瘾"跟这句问话语义最近。
这就是向量的看家本领:模糊表述、口语化提问、换个说法。这是倒排索引完全做不到的。
反过来,向量也有它彻底无能的地方——同形异义。"苹果"是水果还是那家公司?向量把它们编码得很近(因为上下文里它们确实常一起出现),它分不出来。这种歧义消解得靠上下文,不是靠检索。
第三类:词汇表敏感——别名表的主场
回到"台式/台湾"。这类问题的本质是:用户嘴里的词,和你库里的词,是两个词。
| 用户说 | 图谱里登记的名字 |
|---|---|
| 台式奶茶 | 台湾奶茶 |
| 波霸奶茶 | 珍珠奶茶 |
| 粉圆 | 珍珠 |
解法只有一个方向:显式登记这份对应关系——也就是上一篇讲的别名表:
台湾奶茶 → aliases: ["台式奶茶", "台茶"]
珍珠奶茶 → aliases: ["波霸奶茶", "珍奶"]
然后把别名一起索引进全文索引,用户说"台式奶茶",倒排索引里就有"台式"这个词,直接命中 台湾奶茶 节点。
这和 ES 的 synonym 词典是同一件事
如果你用过 Elasticsearch,这个思路你一定见过——analyzer 里配同义词:
"台式,台湾" → 归一化到同一个 term
别名词典和 synonym 词典,本质是同一个东西:一份人工维护、可控、可审计的词表,用来做实体对齐。
为什么必须靠词表,而不能靠模型:
- 词表是确定性的。配了
台式→台湾,它就一定生效,可测试、可回归。 - 词表是可审计、可运维的。对齐错了,加一条、删一条就行。
- 模型对齐是概率性的。这次蒙对了你没底,蒙错了你没法跟人解释"为什么这台机器昨天答对了今天答错了"。
在真实业务里,这种确定性本身就是价值。
三路合起来:一张分工表
把三类敏感和三种检索对上:
| 检索路 | 治什么 | 在"台式奶茶"上的表现 | 在"喝了会上瘾"上的表现 |
|---|---|---|---|
| 倒排 / 关键词 | 字面命中 | 裸查也漏(词元不存在) | 漏(没有字面词) |
| 向量 | 语义相近 | 排第 7,没进候选 | ✅ 命中 珍珠奶茶 |
| 别名 / 全文+词表 | 词汇表对齐 | ✅ 台式 → 台湾 | 一般 |
| 图 | 多跳关系 | —— | 多跳本身没问题,卡在入口 |
看出来了吗——没有哪一路是"更强的检索",它们是三个不同的入口。
真实架构里它们叠着用:多路召回 → 融合(RRF)→ 重排(rerank)→ 再交给 LLM。这不是堆料,是因为每路捞到的东西本来就不一样。
而"词汇表"这一路最容易被忽略,因为它不像向量那样有"AI 感"——它就是一张手写的表。但没有它,"台式奶茶"这类问题永远答不对。
收个口:一个自检清单
下次检索效果不好,先别急着换模型。按这个顺序问:
- 用户说的词,我库里存的是同一个词吗? 不是 → 别名词表问题。加别名,或配同义词。
- 问题是"换个说法",还是"精确某个词"? 换个说法 → 向量。精确某个词(编号、型号)→ 倒排。
- 答案需要沿着关系走两步以上吗? 需要 → 图。不需要 → 向量 + 关键词就够。
大多数"检索不准"的抱怨,卡在第 1 条上。而它恰恰是最不需要 AI 的那一条。
本篇小结
- "检索敏感"其实分三类:字面敏感、词义敏感、词汇表敏感,各要一种工具。
- 倒排索引治字面:编号、型号、专有名词、长尾词——模型没见过的词,它最稳。它不理解词义。
- 向量治语义:口语化、模糊表述、换个说法。但它对同形异义(苹果/Apple)无能无力。
- 别名表治词汇表:用户说的词和你存的词对不上时。这和 ES 的 synonym 词典是同一件事——确定、可控、可审计,不靠模型猜。
- 三路不是"谁更强",是三个不同入口,真实架构里叠着用。
系列预告
下一篇落到工程:既然要三路,是不是得上一套 Milvus + 一套 Elasticsearch?不一定。 一个 Neo4j 就能把向量索引、全文索引和多跳遍历全装下——而且因为向量召回直接返回节点,它反而能把"召回"和"遍历"接在同一个查询里。