M-检索层 · 对齐 RecBot 消费侧:语义 Matcher/Attenuator + 正负软硬路由 + 记忆作用位置 —— 开发文档(面试向)#
记忆层此前参考了 RecBot(arXiv:2509.21317) 的 Parser→P_t、Filter/Attenuator,但检索层没有。本篇把 论文 §3.3 的 Filter / Matcher / Attenuator / Aggregator 四工具落到 item_picker,补上 embedding 语义打分,并系统回答一个更本质的问题:{正/负}×{硬/软} 偏好、长期 vs 短期记忆,各自该在「召回」还是 「精挑」起作用。 讲设计与边界,不讲代码。
一句话概括#
论文的推荐打分是 Filter→Matcher&Attenuator→Aggregator 四工具,正/负偏好都用 embedding 相似度打分
(正向加、负向减)。我们此前只做到「负向靠关键词过滤、正向靠 user 向量召回」,soft 打分是关键词字面
命中、抓不到语义近邻。本篇把 item_picker 的四段打分显式对齐论文四工具,正/负软/正硬都补一路
BGE-M3 语义打分,并确立一条贯穿全系统的路由原则:召回管「谁进漏斗」,精挑管「漏斗内怎么排 + 硬门」;
长期记忆倾斜召回、短期约束在精挑 enforce。
1. 核心原则:一条偏好放召回还是精挑?#
| 阶段 | 在什么集合上跑 | 决定什么 | 判据 |
|---|---|---|---|
| 召回 (item_search + Qdrant) | 全库(百万级) | 哪些候选被 surface 进来(coverage) | 偏好改变的是**「谁有资格出现」** → 放召回 |
| 精挑 (item_picker) | 小候选集(≤100) | 怎么排序 + 硬门淘汰(ranking/enforce) | 偏好改变的是**「已 surface 的怎么排/淘汰」** → 放精挑 |
很多偏好两头都要(召回先 surface、精挑再 refine/enforce)。这条原则是下面所有路由决策的总纲。
2. 架构决策图#
2.1 两阶段数据流(= 论文四工具的落点)#
┌──────────── 偏好来源 ────────────┐
│ 长期库(一贯取向) · 会话P_t(本轮) · 本轮planner(当前意图) │
└───────────────┬────────────────┘
▼
┌─────────── 召回 item_search(决定谁进漏斗)───────────┐
│ query 向量 = 本轮正向意图(planner keywords) │ ← α 语义通路
│ user 向量 = 长期 like 画像(β 加权、时间衰减) │ ← β 个性化通路(负向绝不进)
│ payload filter = 结构化硬约束(价 / 评分 / 平台) │ ← Filter(结构化)
└───────────────────────┬──────────────────────────────┘
▼ top-N 候选(≤100)
┌────────── 精挑 item_picker(四工具打分 + 硬门)──────────┐
│ Filter 负硬 → exclude 命中淘汰 + budget 超预算淘汰 │ −∞
│ Matcher 正硬(must,高权重) + 正软 → 关键词命中 + 语义 +W·sim │ 加分
│ Attenuator 负软 → 关键词命中 + 语义 −W·sim(独立减分项) │ 减分
│ Aggregator + 便宜度 + 评分 → 加权求和 → 排序取 top-K │
└───────────────────────────────────────────────────────┘plaintext2.2 {正/负}×{硬/软} 路由决策矩阵#
| 格子 | 召回(coverage) | 精挑(ranking/enforce) | 为什么这么放 |
|---|---|---|---|
| 正·硬(必须金属) | query/user 向量高权重 bias | Matcher must 高权重加分(关键词 + 语义),不淘汰 | 自由文本无结构化字段 → 做不了可靠硬 filter(keep-only 误杀真金属款);向量能做加法 |
| 正·软(喜欢精致) | 长期→user 向量;本轮→query | Matcher 关键词命中 + 语义 +W·sim | 正向能进向量(加法有效) |
| 负·硬(绝不塑料) | 结构化→payload filter;自由文本→不进 | Filter exclude 命中即淘汰(−∞) | 负向绝不进召回向量;硬 = 二值淘汰(自由文本负向 exclude 安全) |
| 负·软(尽量避免花哨) | 不进向量 | Attenuator 关键词命中 + 语义 −W·sim(独立减分项) | 负向只能做减法;语义减分抓「塑料感」这类字面漏网近邻 |
2.3 长期 vs 短期记忆的作用位置#
| 记忆层 | 是什么 | 主战场 | 为什么 |
|---|---|---|---|
| 长期记忆(Store,一贯取向) | 用户跨会话稳定画像 | 召回(user 向量倾斜 + 结构化硬 filter) | 该预先倾斜「surface 什么」——否则符合口味的好商品可能根本没召回进来,精挑无米下锅 |
| 短期 P_t(会话,本轮约束) | 这一次购买的具体约束 | 精挑(Filter 硬门 + Matcher/Attenuator refine)+ 召回 query(本轮意图本就是 query) | 该在小候选集上确定性 enforce;本轮正向意图已经是 query,召回自然带上 |
3. 三条关键设计决策(面试高频)#
① 正向能全程进向量,负向永不进召回向量。 α·query + β·user 是加法——正向偏好把请求向量拉向目标区,
有效。把「不要塑料」塞进召回向量会把向量推向「塑料」区、反召回更多塑料(embedding 对否定算子编码极弱、
对主题词极强)。所以负向只两条路:Filter 硬淘汰(hard)或独立减分项 Attenuator(soft,−W·sim 减不加、
不进正向向量)——这才是负向「用 embedding」的唯一正确姿势。
② 硬/软的区别:结构化用 filter、自由文本用权重强度,不用「淘汰 vs 加分」硬掰。 负向硬 exclude 是安全的
(标题写了 plastic 就是塑料,删对);正向硬 keep-only 是危险的(标题没写「金属」≠不是金属 → 误杀 → 空结果)。
所以自由文本正向硬(must_have)用更高 Matcher 权重(_W_MATCH_HARD=2.0 vs 软 _W_PREF=1.0)表达「硬」,
强力上浮但不淘汰——用强度差而非二值 drop 表达硬软之别,无空结果风险。可枚举的结构化硬约束(价/评分/
平台)才走真正的二值 filter(Qdrant payload)。
③ 长期倾斜召回、短期精挑 enforce,是「记忆用途」决定的分工,不是缺陷。 长期是「你这个人」(该 bias coverage),短期是「你这次」(该 enforce ranking)。所以正向通道「按来源分裂」(长期 like 在召回 user 向量、 本轮/会话在精挑 Matcher)是对的:讲的时候说「正向影响召回靠长期画像、影响精挑靠本轮 Matcher」即自洽。
4. must_have:正向硬的落地#
给 item_picker 加 must_have 入参(自由文本正向硬,如 ["金属"]):
- 关键词路:命中
+_W_MATCH_HARD(比软偏好_W_PREF重一倍)。 - 语义路:对
must意图算候选 cosine,命中+_W_MATCH_HARD_SEM(比软语义_W_MATCH_SEM重)。 - 不淘汰不匹配的:强力上浮匹配项,但浮不上来也保留——避免自由文本正向 keep-only 的空结果风险。
- 机制路由:会话 P_t 的
like-hard约束自动并入must、like-soft并入prefer(对称于 dislike 的 hard→exclude / soft→attenuate),续聊「上一轮说必须金属」确定性生效、不靠模型每轮转述。
5. 消融标定(scripts/ablation_semantic_pick.py)#
方法:库是英文标题、用户用中文偏好词 → 关键词天然跨语言失配(「压缩」匹配不到「Compression」),
正是语义该发力处。用确定性规则标注(英文属性词在标题里=满足,完全可复现,不用 LLM judge——judge
在薄库上标注不稳)在真实 Qdrant 候选上跑,扫语义权重,用 eval/recall_metrics 的 Recall/NDCG/MRR 量化。
结果(5 场景,确定性 gt):
| 配置 | Recall@10 | NDCG@10 | ΔNDCG |
|---|---|---|---|
| 关键词基线(跨语言失配→偏好盲排序) | 0.590 | 0.451 | — |
| +语义 0.5 | 0.619 | 0.510 | +0.059 |
| +语义 0.8(NDCG 峰) | 0.619 | 0.523 | +0.071 |
| +语义 1.2 | 0.648 | 0.520 | +0.069 |
- 语义确实正向:NDCG@10 提升 +0.04~+0.07,NDCG 峰约 W=0.8;据此把默认权重标定为 0.7(捕获多数 增益、又不过拟合 n=5 噪声)。
- 诚实明细:净正向但非每场景都赢——压缩/must 0.71→0.86(帮),鞋袋 0.86→0.71(伤);判别力温和、 是细粒度微调而非强信号。BGE-M3 对商品文本相似度挤在 0.45–0.65 窄带 → 均匀基线对排序无害(只平移不改序)。
- 规则 gt 的边界:只覆盖「属性写进标题」的跨语言命中,漏掉「是压缩款但标题没写」的——那类要人工 / Amazon ESCI 数据集标注(待办)。故此消融量化的是跨语言命中这一主要增益,非全部。
6. 诚实边界(续)#
- AIA 协同路不做:论文 Matcher 的第二路(Active-Intent-Aware sequential recommender + 多头交叉注意力) 需 GPU + 用户行为序列 + 多模态编码器,与本项目 no-GPU / 无行为数据边界冲突。只借语义路。诚实标注。
- 正向硬非硬保证:自由文本正向硬是「强力上浮」不是「保证在结果里」——数据无结构化材质字段,做不到 硬保证。只有结构化硬(价/评分/平台)是真硬门。
- 长期 like 只倾斜召回、不进精挑 Matcher:刻意——避免与召回 user 向量双重计数(召回已把长期口味的 商品排前,精挑再加会过度放大)。长期只管召回、本轮只管精挑,边界更干净。
7. 验证#
- 单测:语义 Matcher 加分近邻上浮 / Attenuator 减分近邻下沉(用假 tower 确定性构造);must_have 高权重 且不淘汰、P_t like-hard→must 路由;编码失败降级纯关键词;无软意图门控(不编码省延迟)。
- 真实 BGE-M3 跨语言验证:负向意图「塑料/廉价」对英文「cheap plastic」标题 cosine 0.53 > 金属 0.45; 正向意图「金属精致」对金属款 0.68 > 塑料/帆布 0.40。排序均符合预期、中文意图能作用于英文标题。
- 消融(见 §5):确定性 gt 上 NDCG@10 +0.04~+0.07,据此标定权重 0.7。
- 387 单测全过、改动文件 ruff/mypy 零新增违规。
8. 一句话总纲#
召回决定谁进来(长期画像倾斜 + 本轮 query + 结构化硬门),精挑决定怎么排和硬门(负硬淘汰 + 正硬强上浮 + 正负软语义 refine)。正向能全程做向量加法,负向只能在精挑做减法、永不进召回向量。硬软的区别,结构化用 filter、自由文本用权重强度,不用「淘汰 vs 加分」硬掰。