Appearance
L01-06 多轮对话与 Memory 持久化
全局中心内容:策略层和存储层分开——存多少由 ChatMemory 定,存在哪由 Repository 定。 全局讲解主线:手写历史会怎样 → 三层结构 → 做出持久化 → 重启证明不失忆。
P1 · 产出页
中心内容:重启应用,用户刚才说的话还在。
页面内容
- 同 conversationId 下能引用前文
- 重启后记忆仍在(JDBC)
- 可裁剪,不会撑爆上下文
讲解技巧
- 先抛一个反常识的事实:「大部分人做的 AI 应用,重启一次就失忆」。让学员自查有没有这个问题。
- 结尾预告验收方式:重启后问「我叫什么」——这是本节唯一的硬指标。
时长:20s
P2 · 痛点页:手写历史的三个坑
中心内容:重启即失忆,是生产事故不是小毛病。
页面内容
- 不知道截多少 → 太短丢上下文,太长爆窗口
- 无法区分角色 → 用户说的和模型说的混在一起
- 重启即失忆 → 滚动更新后全部对话失忆
讲解技巧
- 第三个坑要讲透后果:「用户不会认为这是部署问题,只会认为你的产品很蠢」。这句话很有冲击力。
- 类比迁移:这跟把 HttpSession 放内存里,一发版用户全掉线是同一个错误。
时长:2min 30s
P3 · 原理图:三层结构
中心内容:存多少(策略)和存在哪(存储)是两件事。
页面内容
- ChatMemory:MessageWindow / Token 预算
- ChatMemoryRepository:内存 / JDBC / Redis
- 换存储不改策略
讲解技巧
- 黑板留这张图:三层框,中间画一条横线分开「策略」和「存储」。
- 类比:「这就是 Spring 里事务策略和数据源分离的思路」。对 Java 工程师,这个类比一次讲透。
时长:3min
P4 · 风险页:conversationId
中心内容:不传 conversationId,所有用户共享一份记忆。
页面内容
- 全局唯一且不可预测(UUID,不用自增 ID)
- 必须校验归属,否则是越权
- 不传会落到默认值 → 所有人混在一起
讲解技巧
- 这是本课最重要的安全点。用「A 用户看到了 B 用户的对话」这个场景讲,冲击力足够。
- 点出为什么本地测不出来:本地只有一个用户。这句话能救很多人的线上事故。
时长:3min
P5 · 选型表:三种裁剪策略
中心内容:窗口 + Token 预算双保险。
页面内容
- 窗口:保留最近 N 条,简单但丢早期信息
- Token 预算:不超窗口,实现稍复杂
- 摘要压缩:保留语义但花钱费时
讲解技巧
- 明确推荐双保险:窗口控条数、预算控总量。给具体数字(20 条 + 4000 Token)。
- 说明摘要压缩为什么不在这里讲:它需要额外的模型调用,属于 03C 的长程记忆话题。
时长:2min
P6 · 编码:Bean 装配
中心内容:换存储只换 Repository,策略不变。
页面内容
JdbcChatMemoryRepository.builder().jdbcTemplate(...)MessageWindowChatMemory.builder().maxMessages(20)initialize-schema: always
讲解技巧
- 可视化反差:把 InMemory 版和 JDBC 版的代码并排——只有 Repository 那一行不同。
- 这就是 P3 那张图的代码兑现,讲的时候回头指一下黑板。
时长:5min
P7 · 编码:挂 Advisor 与传参
中心内容:Advisor 顺序错了不报错,但效果不对。
页面内容
MessageChatMemoryAdvisor.builder().chatMemory(...).advisors(a -> a.param(ChatMemory.CONVERSATION_ID, convId))- 顺序:记忆在检索之前,在审计之后
讲解技巧
- 给出记忆口诀:审计在前、记忆在中、成本在后。
- 强调这类 bug 的可怕之处:不报错、不崩溃,只是效果不对。排查成本极高。
时长:5min
P8 · 编码:会话归属校验
中心内容:越权校验必须写在 Controller 里。
页面内容
conversationService.belongsTo(convId, auth.getName())- 新建会话返回 UUID
- 不给 conversationId 默认值
讲解技巧
- 先翻车:注释掉校验,用两个不同用户的 token 互相访问会话,成功读到对方内容。这个演示极其震撼。
- 强调「不要给默认值」——给默认值的代码本地永远测不出问题。
时长:4min
P9 · 运行与重启验证
中心内容:重启后还能答出「木鱼」,才算通过。
页面内容
- 第一轮:我叫木鱼
- 第二轮:我叫什么 → 木鱼
- 重启应用 → 再问 → 仍是木鱼
- 换 convId → 答不知道
讲解技巧
- 重启验证必须现场做,而且要真的 Ctrl+C 再启动,不能用热部署糊弄。这一刻是本节的高潮。
- 顺带测窗口:聊满 20 条后问最早的信息,验证裁剪生效。
时长:4min
P10 · 避坑与小结
中心内容:记忆表没 TTL 会无限膨胀,上线前就要处理。
页面内容
- 不传 conversationId = 全用户共享记忆
- 记忆里有敏感信息,落库前要脱敏
- 无 TTL 的记忆表会膨胀到千万行
讲解技巧
- 第三条强调「必须在上线前做」:等表长大了再清理,代价高十倍。
- 结尾悬念:「窗口裁剪会丢掉早期的关键约束,比如用户第一句说『全程用英文』——怎么不丢?」引出 03A 状态管理。
时长:2min
讲师备忘
| 项 | 内容 |
|---|---|
| 课前必做 | 准备两个用户的 token 做越权演示;确认 H2/Postgres 建表语句可用 |
| 最容易超时处 | P5 的裁剪策略,容易展开讲摘要压缩——收住,说「03C 讲」 |
| 学员最常问 | 「生产到底用 Redis 还是数据库?」答:高频临时数据用 Redis,需要留档再异步落库 |
| 现场备用 | 数据库连接失败 → 切 H2 内存版继续讲主线,课后再排查 |