StackTraces
Spring AI已驗證

Spring AI 升級破壞性變更:ChatClient 變 ChatModel

@debug_master32 天前更新閱讀 6 min00

一句話先說結論:Spring AI 是快速迭代的框架,1.0 前後踩了幾乎所有“命名遷移”的坑——舊 ChatClient 的功能挪進了 ChatModel,EmbeddingClient/ImageClient 這類 *Client 統一改成 *Model,starter 的 artifact 名也標準化了。升級時最容易翻車的是照著舊博客抄程式碼,結果類名、包名、設定鍵全對不上。升級前先讀官方 upgrade notes,再用 BOM + OpenRewrite 自動遷移。

背景

Spring AI 從 0.7/0.8 一路發到 1.0(2025-05 GA),每個 milestone 都帶著 breaking change。早期很多教程、博客裡的程式碼是 0.8 時代的,直接照搬到 1.x 項目裡會到處編譯不過。理解那條主線——“把模型訪問能力從 *Client 歸到 *Model”,就能抓住大半變化。

現象

升級後最常見的幾類報錯:

// 类找不到
Cannot resolve symbol 'ChatClient'
// 或者方法名变了
Cannot resolve method 'getGenerationTokens'
// 或者配置键不生效,模型 Bean 没注册
Unsupported model provider: 'openai'

更隱蔽的是:程式碼能編譯、啟動也不報錯,但某些設定項(比如 .options 前綴下的 model)悄悄失效,導致運行時走了預設值,行為不對。

如果舊程式碼的方法在升級後直接沒了,運行時拋的是 NoSuchMethodError,例如還是按老名調 getGenerationTokens():

java.lang.NoSuchMethodError: 'java.lang.Long org.springframework.ai.chat.metadata.Usage.getGenerationTokens()'
    at com.example.service.ChatService.ask(ChatService.java:27)
    at com.example.controller.ChatController.chat(ChatController.java:42)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:256)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:190)

它是編譯過了、運行才炸的標誌,說明調用點還停留在舊版本的簽名上,只是編譯期和運行期各拿了一個版本的 class。

根因分析

變化 1:Client → Model 的角色遷移

這是最核心的一條。1.0.0-M1 起,舊的 ChatClient 被拆了:

  • 模型訪問能力(call/stream)挪進了 ChatModel。
  • 新的 ChatClient 持有一個 ChatModel 實例,主打 fluent API(鏈式 prompt().user().call())。

同類命名統一也在發生:EmbeddingClient → EmbeddingModel、ImageClient → ImageModel。所以舊程式碼裡凡是 *Client 當模型用的,升級後基本都要改成 *Model。

變化 2:artifact 名標準化

pre-1.0 的 spring-ai-openai-spring-boot-starter 這種長名字,1.0 起統一成 spring-ai-starter-model-openai。memory 相關還加了 repository 後綴:

spring-ai-openai-spring-boot-starter          → spring-ai-starter-model-openai
spring-ai-model-chat-memory-jdbc              → spring-ai-model-chat-memory-repository-jdbc
spring-ai-elasticsearch-store-spring-boot-starter → spring-ai-starter-vector-store-elasticsearch

只換 starter 名字、不碰內部模塊,就會因為 artifact 找不到而構建失敗。

變化 3:包名和模塊拆分

spring-ai-core 這個大雜燴被拆成了 spring-ai-commons / spring-ai-model / spring-ai-vector-store / spring-ai-client-chat 等。一些類也搬了包:

org.springframework.ai.model.Content         → org.springframework.ai.content.Content
org.springframework.ai.transformer.xxx        → org.springframework.ai.chat.transformer.xxx

變化 4:方法名和設定鍵

  • Usage.getGenerationTokens() → getCompletionTokens(),token 計數 Long → Integer。
  • 到 1.1/2.0,設定屬性去掉了 .options 這一層:spring.ai.openai.chat.options.model → spring.ai.openai.chat.model(2.0.0-M6 起)。

解決方案

用 BOM 統一版本,不手動寫版本號

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.1.7</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

用 BOM 後,starter 和內部模塊版本由 BOM 拉平,避免“版本號各自為政”導致的 ClassNotFoundException。

有官方的 OpenRewrite recipe 可自動化遷移

Spring AI 提供了 OpenRewrite 遷移 recipe(Arconia Spring AI Migrations),能自動改大部分類名/包名/方法名:

# 参考 https://github.com/arconia-io/arconia-migrations

大項目手工改容易漏,建議先跑 recipe 再人工核對剩餘 diff。

升級動作清單

  1. 查 upgrade notes 對應版本段。
  2. 升 BOM 版本,改 starter 為標準命名。
  3. *Client → *Model,getGenerationTokens() → getCompletionTokens()。
  4. 檢查包名是否遷移(IDE 的 import 會提示)。
  5. 檢查設定鍵是否去掉了 .options 層。
  6. 跑一遍測試/壓測確認行為沒變,而不只是編譯通過。

小結

  • Spring AI 升級的核心詞是“改名”:*Client → *Model、artifact 標準化、包名重排、設定鍵去 .options。
  • 別照抄舊博客,先對照 upgrade notes。
  • 用 BOM 管版本 + OpenRewrite 自動化,能省一大半手工活。
  • 編譯過了還不夠,設定鍵靜默失效這種要跑功能驗證才抓得到。

References(參考來源)

最後更新於 2026-09-09

這篇幫到你了嗎?

剛解決了一個棘手的報錯?花兩分鐘記錄下來,幫助下一個遇到同樣問題的開發者。

貢獻一條解法