# Thread Memory 手动测试清单

日期：`2026-05-08`  
测试人：`__________`

---

## 0. 前置检查

- [ ] 已拉取包含以下修复的最新代码并重启后端进程
  - `memory.enabled=false` 时仍允许 `thread_memory` 更新
  - `thread_prompt` 的 JSON 模板转义修复（避免 `KeyError: "profile"`）
  - `thread_updater` 使用非流式安全参数（避免 `stream_options` 400）
- [ ] `config.yaml` 中已启用 `thread_memory.enabled: true`
- [ ] 确认使用的是预期配置文件（当前项目根目录 `config.yaml`）

---

## 1. 基础写入与读取

前置条件：
- 选择一个新的 `thread_id`（例：`1f571481-e3ae-42b5-a513-945bf8f1cbef`）

步骤：
1. 在该线程发送 2-3 轮消息，包含姓名、角色、偏好语气等信息  
2. 等待 `debounce_seconds`（默认 30 秒）  
3. 查询 `thread_memory` 表

期望：
- 出现该 `thread_id` 记录
- `profile/preferences/facts` 有对应内容

结果：
- [1] 通过
- [ ] 失败（备注：`________________`）

---

## 2. Per-Thread 隔离

前置条件：
- 准备两个线程 `thread_A`、`thread_B`

步骤：
1. 在 A 中输入“前端背景”信息  
2. 在 B 中输入“后端背景”信息  
3. 分别等待写入完成后查看两条记录

期望：
- A 仅保存 A 的画像，B 仅保存 B 的画像
- 两个线程不串数据

结果：
- [1] 通过
- [ ] 失败（备注：`________________`）

---

## 3. 全局记忆 Fallback

前置条件：
- 全局 memory 有内容
- 新建一个尚无 per-thread 记录的线程

步骤：
1. 先在该新线程发一轮普通消息  
2. 观察回复是否体现全局记忆  
3. 再继续对话触发 per-thread 写入后观察注入变化

期望：
- 无 per-thread 时可 fallback 到全局
- 有 per-thread 后优先使用 per-thread

结果：
- [ ] 通过
- [ ] 失败（备注：`未执行（N/A）：当前环境 memory.enabled=false，全局记忆关闭，本用例不适用`）

---

## 4. 注入裁剪优先级（Profile > Preferences > Facts）

前置条件：
- 某线程已有大量 facts

步骤：
1. 人为积累 facts 到接近/超过注入预算  
2. 保持 profile/preferences 有值  
3. 观察注入后的表现

期望：
- 超预算时保留 profile + preferences
- 优先裁剪 facts

结果：
- [1 ] 通过
- [ ] 失败（备注：`________________`）

---

## 5. 敏感信息过滤

步骤：
1. 在对话中输入邮箱、手机号、token/password 等敏感样例  
2. 等待写入后查库

期望：
- 敏感信息不应落入 `profile/preferences/facts`

结果：
- [1] 通过
- [ ] 失败（备注：`________________`）

---

## 6. 并发覆盖保护（CAS + version）

步骤：
1. 同一 `thread_id` 短时间内触发两次更新（尽量并发）  
2. 观察最终数据与日志

期望：
- 不出现明显“旧数据覆盖新数据”
- 冲突时可见重试行为（日志）

结果：
- [1] 通过
- [ ] 失败（备注：`________________`）

---

## 7. Debounce 生效

步骤：
1. 在 30 秒内连续发送多条消息  
2. 观察写库频率

期望：
- 多条输入被合并处理，不是每条都立即写库

结果：
- [1] 通过
- [ ] 失败（备注：`________________`）

---

## 8. 线程删除联动清理

步骤：
1. 对已有 per-thread 记录的线程调用 `DELETE /api/threads/{thread_id}`  
2. 查询 `thread_memory` 表

期望：
- 对应 `thread_id` 记录被删除

结果：
- [ ] 通过
- [ ] 失败（备注：`未执行：当前产品决策不接受“删线程即删记忆”，需改为用户显式触发清除后再复测`）

---

## 9. SQLite 自动建表与路径

步骤：
1. 删除现有 `thread_memory.db`（测试环境）  
2. 重启服务并触发一轮写入  
3. 检查 DB 文件和表结构

期望：
- 自动创建 DB 文件与 `thread_memory` 表
- 索引 `idx_thread_memory_owner_id` 存在

结果：
- [1] 通过
- [ ] 失败（备注：`________________`）

---

## 10. 配置开关验证

步骤：
1. 关闭 `thread_memory.enabled`，重启并测试写入  
2. 开启 `thread_memory.enabled`，关闭 `thread_memory.injection_enabled`，重启并测试注入

期望：
- `enabled=false`：不更新 per-thread
- `injection_enabled=false`：不注入 per-thread（可 fallback）

结果：
- [1] 通过
- [ ] 失败（备注：`________________`）

---

## 11. 已知错误回归验证

### 11.1 `KeyError: "profile"` 回归
- [ 1] 未再出现 `thread_prompt.py` 的 `KeyError` 报错

### 11.2 `stream_options` 400 回归
- [ 1] 未再出现 `"'stream_options' only set this when you set stream: true"` 报错

备注：`________________`

---

## 测试总结

- 总用例数：`11`
- 通过数：`____`
- 失败数：`____`
- 结论：
  - [ ] 可上线
  - [ ] 需修复后复测
