報
OpenAI API key must be set不代表 key 沒傳,而是 starter 的自動設定在啟動階段就校驗spring.ai.openai.api-key,跟你之後在程式碼裡new不new沒關係。想在運行時動態傳 key,得先關掉自動設定。
Issue & Env(案發現場與環境)
- 業務場景:用
spring-ai-starter-model-openai接 OpenAI,想在程式碼裡動態創建OpenAiChatModel並傳 key,不寫進 application.yml。 - 環境:Spring AI 1.1.0(issue #4962 報告版本),Spring Boot 3.x,spring-core 6.2.6。
StackTrace(堆疊日誌)
啟動階段就掛,報錯文案把兩個合法 key 名都點出來了。報錯原文:
java.lang.IllegalArgumentException: OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai.chat.api-key property.
它在應用上下文創建 Bean 時拋出,完整堆疊:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'openAiChatModel' defined in class path resource [org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.class]: Failed to instantiate [org.springframework.ai.openai.OpenAiChatModel]: Factory method 'openAiChatModel' threw exception with message: OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai.chat.api-key property.
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1217)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:325)
Caused by: java.lang.IllegalArgumentException: OpenAI API key must be set. Use the connection property: spring.ai.openai.api-key or spring.ai.openai.chat.api-key property.
at org.springframework.util.Assert.hasText(Assert.java:253) ~[spring-core-6.2.6.jar:6.2.6]
at org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration.openAiChatModel(OpenAiAutoConfiguration.java:151)
Reproduce(復現程式碼)
starter 裝進項目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>1.1.0</version>
</dependency>
設定文件裡不寫 key,改成程式碼裡動態 new:
@GetMapping("/call/openai")
public String call() {
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(OpenAiApi.builder()
.apiKey("sk-xxxx")
.baseUrl("https://your-relay.example.com/v1")
.build())
.build();
return chatModel.call("hi");
}
啟動即報上面的 key 缺失。你明明在程式碼裡傳了 key,報錯卻打在啟動階段,而不是 call() 執行的時候。
Root Cause(根因分析)
spring-ai-starter-model-openai 靠 Spring Boot 自動設定裝配 ChatModel Bean。這個自動設定在啟動階段就執行 Assert.hasText,檢查 spring.ai.openai.api-key(或聊天專用 spring.ai.openai.chat.api-key)有沒有值。
兩條線是脫鉤的:自動設定只看「設定文件裡有沒有 key」;你在 controller 裡 new 的 OpenAiChatModel 是另一個物件,管不到自動設定。啟動時自動設定先跑,發現屬性為空,直接拋 IllegalArgumentException,應用上下文起不來,根本輪不到 call()。
報錯文案寫全了合法 key 名,說明這是設計行為,不是 bug。maintainer 在 issue #4962 的答覆是:要運行時自己建模型就關掉自動設定,想省事就把 key 寫進設定。
The Fix(解決方案)
正解:把 key 寫進設定,交給自動裝配
最省事,key 走環境變數,別硬編碼:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
框架自己建好 ChatModel Bean,你直接注入用。聊天模型還能用 spring.ai.openai.chat.api-key 單獨覆蓋公共 key。
真要動態傳 key:關掉自動設定
要按租戶動態選 key,就把 OpenAI 聊天自動設定關掉,再自己建 Bean。官方文檔給的方式是把模型選擇置空:
spring:
ai:
model:
chat: none
或者在啟動類上排除自動設定類:
@SpringBootApplication(exclude = OpenAiAutoConfiguration.class)
public class Application {}
自動設定關了,啟動時不再校驗 key,運行時怎麼動態 new 都行。
