第八章 持久化会话与 Redis 内存管理
版本标注
- Spring AI:
1.1.2- Spring AI Alibaba:
1.1.2.0
s01 > s02 > s03 > s04 > s05 > s06 > s07 > [ s08 ] s09 > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > s18
"会话一旦能记住历史, 聊天才真正像聊天" -- Redis 负责的不是回答, 而是上下文连续性。
⚠️ 阅读提示 :本章中提到的短期记忆,应直接理解为 Agent 状态持久化 。重点概念是ReactAgent、MemorySaver、RedisSaver、threadId。
一、为什么需要会话记忆?
1.1 无状态的问题
在之前的例子中,每次调用 AI 都是独立的:
第一次调用:
我:你叫什么?
AI:我叫小爱
第二次调用(AI已经完全忘了之前的事):
我:我叫什么?
AI:抱歉,我不知道
AI 没有"记忆",每次对话都是全新的。
1.2 人类对话 vs AI 对话
人类对话(有记忆):
我:我叫张三
AI:你好张三,我记住了
我:我叫什么?
AI:你叫张三啊
普通 AI 对话(无记忆):
我:我叫张三
AI:你好张三
我:我叫什么? ← 忘记之前说的了
AI:抱歉,我不知道
1.3 解决方案:ChatMemory
在 Spring AI Alibaba 1.1.2.0 的理解方式里,短期记忆本质上已经可以看成 Agent 状态的一部分。
简单说:
- 用户每说一句话,都会进入当前会话线程的状态中
- 模型每回复一句话,也会写回这个状态中
- 这些状态可以通过
MemorySaver或RedisSaver持久化
所以,本章的核心不再只是"把聊天记录丢进 Redis",而是:
让 Agent 在一个 thread(会话线程)中持续记住上下文。
二、会话记忆的实现方式
2.1 内存版 ChatMemory
最基本的实现,是把 Agent 的短期状态保存在内存里:
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.saver(new MemorySaver())
.build();
这种方式的特点是:
- 简单,适合开发和演示
- 服务一重启,记忆就没了
- 不适合多实例部署
2.2 Redis 版 ChatMemory(本章重点)
把 Agent 状态持久化到 Redis 中,优点:
-
持久化:服务重启,对话历史还在
-
多实例共享:多个服务实例可以共享同一个 Redis
-
性能好:Redis 读取速度很快
RedisSaver redisSaver = new RedisSaver(redissonClient);
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.saver(redisSaver)
.build();
2.3 实现原理
┌─────────────────────────────────────────────────────────┐
│ Redis 持久化短期记忆原理 │
├─────────────────────────────────────────────────────────┤
│ │
│ 用户A 发起 thread-001 会话 │
│ ┌──────┐ │
│ │用户A │ ──→ 写入状态 messages/thread-001 │
│ └──────┘ │
│ │
│ 用户A 再次发言 │
│ ┌──────┐ 从 Redis 读取 thread-001 状态 │
│ │用户A │ ──→ 恢复历史 messages ──→ 合并当前输入 │
│ └──────┘ ↓ │
│ 发送给 Agent/模型: │
│ [系统消息][历史消息][新问题] │
│ │
└─────────────────────────────────────────────────────────┘
三、项目配置详解
3.1 pom.xml 依赖
<!-- Spring AI Alibaba -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Redis 作为短期记忆持久化介质时需要的依赖,请结合官方文档选择对应 saver 实现 -->
这里也要提醒一下理解重点:
- 记忆章节重点关注
MemorySaver/RedisSaver/threadId - Redis 在这里主要扮演 状态持久化介质
- 会话记忆属于 Agent 状态的一部分
所以学习时要抓住主线:
- 会话记忆属于 Agent 状态
- 状态通过 Saver 持久化
- Redis 只是 Saver 的底层存储之一
3.2 application.yml 配置
server:
port: 8008
spring:
application:
name: SAA-08Persistent
# Redis 配置(本地的 Redis)
data:
redis:
host: localhost
port: 6379
# 可以用密码(如果设置了的话)
# password: 123456
3.3 配置类
package com.atguigu.study.config;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.checkpoint.savers.RedisSaver;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.redisson.api.RedissonClient;
/**
* Agent 记忆配置类
*/
@Configuration
public class SaaLLMConfig
{
/**
* 开发环境:使用 MemorySaver
*/
@Bean
public ReactAgent memoryAgent(ChatModel chatModel)
{
return ReactAgent.builder()
.name("memory-agent")
.model(chatModel)
.saver(new MemorySaver())
.build();
}
/**
* 生产环境:使用 RedisSaver
*/
@Bean
public ReactAgent redisMemoryAgent(ChatModel chatModel, RedissonClient redissonClient)
{
return ReactAgent.builder()
.name("redis-memory-agent")
.model(chatModel)
.saver(new RedisSaver(redissonClient))
.build();
}
}
四、控制器代码
package com.atguigu.study.controller;
import jakarta.annotation.Resource;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 持久化会话控制器
* 展示如何使用 Redis 保存对话历史
*/
@RestController
public class ChatMemory4RedisController
{
@Resource(name = "redisMemoryAgent")
private ReactAgent redisMemoryAgent;
/**
* 带会话记忆的对话
*
* 核心点:使用 threadId 指定会话线程
*
* 接口:http://localhost:8008/chatmemory/chat?msg=你好&userId=user001
*
* 参数说明:
* - msg:用户的消息
* - userId:用户ID(用于区分不同用户的会话)
*
* 测试步骤:
* 1. msg=你好&userId=user001
* 2. msg=你还记得我刚才说什么吗?&userId=user001
*
* 注意:两次请求使用同一个 userId,底层会映射到同一个 thread,AI 就能"记住"之前的对话内容!
*/
@GetMapping("/chatmemory/chat")
public String chat(String msg, String userId)
{
RunnableConfig config = RunnableConfig.builder()
.threadId(userId)
.build();
return redisMemoryAgent.call(msg, config).getText();
}
}
五、底层原理深入
5.1 threadId 的作用
在 Agent Framework 中,短期记忆主要通过 threadId 来区分不同会话线程。
你可以把它理解为:
- 一个
threadId对应一段连续对话 - 同一个
threadId下,历史消息会被持续追加 - 不同
threadId之间彼此隔离
示例:
RunnableConfig config = RunnableConfig.builder()
.threadId("user001")
.build();
如果用户两次请求都使用 threadId("user001"),那么 Agent 就会自动读取同一条会话线程里的历史内容。
5.2 Saver 是什么?
Saver 可以理解成"状态保存器",它负责把 Agent 的短期记忆保存下来,并在下次请求时恢复。
常见的两种 Saver:
-
MemorySaver:保存在内存中,适合开发和测试 -
RedisSaver:保存在 Redis 中,适合生产环境ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.saver(new MemorySaver())
.build();
5.3 数据在 Redis 中的理解方式
更准确的说法不是"Redis 中只保存聊天记录列表",而是:
Redis 中保存的是某个
threadId对应的 Agent 状态,其中messages是状态中的一个重要字段。
所以理解重点应该放在:
- Redis 保存的是会话线程状态
- 消息历史只是这个状态的一部分
- 后续如果需要,还可以在状态中加入其他上下文信息
5.4 为什么这里几乎没有使用 RedisTemplate?
很多学过传统 Spring Boot + Redis 的同学,看到这个项目时会有一个疑问:
"以前操作 Redis 时,不是经常用
RedisTemplate吗?为什么这里几乎没看到它?"
这是因为:本章的重点不是手写 Redis 增删改查,而是使用框架提供的 Saver 机制来持久化 Agent 状态。
如果按传统方式写,你可能要自己做这些事情:
- 自己设计 key
- 自己管理消息列表
- 自己做序列化和反序列化
- 自己控制会话隔离
- 自己处理历史上下文恢复
而在 Spring AI Alibaba 1.1.2.0 的推荐思路里,这些底层细节尽量交给框架处理:
- 你关注的是
threadId - 你关注的是
Saver - 你关注的是 Agent 是否能恢复历史上下文
也就是说,代码里没有显式写 RedisTemplate,不代表没有用 Redis,而是因为 Redis 的读写细节被框架封装起来了。
什么时候仍然需要 RedisTemplate?
下面这些业务场景,依然很适合自己使用 RedisTemplate:
- 商品缓存
- 验证码缓存
- 排行榜
- 令牌管理
- 复杂自定义 Redis 结构
简单总结:
-
RedisTemplate:通用 Redis 操作工具 -
RedisSaver:AI Agent 短期记忆持久化工具RunnableConfig config = RunnableConfig.builder()
.threadId("user001")
.build();agent.call("你好", config);
六、本章小结
6.1 核心概念
| 概念 | 说明 |
|---|---|
| ReactAgent | Agent Framework 的核心执行入口 |
| MemorySaver | 内存版短期记忆保存器,适合开发测试 |
| RedisSaver | Redis 版短期记忆保存器,适合生产环境 |
| threadId | Agent Framework 中更推荐的会话线程标识 |
| messages | Agent 状态中的消息历史,是短期记忆的重要组成部分 |
6.2 使用流程
1. 配置 Redis(application.yml)
2. 创建 ReactAgent
2. 配置 MemorySaver 或 RedisSaver
3. 调用时通过 RunnableConfig.threadId(...) 指定会话
4. Agent 自动从状态中恢复历史消息
5. 如有需要,再通过 Hook 做裁剪、删除、总结
6.3 注意事项
- 不同用户使用不同的 userId:这样每个用户的对话是独立存储的
- Redis 需要提前启动 :确保本地 Redis 在运行
redis-server - 会话ID可以是用户ID或会话ID:根据业务需求决定
- 特别注意版本号 :本章按
Spring AI Alibaba 1.1.2.0的官方文档思路整理,学习时请重点关注ReactAgent、MemorySaver、RedisSaver、threadId等概念
本章重点:
- 理解为什么需要会话记忆
- 掌握
ReactAgent + Saver的短期记忆主线 - 学会通过
threadId区分不同用户会话
下章剧透(s09):
学会了对话记忆后,下一章我们将开启多模态的学习------让 AI 根据文字描述生成图片(Text to Image)!
📝 编辑者 :Flittly
📅 更新时间 :2026年3月
🔗 相关资源 :Spring AI ChatMemory | Redis 官方文档