3071 字
约 10 分钟
17
S13-A07 · 检索相关性工程:把“搜得准”变成可维护的指标

用户说“搜不到”,你打开后台一搜就有;用户说“排得不对”,你看排序也说得过去。这类争论靠演示解决不了,因为双方拿的查询、期望和样本都不一样。相关性工程不是把排序调到所有人满意,而是把它变成可归因、可度量、可回归的机制——否则每次改权重都只是赌一次。


1. 归因四分:先确定问题在哪一层

“搜得准不准”混了四种故障。不做归因就调权重,等于把排序层的补丁打在索引层的病上。

归因 现象 取证手段 责任层
没索引到 全站搜该词无结果,库里确实有 直查存储层文档数 vs 索引文档数 采集/索引链路
词没匹配上 文档在索引里,但查不到 _analyze 对比查询与文档字段的切词 分析器/查询构造
匹配了但靠后 首屏没有,往后翻能看到 取该 query 的完整结果序列定位排名 打分/排序
排序对了没展示 引擎返回正确,页面看不到 对比接口响应与页面渲染 应用层过滤/前端
# 逐层取证模板(参数名按所部署引擎版本核对)
curl -s "HOST/INDEX/_analyze" -H 'Content-Type: application/json' \
  -d '{"field":"title","text":"QUERY_TERM"}'
curl -s "HOST/INDEX/_count" -d '{"query":{"term":{"title.keyword":"QUERY_TERM"}}}'
curl -s "HOST/INDEX/_search?explain=true" -d '{"query":{"match":{"title":"QUERY_TERM"}}}'

四分法把责任分给四个团队:采集、检索配置、排序、前端。省掉归因,所有投诉都落到排序身上,而排序最难自证清白。


2. 指标设计:哪些数能用,哪些数在骗人

相关性没有绝对分数,只有相对变化。指标只用来发现退化。

指标 定义 常见误导
点击率 CTR 有点击的会话 / 有结果的会话 展示位置越好点击越高,与相关性无关
首条点击率 位置 1 被点击占比 用户习惯性点第一条,指标虚高
无结果率 零结果查询数 / 总查询数 可被“兜底返回一堆不相关”刷低
改查率 同一会话内连续改写的比例 用户补充条件是正常行为,不全是差评
{"event":"search","query_raw":"小刀能带上飞机吗","query_norm":"小刀 飞机 携带",
 "result_count":12,"clicked_rank":3,"dwell_ms":8400,"reformulated":false,"trace_id":"T"}

三条纪律:分母写清楚(无结果率的分母是查询数还是会话数);按查询分层看(头部与长尾混着平均等于没说);单一指标不决策(只盯 CTR 会让人把吸睛但无关的结果往上顶)。


3. 离线评测集:从日志采样到分层

没有标注集只能凭感觉上线,感觉的方差常大于改动的收益。评测集不求大,求代表性。

采样来源 典型占比 覆盖什么
头部高频查询 小 回归安全网,改动最先暴露在这里
长尾随机查询 大 真实分布,最容易漏掉的部分
零结果查询 小 召回缺口,改分析器/同义词的靶子
业务指定查询 小 高价值场景,不允许退化
人工构造对抗查询 极小 拼写错误、混合中英、歧义词
-- 分层采样占位:先按频次分桶再抽,避免只抽到头部
SELECT bucket, query_norm FROM (
  SELECT query_norm, CASE WHEN cnt >= HEAD_T THEN 'head'
           WHEN cnt >= MID_T THEN 'mid' ELSE 'tail' END AS bucket,
         ROW_NUMBER() OVER (PARTITION BY bucket ORDER BY RAND()) rn
  FROM query_freq_daily
) t WHERE rn <= QUOTA_PER_BUCKET;

分层采样要分层报告:整体涨 2% 而长尾跌 8% 的改动通常不该上线。评测集建立后冻结版本,只追加,不在改动同时换样本。


4. 相关性标注:分级与一致性

二值标注不够用:它无法表达“能答但不完全对”。常用三级:

等级 含义 判定口径
0 不相关 答非所问 主题不同,或只擦边命中关键词
1 部分相关 沾边、可参考 主题相关但未回答核心意图
2 完全相关 直接回答 首段即可满足查询意图

一致性不是可选项:两人一致率七成时,任何 2% 的指标变化都是噪声。

# 一致率与 kappa(占位实现,用于标注培训后自检)
def kappa(a, b):
    n = len(a); po = sum(x == y for x, y in zip(a, b)) / n
    pe = sum((a.count(l) / n) * (b.count(l) / n) for l in set(a) | set(b))
    return (po - pe) / (1 - pe) if pe < 1 else 1.0

顺序是:写标注手册 → 两人标同一小批 → 算 kappa → 对齐分歧 → 再批量标。跳过培训直接标,标注集本身就是错的。


5. BM25 之外:加信号的收益与侵蚀

BM25 只回答“文本像不像”,业务要的是“该给谁”。加信号能改善头部效果,代价是可解释性下降。

信号 收益 侵蚀风险 施加位置
时效衰减 新闻/公告明显 常青内容被压死 查询期打分
热度 优质内容上浮 强者更强,新内容永无曝光 查询期或离线分
用户画像 个性化明显 同查询不同人不同结果,难复现 应放在重排层
业务权重 商业目标可调 堆叠后无人说得清排序理由 配置化+版本化
{
  "function_score": {
    "query": { "match": { "title": "QUERY_TERM" } },
    "functions": [
      { "gauss": { "publish_at": { "scale": "30d", "decay": 0.5 } }, "weight": W_TIME },
      { "field_value_factor": { "field": "hot_score", "factor": 1.0 }, "weight": W_HOT }
    ],
    "score_mode": "sum",
    "boost_mode": "multiply"
  }
}

判断信号该不该留,问三句:它解决哪些具体 badcase?去掉退回什么状态?权重谁维护? 答不上来的,两次迭代后没人敢动。


6. 查询理解:收益排序

同义词、纠错、改写、意图分类不是并列选项:收益从高到低,代价递增。按顺序做能用最小改动拿到大部分提升。

手段 典型收益面 主要代价 建议顺序
同义词/别名词典 高:术语、缩写、俗称 词典维护成本,易过宽 1
拼写纠错 高:错字、拼音混输 误纠会把正确词改坏 2
查询改写 中:口语转化为检索式 需要日志与规则/模型 3
意图分类 中:路由到不同索引 分类错则整块结果错 4
向量召回兜底 中:语义相近表达 成本与噪声,需要阈值 5
# 同义词(格式按所部署引擎版本核对):=> 表示单向展开
synonyms: ["服务器, 主机, server", "报错, 异常, error => 错误"]

同义词最容易犯的错是双向过宽:苹果与水果互相展开,查“苹果手机”时水果内容全被拉进来。规则应单向、带条件、可回滚。


7. badcase 收集与回归

badcase 是相关性工作的燃料,收集入口要低摩擦。

badcase 最小字段
query    原始查询        expect  期望命中的文档标识
got      实际前 3 条     note    为什么判错(对应第 1 节四分)
by/date  谁何时报的      status  open / fixed / wontfix

回归纪律:每次改动跑同一集合,报全量结果而非“已修复”。输出用固定列表格,方便 diff:

python eval.py --set EVAL_SET.json --endpoint HOST --out result.csv
diff -u baseline.csv result.csv | head -50   # 只看三类:通过/回归/提升

健康的 badcase 库是“陈旧条目多、新增也有”。长期只增不消化,说明收集与修复脱节。


8. AB 实验在搜索场景的特殊性

搜索 AB 比推荐、广告都难,原因在分布:

特殊性 后果 应对
查询分布长尾 单 query 样本量小,方差大 按查询聚类分组后再比
指标滞后 点击在会话中后段才发生 观察完整会话窗口,不切流即比
会话内污染 同用户跨组体验不一致 按用户/会话哈希分流
冷启动样本 新 query 无历史基线 单独看,不混入总体

按请求分流是最常见的错误:同一用户两次搜索命中不同版本,归因就乱。分流单位不小于观测单位(会话级观测 → 用户级分流)。


9. 冷启动与长尾查询

长尾查询的共性是每个词样本都不足。对策不是提升排序精度,而是减少空结果:

  • 降级策略:无结果时依次退到 AND 放宽为 OR、字段放宽、同义词扩展,每次降级记日志,用于建定向词典;
  • 兜底展示:明说“没有完全匹配,以下是相近内容”,而不是静默返回无关项;
  • 长尾单看:长尾改善常不体现在总 CTR 上,只看总指标会把长尾优化判为无效。

10. 排错:相关性下降的常见原因

现象 可能原因 核查点
某天起整体变差 索引重建/分析器改动 mapping 与分析器是否同步变更
只劣化部分词 权重堆叠或同义词过宽 最近一次配置 diff
首屏正常翻页异常 深分页/重排截断 重排窗口是否覆盖该位次
结果数量骤减 过滤器条件收紧 查询模板里的 filter 是否含新增条件
结果重复 多索引/别名叠加 别名是否指向了同一份数据的多个索引
用户搜不到你搜得到 权限或登录态过滤 同身份复现,查过滤链

核查顺序固定:索引与文档数 → 分析器 → 打分。 反过来查,常在权重里绕很久才发现少了个字段。


11. 边界:什么情况本就不该做相关性工程

场景 判断 替代动作
查询量太小 没有统计意义,指标不可读 只做 badcase 清单人工看
强结构化查询(ID/编号) 精确匹配足够 不引入打分与改写
内容本身质量差 排序救不回来 先治理内容与采集覆盖
结果集很小且检索是唯一入口 排序收益低于展示改造 优化筛选与分类导航
团队无人维护词典与标注 机制会腐化 明确负责人再启动

天花板由内容覆盖与评测机制决定:内容缺的排不上去,没有评测的改了也不知道。


12. 自测

  1. 用户投诉“搜不到”,你的第一条取证命令查什么?
  2. 无结果率下降了但用户投诉变多,最可能是哪种掩盖?
  3. 标注一致性只有七成时,2% 的指标提升能不能作为上线依据?
  4. 同义词为什么建议单向配置?
  5. 搜索 AB 实验为什么不该按请求分流?
  6. 长尾查询改善不明显体现在总 CTR 上,你应该另看什么?
S13-A07 · 检索相关性工程:把“搜得准”变成可维护的指标
https://www.clxhxhhr.top/posts/6104/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。