M5 · 跨会话偏好记忆与个性化#
简历 Bullet Point: 跨会话偏好记忆系统:独立记忆管家(curator)会话结束后统一判定与矛盾消解,LLM 只提供 slug,去重键由代码从 polarity/category/domain/slug 确定性派生;like 走偏好词并入检索词、dislike 走关键词双档(hard 淘汰仅限用户授权 / soft 减分含 Attenuator 语义路)+ 行为亲和(收藏→弱加分);三层记忆分治(长期 Store / 会话级 P_t / 行为历史)
开场钩子#
用户第一轮说”不要塑料的”,item_picker 正确淘汰了塑料商品。第二轮(新会话)用户说”推荐旅行包”——结果里出现了塑料材质的包。“不要塑料”在上一次会话里,这次没有了。
这逼出了长期记忆系统:偏好必须跨会话持久化。
一、模块运作流程#
1.1 三层记忆分治#
| 层 | 存储 | 生命周期 | 写入时机 |
|---|---|---|---|
| 长期偏好 | PreferenceStore(SQLite 默认) | 永久 | curator 会话结束后扫 |
| 会话级 P_t | SessionState | 单次会话 | planner/picker 逐轮更新 |
| 行为历史 | turns.json | 单次会话 | 每轮追加 |
1.2 记忆 Schema#
PreferenceEntry 四个结构化字段:
polarity: like / dislike(封闭枚举)category: brand / material / style / price / feature / general(封闭枚举)domain: 自由文本(如 “travel_bag”)slug: LLM 提供的原子标识(如 “no_plastic”)dedup_key: 由polarity_category_domain_slug代码派生,不手拼
SessionState (P_t) 五个结构化字段:
current_intent/slots/rejected_options/open_questions/decisions_maderejected_options由 item_picker 机械灌入(不经 curator 猜测)
1.3 记忆管家 curator#
会话结束后独立运行,扫本轮对话提取偏好。核心原则:
- LLM 只负责识别偏好和提供 slug,去重键由代码派生
- 矛盾消解:新偏好与已有偏好极性冲突 → 覆盖并重置 confidence
- 同义复述命中去重键 → 覆盖 + 提置信
1.4 偏好执行双通道#
like 通道:偏好词并入检索词(planner 输出的 prefer_keywords 拼进 query 一起编码),不再做向量级 user 塔融合(实践中零调用、按减法原则移除)
dislike 通道:
dislike-hard(仅source="user"且blocking=True):item_picker 确定性淘汰(材质/标题含关键词 → 直接删),中文原子词跨语言匹配dislike-soft(agent 学到的 / 弱表达):Attenuator 减分但不淘汰,含关键词命中路 + 语义路
授权分档核心决策:硬淘汰权只由用户显式授予,agent 学到的一律只减分——双重校验 is_blocking = blocking and source == "user"
1.5 偏好注入分层#
dislike-hard 永远全量注入 prompt(安全类不能漏),其余超阈值走语义 top-k。
二、踩坑实录#
坑 1:embedding 对否定编码弱#
- “不要塑料”和”要塑料”向量距离很近。dislike 不能走向量通道,改关键词硬过滤。
坑 2:手拼 key 导致去重失效#
key手动拼接容易拼错或不一致。改成dedup_key由代码从四个结构化字段派生。
坑 3:shopping_summary 写偏好被 curator 取代#
- 早期 summary 的
new_preferences直接落库。改成 curator 统一负责,summary 不再写偏好。
坑 4:中文硬约束跨语言失效#
- “塑料”匹配不到英文”plastic”。加中文原子词→英文关键词映射。
三、面试问答#
Q1: 为什么 LLM 只提供 slug 而不是完整 key?#
LLM 生成的 key 不稳定——同一偏好可能产出不同 key。slug 是最小原子标识,dedup_key 由代码从 polarity/category/domain/slug 确定性派生,不依赖 LLM 一致性。
Q2: 为什么 like 和 dislike 走不同通道?#
embedding 对否定编码弱——“不要塑料”和”要塑料”距离近。like 走偏好词并入检索词(拼进 query 文本编码),dislike 走关键词匹配(硬淘汰或 Attenuator 减分)。两通道完全分离。向量级 user 塔融合原先做了但实践中长期零调用,已按减法原则移除。
Q3: rejected_options 为什么不经 curator?#
item_picker 精确知道哪些候选被淘汰了、因为什么原因。机械灌入不需要 LLM 猜测。
四、诚实边界#
| 维度 | 做了 | 没做 |
|---|---|---|
| 长期存储 | SQLAlchemy/DB 统一后端(SQLite/PostgreSQL) | 分布式存储 |
| 偏好提取 | curator 单次 LLM 调用 | 多轮对话连续追踪 |
| 个性化 | 偏好词并入检索 + 行为亲和(收藏→弱加分) | 向量级 user 塔融合(已删,零调用) |
| 跨语言 | normalize_terms 中英归一 + term_hits 词边界判定 | 完整多语言同义词库 |