Spring AI版本升级:0.x迁移到1.0的完整实战指南
2026/4/30大约 6 分钟
Spring AI版本升级:0.x迁移到1.0的完整实战指南
适读人群:项目中使用Spring AI 0.x版本、需要升级到1.0的工程师 阅读时长:约16分钟 文章价值:避开迁移陷阱,2天内完成升级,不踩坑
先说一件真实的事
老王他们团队去年年初用Spring AI 0.8.x版本做了个智能问答项目,跑了将近一年。前段时间技术负责人说要把Spring Boot升到3.3,顺便把Spring AI也升到1.0正式版。
老王接了这个任务,信心满满,改了依赖版本号,跑了一下——一片红。
整个下午,他都在跟编译报错和运行时异常死磕。API改了,包名变了,Bean的注册方式变了,自动配置属性也变了。
最惨的是,有些改动在文档里没有清晰说明,只能靠挨个看源码和GitHub Issues来还原。
我把他踩过的那些坑,加上我自己整理的迁移清单,写成了这篇文章。
为什么要升级到 1.0
Spring AI 0.x 是 Milestone 版本,有几个硬伤:
| 维度 | 0.x | 1.0 |
|---|---|---|
| API稳定性 | 每个Milestone都可能破坏性改动 | 遵循Semver,稳定 |
| 生产可用性 | 不推荐生产(官方说法) | 可生产 |
| 依赖版本 | 需手动管理 | BOM统一管理 |
| Advisor机制 | 不完整 | 完整,扩展性强 |
| 会话记忆 | API较原始 | ChatMemory统一抽象 |
| 结构化输出 | 功能有限 | BeanOutputConverter成熟 |
迁移整体路径
第一步:更新依赖
pom.xml 变更
<!-- 旧:0.x 方式 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>0.8.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 0.x 的依赖命名 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
</dependencies><!-- 新:1.0 方式 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 1.0 依赖命名变了:spring-ai-starter-model-xxx -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>依赖命名规则变更(这是最容易漏的地方):
| 模型 | 0.x artifactId | 1.0 artifactId |
|---|---|---|
| OpenAI | spring-ai-openai-spring-boot-starter | spring-ai-starter-model-openai |
| Ollama | spring-ai-ollama-spring-boot-starter | spring-ai-starter-model-ollama |
| 阿里云 | spring-ai-dashscope-spring-boot-starter | spring-ai-starter-model-openai-dashscope(或Alibaba模块) |
| PGVector | spring-ai-pgvector-store-spring-boot-starter | spring-ai-pgvector-store-spring-boot-autoconfigure |
第二步:修复 ChatClient API 变更
0.x 的 ChatClient 和 1.0 的 ChatClient 完全是两套接口。
// ====== 0.x 写法 ======
@Service
public class OldChatService {
@Autowired
private ChatClient chatClient; // 0.x中ChatClient是直接注入的
public String chat(String message) {
// 0.x API
return chatClient.call(message);
}
// 0.x 带Prompt的方式
public String chatWithPrompt(String message) {
Prompt prompt = new Prompt(List.of(
new SystemMessage("你是一个助手"),
new UserMessage(message)
));
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
}// ====== 1.0 写法 ======
@Configuration
public class ChatConfig {
// 1.0中需要手动创建ChatClient,注入ChatModel
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultSystem("你是一个助手")
.build();
}
}@Service
public class NewChatService {
private final ChatClient chatClient; // 注入Builder创建的ChatClient
public NewChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String chat(String message) {
// 1.0 流式Builder API
return chatClient.prompt()
.user(message)
.call()
.content();
}
// 1.0 带参数的方式
public String chatWithOptions(String message) {
return chatClient.prompt()
.system("你是一个专业助手")
.user(message)
.options(OpenAiChatOptions.builder()
.withTemperature(0.7)
.build())
.call()
.content();
}
}第三步:修复 VectorStore API 变更
// ====== 0.x 写法 ======
// 0.x的检索API
List<Document> results = vectorStore.similaritySearch(query, 5);
// 或者
List<Document> results = vectorStore.similaritySearch(query);// ====== 1.0 写法 ======
// 1.0统一用SearchRequest对象
List<Document> results = vectorStore.similaritySearch(
SearchRequest.query(query)
.withTopK(5)
.withSimilarityThreshold(0.7)
);
// 带元数据过滤
List<Document> filtered = vectorStore.similaritySearch(
SearchRequest.query(query)
.withTopK(5)
.withFilterExpression("category == 'tech' && year >= 2023")
);第四步:修复会话记忆 API 变更
这是改动最大的地方之一:
// ====== 0.x 写法 ======
@Service
public class OldMemoryService {
// 0.x没有统一的Memory抽象,通常自己维护消息列表
private final Map<String, List<Message>> sessions = new ConcurrentHashMap<>();
public String chat(String sessionId, String userMessage) {
List<Message> history = sessions.computeIfAbsent(sessionId, k -> new ArrayList<>());
history.add(new UserMessage(userMessage));
Prompt prompt = new Prompt(history);
ChatResponse response = chatClient.call(prompt);
String assistantMessage = response.getResult().getOutput().getContent();
history.add(new AssistantMessage(assistantMessage));
return assistantMessage;
}
}// ====== 1.0 写法 ======
@Configuration
public class MemoryConfig {
@Bean
public ChatMemory chatMemory() {
// 开发用InMemory,生产用持久化方案
return new InMemoryChatMemory();
}
@Bean
public ChatClient chatClientWithMemory(ChatModel chatModel, ChatMemory chatMemory) {
return ChatClient.builder(chatModel)
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory)
)
.build();
}
}@Service
public class NewMemoryService {
private final ChatClient chatClient;
public String chat(String sessionId, String userMessage) {
return chatClient.prompt()
.advisors(a -> a.param(
AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, sessionId))
.user(userMessage)
.call()
.content();
}
}第五步:修复 Embedding API 变更
// ====== 0.x 写法 ======
@Autowired
private EmbeddingClient embeddingClient; // 0.x用EmbeddingClient
public float[] embed(String text) {
EmbeddingResponse response = embeddingClient.embedForResponse(List.of(text));
return response.getResults().get(0).getOutput();
}// ====== 1.0 写法 ======
@Autowired
private EmbeddingModel embeddingModel; // 1.0改为EmbeddingModel
public float[] embed(String text) {
// API基本一致,只是类名变了
EmbeddingResponse response = embeddingModel.embedForResponse(List.of(text));
return response.getResults().get(0).getOutput();
}第六步:修复配置属性变更
# ====== 0.x application.yml ======
spring:
ai:
openai:
api-key: sk-xxx
chat:
options:
model: gpt-3.5-turbo
temperature: 0.7
embedding:
options:
model: text-embedding-ada-002# ====== 1.0 application.yml ======
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com # 现在更明确
chat:
options:
model: gpt-4o-mini # 1.0默认模型也变了
temperature: 0.7
max-tokens: 2048 # 属性名变了(原为max-completion-tokens)
embedding:
options:
model: text-embedding-3-small # 推荐用新模型完整迁移检查清单
常见报错对照表:
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
No qualifying bean of type 'ChatClient' | 0.x时自动注册,1.0需要手动创建Bean | 添加@Bean ChatClient chatClient(ChatModel) |
Method not found: VectorStore.similaritySearch(String, int) | API签名变了 | 改为SearchRequest.query(q).withTopK(k) |
Could not autowire field EmbeddingClient | 类名变了 | 改为EmbeddingModel |
application.properties: spring.ai.openai.chat.model not found | 属性路径变了 | 看1.0文档确认新路径 |
Circular dependency | Builder配置方式引入了循环 | 检查Bean定义,避免相互依赖 |
升级后的验证测试
@SpringBootTest
@ActiveProfiles("test")
class MigrationVerificationTest {
@Autowired
private ChatClient chatClient;
@Autowired
private EmbeddingModel embeddingModel;
@Autowired
private VectorStore vectorStore;
@Test
void testChatClientWorks() {
String response = chatClient.prompt()
.user("请回答:1+1等于几?只回答数字。")
.call()
.content();
assertThat(response).contains("2");
}
@Test
void testEmbeddingWorks() {
float[] embedding = embeddingModel.embed("测试文本");
assertThat(embedding).isNotEmpty();
assertThat(embedding.length).isGreaterThan(100);
}
@Test
void testVectorStoreWorks() {
// 添加测试文档
vectorStore.add(List.of(
new Document("Spring AI是一个强大的AI框架")
));
// 检索
List<Document> results = vectorStore.similaritySearch(
SearchRequest.query("Spring AI是什么").withTopK(1));
assertThat(results).isNotEmpty();
}
}升级小技巧
- 先在新分支上改,不要直接改主分支
- 逐模块迁移,先改依赖编译,再改API,再改配置,每步验证一次
- 保留0.x代码注释,方便对比新旧写法
- 看源码胜过看文档,1.0的JavaDoc比文档更新更及时
- GitHub Issues是金矿,遇到奇怪报错,先去Issues里搜
老王最终花了一天半完成了迁移,没有业务逻辑改变,只是API和配置的调整。他说最大的感受是:1.0的代码比0.x清晰多了,ChatClient.prompt().user().call()这个链式风格,读起来很舒服。
