本文根据 Spring AI 官方文档 整理,用大白话把原版内容讲清楚,代码可以直接复制使用。
前言:为什么LLM需要记忆?
大语言模型(LLM)说白了就是没记性——你跟它说啥,它听完就忘,每次对话都像第一次见面。这就很尴尬了:怎么让AI记住咱们刚才聊过啥?
Spring AI 提供了Chat Memory机制来解决这个痛点。本文会用最接地气的方式,带你搞懂 Spring AI 的对话记忆,从概念到各种存储方式,再到能跑的代码。
一、核心概念:记忆 vs 历史
在开始之前,先分清两个容易搞混的词:
| 概念 | 大白话解释 | 用途 |
|---|---|---|
| Chat Memory(对话记忆) | AI“脑子”里记着的那些事,为了接上话茬儿用的 | 维持当前聊天上下文 |
| Chat History(对话历史) | 完整聊天记录,一句话不落 | 翻旧账、做审计、恢复会话 |
一句话总结:
ChatMemory只管“记住该记住的”,不管“存下所有的”。完整的聊天记录建议用 Spring Data 自己去存。
二、整体架构:ChatMemory 是怎么工作的?
Spring AI 把“记忆”拆成两层,想换哪层就换哪层:
ChatMemory(记哪些):决定哪些消息要记住、什么时候忘掉(比如只留最近10条、过期自动删等)。ChatMemoryRepository(存哪去):消息真正存的地方,能从数据库读写。
三、内置记忆类型:MessageWindowChatMemory
目前 Spring AI 内置的记忆策略主要是MessageWindowChatMemory——翻译成人话就是**“只记最近N条消息”**。
// 创建一个只记最近10条消息的记忆ChatMemorychatMemory=MessageWindowChatMemory.builder().maxMessages(10)// 窗口大小,默认是20.build();它是怎么工作的?
注意:系统消息(SystemMessage)像钉子户一样,无论窗口怎么滑,它都不会被删。
四、内置存储实现(Repository)详解
Spring AI 支持多种存储后端,从内存到各种数据库全覆盖:
| 存储类型 | 啥时候用 | 有啥特点 |
|---|---|---|
| InMemory | 本地开发测试、流量很小 | 基于ConcurrentHashMap,重启就丢 |
| JDBC | 正式生产环境,用关系型数据库 | 支持MySQL/Postgres等,自动建表 |
| Cassandra | 数据量巨大、要高可用 | 原生支持自动过期(TTL),适合长期存 |
| Neo4j | 需要分析消息间的关系 | 消息存成节点,能查复杂关系 |
| MongoDB | 文档型数据、schema灵活 | 自动建索引,支持过期 |
| Cosmos DB | 用Azure云、全球部署 | 自动按会话ID分区 |
快速上手:用 JDBC 存到 PostgreSQL
加依赖
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId></dependency>写配置(application.yml)
spring:datasource:url:jdbc:postgresql://localhost:5432/chatdbai:chat:memory:repository:jdbc:initialize-schema:always# 自动建表,不用自己写SQL注入使用
@AutowiredprivateJdbcChatMemoryRepositoryrepository;// 组合窗口策略 + JDBC存储ChatMemorychatMemory=MessageWindowChatMemory.builder().chatMemoryRepository(repository).maxMessages(10).build();
五、实战:在 ChatClient 里用上记忆
Spring AI 推荐通过Advisor(顾问)来给ChatClient加记忆功能。你可以把 Advisor 理解成一个“拦截器”,在发消息前自动把历史记录塞进去。
5.1 用 MessageChatMemoryAdvisor(推荐)
// 1. 配置记忆ChatMemorychatMemory=MessageWindowChatMemory.builder().maxMessages(20).build();// 2. 创建 ChatClient,配上记忆顾问ChatClientchatClient=ChatClient.builder(chatModel).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();// 3. 聊天时传入 conversationId(就像房间号)StringconversationId="user-123-session";Stringresponse=chatClient.prompt().user("我的名字是詹姆斯·邦德").advisors(advisor->advisor.param(ChatMemory.CONVERSATION_ID,conversationId)).call().content();// 第二轮对话,AI 还记得你叫啥Stringresponse2=chatClient.prompt().user("我叫什么名字?").advisors(advisor->advisor.param(ChatMemory.CONVERSATION_ID,conversationId)).call().content();// 输出:詹姆斯·邦德关键点:
ChatMemory.CONVERSATION_ID是必须传的,用来区分不同会话(比如不同用户、不同聊天窗口)。不传就报错。
5.2 完整流程(看图就懂)
六、高级话题:VectorStoreChatMemoryAdvisor
如果你需要长期记忆或者**“凭感觉”找相关记忆**(比如 AI 能想起三天前聊过的话题),可以用VectorStoreChatMemoryAdvisor。它不再死板地只记最近N条,而是把所有历史存进向量数据库,每次根据语义相似度找最相关的记忆。
VectorStorevectorStore=...// 比如 Chroma, PGVectorChatClientchatClient=ChatClient.builder(chatModel).defaultAdvisors(newVectorStoreChatMemoryAdvisor(vectorStore)).build();安全提醒:这个顾问会把搜到的内容以 XML 标签形式塞进系统提示词里,有被恶意利用的风险。对安全要求高的场景,还是用上面那个
MessageChatMemoryAdvisor更稳妥。
七、总结 + 最佳实践
先想清楚要啥:短期会话记忆用
MessageWindowChatMemory,长期语义记忆用VectorStoreChatMemoryAdvisor。生产环境怎么选:
- 中小项目:JDBC + PostgreSQL/MySQL,窗口大小设合适(比如20-50条)。
- 数据量大、写入频繁:Cassandra或MongoDB,再设个自动过期时间。
- 用 Azure 云:直接上Cosmos DB Repository。
永远记得传 Conversation ID:不管用哪种 Advisor,每次请求都带上唯一的会话标识,不然 AI 就乱套了。
工具调用(Tool Calls)的坑:当前版本里,工具调用的中间消息不会被存进记忆。如果这功能对你很重要,要么等官方更新,要么自己手动处理。
掌握了 Spring AI 的 Chat Memory 机制,你就能让 AI 不再是“金鱼记忆”,做出真正能聊到一块儿的智能应用。