Query 理解与多路召回:从脏输入到候选池
Published in:2025-12-15 | category: 搜推算法

本文是 搜推算法笔记 第 2 篇。索引见 搜推算法 · 知识索引

上一篇:搜推算法基础 · 下一篇:向量检索与 ANN

「搜不到」在工程上常有两类根因:Query 层(输入脏、错、歧义)和召回层(覆盖不够、路数缺失)。例如用户输入「蓝芽耳机」——Query 有错别字;搜「纯奶」但标题写「纯牛奶」——字面召回覆盖不足。两者表象相同,排查路径不同。

Query 理解和召回是搜索链路的上下游:前者把脏输入变成干净、可扩展的信号;后者用这些信号从全库捞出候选池。本文将两层合并讲解,因其在工程上总是紧挨着发生。

一、Query 理解:把脏输入变干净

1.1 用户输入的常见问题

问题例子不处理的后果
错别字蓝芽耳机召回失败/偏移
同义表达纯奶 / 纯牛奶漏召
过短/过宽「奶」「手机」结果太散
过长口语「我想买个送女朋友的红色围巾」噪声词干扰
类目混淆「苹果」水果还是手机?错类目硬过滤会空

1.2 标准流水线

1
2
3
4
5
6
7
8
9
10
11
12
原始 Query


① 清洗归一化

② 纠错(低置信保留原词)

③ 改写扩展(产出多个 Q')

④ 意图 / 类目 / 实体识别

输出:corrected_query, query_list[], predicted_category, tokens ...

五条设计原则:

  1. 先确定性、后统计/模型(清洗永远先做)
  2. 先纠错、后改写(避免错词扩散)
  3. 低置信不改(误伤成本 > 漏纠)
  4. 改写要有上限(防延迟与噪声爆炸)
  5. 类目慎用硬过滤(预测错就空结果)

1.3 文本清洗

清洗是规则系统,追求稳定、可解释、零误伤:

步骤例子说明
Trim / 合并空白" 红 牛 ""红牛"视业务是否保留中间空格
大小写统一iPhoneiphone英文检索常用
全角转半角A中文输入法常见
标点处理去掉无意义符号保留型号里的 -+
长度截断最长 30~64 字防恶意超长

电商 SKU 字段里型号常含 -+,清洗时不能一刀切去掉。

1.4 纠错

噪声信道模型:(q^* = \arg\max_{c} P(c) \cdot P(q \mid c))

方法中文场景英文场景局限
编辑距离通用通用候选爆炸
拼音纠错核心(蓝芽→蓝牙)不适用多音字
键盘距离核心(teh→the)移动端差异
语言模型整句纠错整句纠错计算贵
日志共现补充信号补充信号冷启动无日志

工程流水线:检测 → 候选生成 → 多信号打分 → 阈值决策。误纠通常比漏纠更伤——品牌、型号、SKU 词进保护表。

1.5 改写

用户搜「纯奶」,标题可能是「新鲜纯牛奶 200ml」。改写产出 ["纯奶", "纯牛奶", "牛奶"],供下游多 Query 召回。

来源覆盖率噪声风险适用阶段
同义词典冷启动、核心品类
删除改写长尾口语 Query
日志挖掘有流量后
向量近邻需人工审核
LLM 生成很高很高辅助 + 人工审核

LLM 改写是 2024–2026 的新选项,工业界很少全自动——「牛奶」扩成「奶粉、奶茶、奶牛」的噪声太常见。

1.6 意图与类目

任务输出用途
意图分类找商品/找店铺/找活动路由
类目预测一级/叶子类目召回约束、特征
实体识别品牌、型号、属性结构化召回

硬过滤 vs 软约束:

策略做法风险
硬过滤只保留预测类目商品类目预测错 → 空结果
软约束不删候选,调分或当 LTR 特征可能混入跨类目

类目模型不够准时,优先软约束。「苹果」消歧靠用户历史、多意图召回 + 精排。

1.7 端到端例子

输入: 蓝芽耳机性价比高

1
2
3
4
5
① 清洗 → "蓝芽耳机性价比高"
② 纠错 → 拼音=蓝牙,置信 0.94 → "蓝牙耳机性价比高"
③ 改写 → ["蓝牙耳机性价比高", "蓝牙耳机", "无线耳机"]
④ 类目 → 数码配件 (0.89),软约束
⑤ 分词 → [蓝牙, 耳机, 性价比, 高]

二、召回层:把候选捞进池子

召回决定上限——候选池里没有的东西,精排再强也排不出来。

2.1 召回层目标

[
R(q,u,c) \subset I,\quad |R| = K \ll |I|
]

目标含义
覆盖好物品相关/会转化的进池(Recall@K)
控制噪声池子别太脏
P99 在延迟预算内
容错一路挂了不至于全空

2.2 倒排召回与 BM25

1
2
倒排:term → [(doc_id, tf), ...]
查询:分词 → 取倒排链 → Boolean 合并 → BM25 打分 → TopK

[
\mathrm{BM25}(q,d)=\sum_{t\in q}
\mathrm{IDF}(t)\cdot
\frac{f(t,d)\cdot(k_1+1)}{f(t,d)+k_1\cdot\bigl(1-b+b\cdot\frac{|d|}{\mathrm{avgdl}}\bigr)}
]

词越稀有 IDF 越大;词频加分但饱和;长文档要惩罚。ES 默认就是 BM25,参数调优见 ES 相关度评分

IDF 从 TF-IDF 到 BM25

经典 TF-IDF 的 IDF:

[
\mathrm{IDF}_{\mathrm{TF-IDF}}(t)=\log\frac{N}{n_t}
]

BM25 改用带平滑的 IDF(Lucene 实现):

[
\mathrm{IDF}_{\mathrm{BM25}}(t)=\log\frac{N-n_t+0.5}{n_t+0.5}
]

差异直觉:

  • (n_t \to N)(词出现在几乎所有文档):BM25 IDF → 0,几乎不贡献分——合理,因为无区分度
  • (n_t) 很小时:BM25 IDF 更大,更强调稀有词
  • +0.5 平滑:避免 (n_t=0) 或 (n_t=N) 时分母/分子为 0

倒排查询:Boolean 合并

多 term Query 在倒排层先做集合运算,再对交集/并集候选算 BM25:

1
2
3
4
5
6
7
Query: "蓝牙 耳机"
term "蓝牙" → postings: {d1, d3, d5, ...}
term "耳机" → postings: {d1, d2, d5, ...}

must(AND)→ 交集 {d1, d5, ...} → 仅对这些 doc 算 BM25
should(OR) → 并集,命中 term 数越多分越高
filter → 过滤,不参与打分(库存、类目)

工程含义: AND 召回更准但易空;OR 覆盖广但噪声大。中文短 Query 常用 should + minimum_should_match 折中。

BM25 参数调优直觉

参数调大调小典型症状
(k_1)词频重复命中加分更多词频饱和更快标题堆砌词霸榜 → 降 (k_1)
(b)长文档惩罚更重长度影响更小长标题总排前面 → 升 (b)

ES 默认 (k_1=1.2, b=0.75)。短标题场景(商品名)有时适当降低 (b);长文本场景(内容搜索)可升高 (b)。

编辑距离纠错原理

Levenshtein 距离:最少几次「插入 / 删除 / 替换」把字符串 (a) 变成 (b)。

1
2
蓝芽 → 蓝牙:替换「芽」→「牙」,距离 = 1
iphone → iphoen:交换相邻字符,距离 = 2(或 1 次替换+1 次删除,视定义)

纠错候选生成:对 Query 每个词,在词典/索引词表中找编辑距离 ≤ 2 的候选,再用语言模型或共现频率排序:

[
c^*=\arg\max_c ; P(c)\cdot P(q\mid c)
]

(P(c)) 是候选词先验(词频/热度),(P(q\mid c)) 是「用户本想说 (c) 却打成 (q)」的噪声概率。拼音纠错把「编辑」换成「拼音编辑距离」——「蓝芽」与「蓝牙」拼音相同,距离为 0。

2.3 各路召回算法对比

算法个性化冷启动延迟可解释搜索主路推荐主路
BM25 倒排强(文本)辅助
向量语义弱~中补召
热门/类目热门极低兜底兜底
ItemCF很少
多 Query辅助
双塔兴趣辅助

2.4 BM25 vs 向量:互补而非替代

维度BM25向量
同义词
专名/型号
描述性 Query
延迟
可解释性

最佳实践:两路互补,RRF 融合。 字面召回(BM25)保精确,语义召回(向量)补覆盖。向量细节见 向量检索与 ANN

2.5 UserCF vs ItemCF

维度UserCFItemCF
相似度用户 ↔ 用户物品 ↔ 物品
缓存(离线预计算)
电商场景较少更常见
搜索场景很少作主路相关推荐、兜底

ItemCF 计算示例:

用户 (u) 点击过 ({A, B}),物品相似表 (\mathrm{sim}(A,C)=0.8, \mathrm{sim}(B,C)=0.5),则:

[
\mathrm{score}(u,C)=0.8+0.5=1.3
]

离线可预计算「物品 → TopK 相似物品」表,在线 (O(|I_u|)) 查表累加——适合「看了又看」「买了又买」。

Swing 等改进: 惩罚两个物品被同一批「大众用户」共现带来的虚高相似度,缓解热门物品垄断。

2.6 多 Query 召回

Query 理解产出 (Q_{\mathrm{list}}),对每个 (q_i) 做倒排或向量 TopK,合并去重。价值:扩覆盖——改写词能召到原词漏掉的商品。

成本控制:限制改写数 m、每路 TopK、合并后截断。


三、多路架构与融合

3.1 架构

1
2
3
4
5
6
7
                 ┌─ A 倒排/向量(相关)
Query 理解 ──────┼─ B 多 Query(覆盖)
└─ C 热门补召(防空)

asyncio 并发 gather

RRF 融合 → 去重 → 过滤 → 精排

并发耗时 (T_{\mathrm{parallel}}\approx\max T_i)。单路超时返回空,其它路继续;全部空走全局兜底。Python 实现见 Python 搜推技术栈

3.2 RRF 融合

各路分数不可比(BM25 0~几十、余弦 0~1、销量 0~10 万),RRF 只看排名

[
\mathrm{RRF}(d)=\sum_{i\in\text{lanes}}\frac{1}{k+\mathrm{rank}_i(d)}
]

(k) 常取 60。手算例(k=60):

商品路A名次路B名次RRF排序
d1150.031772
d2210.032521
d330.015873

为什么 RRF 有效?

  1. 尺度无关:BM25 分可能是 12.3,向量余弦是 0.87——直接加权需要归一化且对分布敏感;RRF 只看 rank
  2. 鲁棒:某路分数整体偏高不会 dominate 融合结果
  3. 简单:无需离线标定各路权重(但 LTR 精排仍可进一步学习最优融合)

(k) 的作用: 分母中的 (k) 控制「排名靠后」的衰减速度。(k) 小 → 前几名权重差距大;(k) 大 → 各路排名 10 和 20 差别变小。常用 60 是经验值,可在 20~100 间 AB 调优。

某文档只在一路出现、排名为 (r) 时,RRF 贡献为 (\frac{1}{k+r})。两路都进 Top10 的文档通常融合分更高——多路共识信号。

3.3 融合策略对比

策略优点缺点适用
RRF不依赖分数尺度丢掉分差异构多路首选
Min-Max + 加权可表达路权重需调参同构路
级联延迟低前面路错则全错粗筛 + 精筛

权重需精细控制时,RRF 之后对 Top 候选再做 LTR 精排。


四、典型故障模式与排查

4.1 Query 层:分词与清洗

现象: 索引中有文档,但搜索无结果或结果偏移。

常见原因:

原因说明排查方向
分析器不一致索引与查询分词策略不同对比 index/search analyzer
品牌词切坏「华为手机」被切成单字自定义词典
清洗过度型号中 -+ 被去掉检查 normalize 规则
纠错误伤正确 SKU 被改成热门词检查保护表、纠错阈值

解法: 重建索引并统一 analyzer;品牌/SKU 进自定义词典;清洗规则保留型号特殊字符。

4.2 召回层:覆盖不足

现象: Query 理解输出正常,但候选池为空或缺少明显相关物品。

常见原因:

原因说明排查方向
仅字面召回同义/描述性 Query 漏召加向量语义路
改写未启用用户表述与标题用词不同加多 Query 召回
类目硬过滤预测错导致全空改软约束
单路超时某路挂掉且无兜底检查超时与降级

解法: BM25 + 向量多路互补;无结果时触发语义补召 + 类目热门兜底;RRF 融合后 LTR 精排压噪。

4.3 快速定位:看哪一层

1
2
3
4
有结果但不相关 → 精排 / 机制层
完全无结果 → Query 理解 → 召回 → 过滤规则
有内容却搜不到 → Query 理解(分词)→ 召回(路数)
同义搜不到 → 缺向量路 / 缺改写

五、怎么选召回路

场景问题优先加哪路
关键词搜不准BM25 调权、同义词
同义/描述性差向量语义
措辞多变漏召多 Query
空结果多类目热门补召
要个性化CF / 双塔
新物品多内容/向量,而非纯 CF

设计口诀:每路必须能回答「本路解决哪种失败」。


下一步

Prev:
华为管理法学习笔记(上):战略的本质与落地闭环
Next:
《格鲁夫给经理人的第一课》读书笔记(一):产出导向的管理思维