【SpringAIAlibaba新手村系列】(8)持久化会话与 Redis 内存管理

第八章 持久化会话与 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 状态持久化 。重点概念是 ReactAgentMemorySaverRedisSaverthreadId


一、为什么需要会话记忆?

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 状态的一部分

简单说:

  • 用户每说一句话,都会进入当前会话线程的状态中
  • 模型每回复一句话,也会写回这个状态中
  • 这些状态可以通过 MemorySaverRedisSaver 持久化

所以,本章的核心不再只是"把聊天记录丢进 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 的官方文档思路整理,学习时请重点关注 ReactAgentMemorySaverRedisSaverthreadId 等概念

本章重点

  1. 理解为什么需要会话记忆
  2. 掌握 ReactAgent + Saver 的短期记忆主线
  3. 学会通过 threadId 区分不同用户会话

下章剧透(s09):

学会了对话记忆后,下一章我们将开启多模态的学习------让 AI 根据文字描述生成图片(Text to Image)!


📝 编辑者 :Flittly

📅 更新时间 :2026年3月

🔗 相关资源Spring AI ChatMemory | Redis 官方文档

相关推荐
饼干哥哥2 小时前
1句话抓100个独立站数据?用第一性原理看懂OpenClaw的架构
人工智能
s1mple“”2 小时前
大厂Java面试实录:从Spring Boot到AI技术的面试场景深度解析
spring boot·redis·微服务·kafka·java面试·rag·ai技术
Allen正心正念20252 小时前
Research Agent架构
人工智能·架构
夜郎king2 小时前
【码动四季】Trae + 腾讯地图 MCP 实战:让 AI 直接调用地图能力,一步到位
人工智能·trae实战·trae接入腾讯地图mcp·atomgit 码动四季
东离与糖宝2 小时前
Java 干掉 Python 垄断!LangChain4j + PgVector 本地知识库开发全流程
java·人工智能
郑同学zxc2 小时前
Claude Code 的学习笔记
人工智能·笔记·学习
适应规律2 小时前
深度学习第四版
人工智能·深度学习
东离与糖宝2 小时前
OpenClaw 企业级实战:Java 微服务集成 AI 智能体,自动处理业务流
java·人工智能
nap-joker2 小时前
使用Image - To - image条件生成对抗网络评估乳腺癌新辅助化疗反应的动态对比增强MRI血管渗透性映射
人工智能·神经网络·生成对抗网络