乐于分享
好东西不私藏

5行代码让AI"记住"上下文:Spring AI ChatMemory实战

5行代码让AI"记住"上下文:Spring AI ChatMemory实战
5行代码让AI"记住"上下文:Spring AI ChatMemory实战
最近有个读者问我:
"我的AI对话小程序,用户问'继续上文',AI就懵了,咋整?"
这其实是多轮对话的经典问题——AI没有"记忆",每次请求都是独立的。
解决方案很简单:ChatMemory
今天就用你熟悉的Spring Boot风格,5行代码搞定。

01

问题复现:AI都是"金鱼脑"
先看没有记忆的代码:
@GetMapping("/chat")public String chat(@RequestParamString question) {        return chatClient.prompt()                   .user(question)                   .call()                   .content();}
用户这么用:
用户:你叫什么名字?AI:我是你的AI助手。用户:继续AI:...你在说什么继续?我不知道你指的是什么。
AI的视野只有当前这一轮对话,上一轮问啥它完全不记得。

02

ChatMemory是什么?
一句话:AI的"记忆宫殿"
每次对话后,把消息存起来;下次对话时,把历史消息一起发给AI。
第一轮:用户问好 → AI回复      → 存入记忆第二轮:用户说"继续" → 从记忆读取 → AI知道上文 → 回复
Spring AI的ChatMemory体系:
ChatMemoryRepository(存储层接口)       ↑ChatMemory(管理器)       ↑MessageChatMemoryAdvisor(拦截器,注册到ChatClient)

03

实战:5行代码搞定多轮对话
最终效果
用户:你叫什么名字?AI:我是你的AI助手。用户:继续AI:你是问我叫什么名字吗?我是你的AI助手,我们可以聊聊任何话题。
代码实现
Controller核心代码
@RestController@RequestMapping("chat/memory")public class ChatMemoryController {    @Resource    private MiniMaxChatModel miniMaxChatModel;    // Spring AI 默认有一个ChatMemory实例    @Resource    private ChatMemory chatMemory;  // 自动配置的InMemory    @GetMapping(value = "v1", produces = MediaType.TEXT_EVENT_STREAM_VALUE)    public Flux<StringstreamEvent(@RequestParam("question"String question) {        // 关键5行代码        // 这里引入一个新的概念:Advisor,后续文章会讲解它的作用        ChatClient chatClient= ChatClient.builder(miniMaxChatModel)                               .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())                              .build();        return chatClient.prompt()               .user(question)                           .stream()                           .content();        }}
核心就这几行:
ChatClient chatClient = ChatClient.builder(miniMaxChatModel)                           .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())                       .build();
把ChatMemoryAdvisor注册到ChatClient,它会自动:
  1. 读取历史消息,加入Prompt
  2. 把新消息写入记忆
就这么简单。

04

不同存储方案怎么选?
  1. InMemory(开发测试用)
零配置,Spring Boot自动配置:
// 直接注入,框架已经配好了@Resourceprivate ChatMemory chatMemory;
缺点:重启后记忆丢失。
2. JDBC(生产环境推荐MySQL/PostgreSQL)
<dependency>      <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId></dependency>
配置:
spring.ai.chat.memory.repository.jdbc.initialize-schema=embedded
自动建表,重启不丢数据。
3. Redis(高性能+高级查询)
<dependency>  <groupId>org.springframework.ai</groupId>  <artifactId>spring-ai-starter-model-chat-memory-repository-redis</artifactId></dependency>
配置:
spring.ai.chat.memory.redis.host=localhostspring.ai.chat.memory.redis.port=6379spring.ai.chat.memory.redis.time-to-live=24h
支持按内容搜索、时间范围查询,比JDBC快10倍。

05

原理深入:Advisor是怎么工作的?
用户请求    ↓MessageChatMemoryAdvisor(拦截器)    ├── 读取:chatMemory.get(sessionId) → 历史消息    ├── 拼接:历史消息 + 当前问题 → 完整Prompt    ↓发给AI Model    ↓  AI回复    ↓MessageChatMemoryAdvisor(拦截器)    ├── 写入:chatMemory.add(sessionId, 用户消息, AI回复)        ↓ 返回给用户
会话隔离也很简单,每个用户用不同会话ID:
// 给不同用户分配不同记忆空间String sessionId = user.getId();  // 用户ID维度隔离MessageChatMemoryAdvisor(chatMemory, sessionId)

06

记忆容量怎么控制?
AI的上下文长度有限(Token限制),记忆不是越多越好:
// 保留最近N条消息(推荐10-20)MessageWindowChatMemory.builder().maxMessages(10).build();// 或者按Token数量TokenWindowChatMemory.builder().maxTokens(2000).build();
经验值:
  • 简单问答:maxMessages=10
  • 复杂对话:maxMessages=20
  • 超长对话:考虑maxTokens=2000

07

踩坑记录
坑1:会话ID没区分,全员共享记忆
// ❌ 错误:所有用户共享同一个会话MessageChatMemoryAdvisor(chatMemory, "global")// ✅ 正确:按用户ID隔离MessageChatMemoryAdvisor(chatMemory, user.getId())
坑2:InMemory以为生产也能用
测试环境好好的,上线后发现用户记忆全丢了。
生产建议: Redis or JDBC,消息持久化。
坑3:记忆太多,Token爆表
AI模型有上下文长度限制(MiniMax通常是32K),超过就直接截断。
解决: 严格控制maxMessages,或者用TokenWindowChatMemory

08

结语
ChatMemory是AI应用的基本功,Spring AI把它封装得像数据库操作一样简单。
核心记住两点:
  1. Advisor机制:MessageChatMemoryAdvisor自动管理记忆
  2. 会话隔离:不同用户/场景用不同sessionId
扫码关注我,回复「ChatMemory」获取完整项目代码和配置。