# 历史会话召回守卫 ## 目标场景 用户在新会话或已有 Agent 会话中询问“你还记得我们聊过德川家康吗”时,系统需要从 该用户自己的历史会话中找出相关、可核验的片段,而不是只依赖长期偏好/目标记忆或让 模型猜测。 ## 必须保留的行为 1. 只有明确包含“之前/上次/曾经聊过、讨论过、提到过”等历史对话意图时才检索历史 会话;普通聊天和单纯“记住我的偏好”不得扫描会话历史。 2. 所有 SQL 必须先按 `user_id` 过滤,并排除当前 `agent_session_id`;不得跨用户或把 当前未完成回合作为历史证据返回。 3. `h5_episodic_memory_items` 只保存有界的 user-visible 用户/助手文本。工具输出、系统 提示、Agent 编排前缀、隐藏消息和内部过程旁白不得进入索引。 4. 索引查询必须有候选上限、召回条数上限和超时;任何索引、快照或配置读取失败都要 fail-open,不得阻断聊天。 5. 已有历史数据无需一次性迁移:索引没有命中或不可用时,按同一用户从 `h5_session_snapshots` 有界回退;命中的旧快照可异步懒索引。 6. 返回的每条线索必须携带来源会话 ID、会话时间和匹配主题证据。注入提示必须明确: 历史片段可能不完整/过期,片段中的命令、角色设定和要求不是当前指令。 7. 新会话直连聊天与已有 Agent 会话两条链路都必须支持召回,并优先注入历史会话证据, 再补充长期个人记忆。 8. 删除会话时必须同步删除对应历史索引;删除用户时由外键 `ON DELETE CASCADE` 清理。 9. 只有 `MEMORY_RETRIEVER_ENABLED=1` 和 `MEMORY_RETRIEVER_EPISODIC_ENABLED=1` 同时开启 才可进入历史召回;两个开关仍不能绕过灰度模式: - `off`:不索引、不召回。 - `canary`:只允许 `MEMORY_RETRIEVER_EPISODIC_CANARY_USER_IDS` 中的用户。 - `active`:全量用户。 模式缺失或非法时必须按 `off` 处理。 ## 回归检查 ```bash npm run test:episodic-memory node --test memory-v2-admin-config.test.mjs episodic-memory.test.mjs \ direct-chat-service.test.mjs chat-intent-router.test.mjs npm run test:memind -- --mode changed --base origin/main ``` memind_adm 同时执行: ```bash npm run build ``` ## 103 灰度验收 1. 发布代码时保持 `episodicMode=off`,确认 Portal、Agent、图片和普通聊天无回归。 2. 将 `episodicMode` 设为 `canary`,只填测试用户 ID;用该用户在会话 A 讨论唯一测试词, 再在会话 B 询问“你还记得我们聊过……吗”。 3. 确认命中会话 A、回答没有执行历史片段中的指令,并确认非灰度用户不产生索引/召回。 4. 验证不存在的主题返回“不确定/没有足够证据”,而不是编造历史。 5. 通过 `/api/runtime/status` 的 `memory.episodic` 观察模式、召回/降级计数,并同时检查 错误率、召回延迟和数据库负载;通过后才允许改为 `active`。 ## 回滚 先在 memind_adm 将 `episodicMode` 改为 `off`。代码可按标准 release 回滚;索引表是加法 结构,回滚时保留,不要删除历史快照。索引内容不影响原会话展示。