Spring AI 1.0.7 / 1.1.6 起
PromptChatMemoryAdvisor标记弃用,2.0.0-M6 起直接移除,编译会报cannot find symbol。新版聊天记忆 advisor 要求每次请求显式传 conversation ID,旧的隐式兜底被删掉。没传 ID 的代码升级后会抛异常,多用户场景还会互相读历史。
Issue & Env(案发现场与环境)
- 业务场景:升级 Spring AI 后,原本能跑的多轮对话直接编译不过;或者改完能编译,一调用还是抛异常。
- 环境:以 Spring AI 1.1.x 为基线。
PromptChatMemoryAdvisor自 1.0.7、1.1.6 起弃用,2.0.0-M6 起移除。1.0.7 之前的版本仍可用旧写法。 - 影响面:单用户单会话时往往看不出问题,一旦多用户或多会话共用,历史就会串。
StackTrace(堆栈日志)
编译期先炸。IDE 或 mvn compile 给出的原文:
java: cannot find symbol
symbol: class PromptChatMemoryAdvisor
location: package org.springframework.ai.chat.client.advisor
如果代码已换成新 advisor,但没传 conversation ID,运行期会抛:
java.lang.IllegalStateException: Conversation ID is required
这条异常文案在不同版本里可能不一样,以实际版本为准。截至撰写时,官方文档只声明 conversation ID 为必填,未给出统一的消息文本,这里不做臆测。
Reproduce(复现代码)
先看旧写法,这是升级前很常见的一段:
@Bean
ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultAdvisors(new PromptChatMemoryAdvisor(chatMemory))
.build();
}
升级到新版本后,PromptChatMemoryAdvisor 已不在 org.springframework.ai.chat.client.advisor 包里,编译直接失败。
把 advisor 换掉、却忘了传会话 ID,运行期就会抛 IllegalStateException:
chatClient.prompt()
.user("你刚才说的第二点再展开下")
.call()
.content();
Root Cause(根因分析)
旧的隐式 conversation ID 被去掉了
老版本里,调用方不传 conversation ID 时,advisor 会用一个隐式默认值兜底。单用户跑得通,但多个用户、多个会话共用同一个默认值,历史就会互相污染。A 用户问「上一句是什么」,可能读到 B 用户的对话。
新版本把这个兜底删了,改成强制显式传入。谁不传,谁就抛 IllegalStateException。这是一次有意的行为收紧,不是 bug。
advisor 类被整体替换
PromptChatMemoryAdvisor 属于旧体系,它绑的是「把历史拼进 prompt」这一层。1.0.7 / 1.1.6 起该类进入弃用状态,2.0.0-M6 起移除,由 MessageChatMemoryAdvisor 承接,底层走新的 BaseChatMemoryAdvisor 体系。
所以升级后有两件事同时发生:类名换了,参数也从「默认兜底」变成「必须显式传」。只改其中一件,另一件仍会出错。
The Fix(解决方案)
第一步:换 advisor
把 PromptChatMemoryAdvisor 换成 MessageChatMemoryAdvisor:
@Bean
ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
第二步:每次请求传 conversation ID
光换类还不够,得在请求里带上会话 ID:
chatClient.prompt()
.user("你刚才说的第二点再展开下")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, sessionId))
.call()
.content();
sessionId 要每个会话唯一。推荐用「用户 ID + 会话 ID」拼出来,比如 tenantId + ":" + conversationId。别写死成常量字符串,否则等于把不同用户的历史又混到一起。
从旧写法迁到新写法
对照如下:
// 旧:隐式默认 ID,多会话会串历史
new PromptChatMemoryAdvisor(chatMemory)
// 新:advisor 换成 MessageChatMemoryAdvisor
MessageChatMemoryAdvisor.builder(chatMemory).build()
// 旧:不带 conversation ID
chatClient.prompt().user(q).call().content()
// 新:每次请求显式传
chatClient.prompt().user(q)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, sessionId))
.call().content()
升级检查清单
- 全局搜
PromptChatMemoryAdvisor,全部替换。 - 搜聊天记忆相关的
.call(),确认每个请求都带了CONVERSATION_ID。 - 压一遍多会话并发场景,确认历史不串。
References(参考来源)
- Spring AI 1.0.7 / 1.1.6 / 2.0.0-M6 available now — Spring Blogspring-blog
- Upgrade Notes — Spring AI 官方文档spring-docs
- Chat Memory — Spring AI 官方文档spring-docs
