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

简介: 本文详解 Spring AI 的会话记忆机制,从内存版 MemorySaver 到 Redis 版 RedisSaver,实现 AI 对话的上下文连续性。文章以 ReactAgent 为核心,讲解如何通过 threadId 管理会话线程,并将 Agent 状态持久化到 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 -->
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring-ai-alibaba dashscope-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.1.2.0</version>
        </dependency>
        <!-- spring-ai-alibaba agent framework -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-agent-framework</artifactId>
            <version>1.1.2.0</version>
        </dependency>
        <!-- redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.22.0</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

<!-- 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月前
|
人工智能 前端开发 Java
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
本文围绕 Spring AI 中的流式输出与响应式编程展开,重点解释了传统一次性响应与流式返回的差异,以及 Flux 在异步数据流中的核心作用。文章结合 ChatModel.stream() 与 ChatClient 的多种代码示例,说明如何实现 AI 内容的边生成边返回,并帮助读者理解流式调用在用户体验、性能和长文本场景中的实际价值。
887 4
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
|
1月前
|
人工智能 JSON Java
【SpringAIAlibaba新手村系列】(6)PromptTemplate 提示词模板与变量替换
本章详解Spring AI的PromptTemplate提示词模板机制,涵盖变量替换、系统消息模板(SystemPromptTemplate)、外部文件加载等核心功能,助力实现提示词参数化、复用与动态组装,提升RAG、Agent及结构化输出场景下的开发效率与可维护性。
317 6
|
1月前
|
人工智能 JSON Java
【SpringAIAlibaba新手村系列】(7)结构化输出与对象映射
本文详解 Spring AI 结构化输出功能,通过 Java Record 与 .entity() 方法,实现 AI 的 JSON 响应自动映射为 Java 对象,解决纯文本难以集成的问题。文中还对比了 Lambda 写法并提供 Prompt 设计最佳实践。
359 4
|
1月前
|
NoSQL Java 数据库
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
本文围绕 Embedding 与向量数据库展开,讲解了文本向量化、相似度检索和 VectorStore 的基本用法,并结合 SimpleVectorStore 示例说明了 Spring 中自动装配与手动注册 Bean 的区别,为后续学习 RAG 打下基础。
631 5
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
|
人工智能 JavaScript Java
【SpringAIAlibaba新手村系列】(1)初识 Spring AI Alibaba 框架
本文介绍了SpringAIAlibaba框架的基本概念和使用方法。作为Spring官方AI框架的阿里云实现版本,它简化了Java开发者调用AI模型的过程。文章详细讲解了核心概念如ChatModel、ChatClient,以及阿里云百炼平台的功能。通过HelloWorld项目示例,展示了如何配置APIKey、编写控制层代码,实现普通调用和流式输出两种AI交互方式。重点阐述了SpringAI与SpringAIAlibaba的关系,以及自动配置机制的工作原理,帮助开发者快速上手这一框架。
3944 4
|
1月前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(2)Ollama 本地大模型调用
本章详解如何用Spring AI接入Ollama本地大模型:解决远程调用的联网依赖、隐私泄露与费用问题;支持Qwen、Llama等开源模型,零成本、低延迟、全离线运行;重点掌握`@Qualifier`多模型注入、流式响应(Flux)及本地API(`http://localhost:11434`)集成。
751 5
|
存储 人工智能 Java
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
本章深度解析 Spring AI 中 `ChatModel`(底层接口)与 `ChatClient`(高级封装)的本质区别:前者如“手动挡”,精准控制但需写大量样板代码;后者似“智能点餐机”,链式调用、支持系统提示、模板、工具调用等,开发高效。初学者推荐优先使用 `ChatClient`。
617 0
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
|
1月前
|
人工智能 自然语言处理 前端开发
【SpringAIAlibaba新手村系列】(9)Text to Image 文本生成图像技术
本文介绍 Spring AI 中的文生图能力,围绕 ImageModel、ImagePrompt 与阿里云百炼图像模型展开,演示如何根据文字描述生成图片链接,并结合 Prompt 编写技巧与参数配置,帮助开发者提升生成效果与落地能力。
499 8
|
1月前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(5)Prompt 提示词基础与多种消息类型
本章详解Spring AI 1.1.2中Prompt核心机制:以System/User/Assistant/Tool四类消息构建结构化提示,强调“角色决定语义”;涵盖多模型配置、链式API与底层Message组装两种实践方式,并给出系统消息设计最佳实践。
593 7
|
1月前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
本文详细解析 Spring AI 的 Tool Calling 技术,阐明其如何弥补大模型“会说不会做”的局限。通过 @Tool 注解,开发者可轻松将 Java 方法暴露为 AI 工具。文中深入讲解了 ToolCallbacks.from() 注册工具的原理,以及工具方法在当前 Spring Boot 进程内通过反射动态执行的底层逻辑,强调了模型决策与框架执行的协同过程,为理解 AI 赋能实际操作奠定基础。
695 0

热门文章

最新文章