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
