Spring AI 1.0全解析:核心变化·新特性·升级迁移指南
2026/4/30大约 6 分钟
Spring AI 1.0全解析:核心变化·新特性·升级迁移指南
适读人群:正在使用Spring AI 0.x或准备开始Spring AI项目的Java工程师 阅读时长:约18分钟 文章价值:全面掌握Spring AI 1.0的核心变化,平滑完成升级迁移,避开常见坑点
那个"升级了三天还没跑起来"的哥们
春节后有个在星球里的同学小张,发消息说他们要把项目从Spring AI 0.8升级到1.0。他信心满满地说,不就是改个版本号嘛,半天搞定。
结果三天后,他发来一长串报错截图,加上一句话:"老张,救命,这两个版本简直不是同一个框架……"
他说的是真的。Spring AI 1.0不是小版本迭代,整个API设计做了大重构,很多核心概念都变了。如果你不了解这些变化,升级会是一场噩梦。
这篇文章,我把1.0的核心变化逐一拆解,再给你一份可操作的迁移指南。
版本对比总览
核心变化汇总:
| 特性 | 0.x | 1.0 | 影响程度 |
|---|---|---|---|
| 主接口 | AiClient | ChatClient | 高,需重写调用代码 |
| 构建方式 | new方式或@Bean | Builder模式 | 高 |
| 拦截器 | 无 | Advisor机制 | 中(新增能力) |
| 流式 | 手动处理Flux | 原生Flux支持 | 中 |
| Memory | 手动管理 | ChatMemory抽象 | 中 |
| 向量存储 | VectorStore(基本) | VectorStore(增强) | 低 |
| 函数调用 | FunctionCallback | @Tool注解+函数式 | 高 |
核心变化一:ChatClient的Builder模式
0.x里,你可能这样写:
// 0.x 旧写法(不要再用了)
@Service
public class OldChatService {
@Autowired
private OpenAiChatClient chatClient; // 直接注入具体实现
public String chat(String message) {
Prompt prompt = new Prompt(message);
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
}1.0的新写法:
// 1.0 新写法
@Service
@Slf4j
public class NewChatService {
private final ChatClient chatClient;
// 通过Builder注入,不依赖具体实现
public NewChatService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是一个专业的Java技术顾问")
.defaultOptions(OpenAiChatOptions.builder()
.withModel("gpt-4o-mini")
.withTemperature(0.7)
.build())
.build();
}
public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
// 流式响应
public Flux<String> streamChat(String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
// 携带历史记录的多轮对话
public String chatWithHistory(List<Message> history, String newMessage) {
return chatClient.prompt()
.messages(history)
.user(newMessage)
.call()
.content();
}
}Builder模式的好处:
- 可以为不同场景配置不同的ChatClient实例(system prompt不同、模型不同)
- 通过
@Qualifier区分多个ChatClient Bean - 调用时还可以动态覆盖默认配置
核心变化二:Advisor机制(最重要的新特性)
Advisor是1.0最核心的新特性,类似于Spring MVC的拦截器,但作用于AI调用链路:
实现一个Advisor:
/**
* 请求日志Advisor:记录每次AI调用的输入输出
*/
@Component
@Slf4j
public class RequestLoggingAdvisor implements CallAroundAdvisor {
@Override
public String getName() {
return "RequestLoggingAdvisor";
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100; // 靠近内层,最接近LLM调用
}
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest,
CallAroundAdvisorChain chain) {
// 前置:记录请求
String userInput = advisedRequest.userText();
log.info("[AI调用] 请求: {}",
userInput.length() > 100 ? userInput.substring(0, 100) + "..." : userInput);
long start = System.currentTimeMillis();
AdvisedResponse response = chain.nextAroundCall(advisedRequest);
// 后置:记录响应
long latency = System.currentTimeMillis() - start;
String responseContent = "";
if (response.response() != null && response.response().getResult() != null) {
responseContent = response.response().getResult().getOutput().getContent();
}
log.info("[AI调用] 响应: latency={}ms, content={}",
latency,
responseContent.length() > 100 ? responseContent.substring(0, 100) + "..." : responseContent);
return response;
}
}内置的MessageChatMemoryAdvisor(多轮对话记忆):
@Service
public class ConversationService {
private final ChatClient chatClient;
private final ChatMemory chatMemory;
public ConversationService(ChatClient.Builder builder) {
// 使用InMemoryChatMemory(生产用RedisChatMemory)
this.chatMemory = new InMemoryChatMemory();
this.chatClient = builder
.defaultSystem("你是一个有帮助的助手,记住对话历史")
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory),
new SimpleLoggerAdvisor()
)
.build();
}
public String chat(String sessionId, String message) {
return chatClient.prompt()
.user(message)
.advisors(spec -> spec.param(
AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY,
sessionId
))
.call()
.content();
}
}核心变化三:Function Calling全新API
0.x的函数调用写起来很繁琐,1.0简化很多:
// 0.x 旧方式(繁琐)
@Bean
public FunctionCallback weatherFunction() {
return FunctionCallbackWrapper.builder(new WeatherService())
.withName("getWeather")
.withDescription("获取天气信息")
.withResponseConverter(Object::toString)
.build();
}
// 1.0 新方式一:@Bean注册函数
@Bean
@Description("根据城市名获取当前天气")
public Function<WeatherRequest, WeatherResponse> getWeatherFunction() {
return request -> {
// 实际调用天气API
return weatherApiService.getWeather(request.city());
};
}
// 1.0 新方式二:直接在调用时注册(更灵活)
@Service
@Slf4j
public class WeatherChatService {
private final ChatClient chatClient;
public WeatherChatService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public String getWeatherInfo(String userQuestion) {
return chatClient.prompt()
.user(userQuestion)
.functions(FunctionCallback.builder()
.function("getCurrentWeather", (WeatherRequest req) -> {
log.info("查询天气: city={}", req.city());
return new WeatherResponse(req.city(), "晴", 25, "℃");
})
.description("获取指定城市的当前天气")
.inputType(WeatherRequest.class)
.build()
)
.call()
.content();
}
record WeatherRequest(String city) {}
record WeatherResponse(String city, String condition, int temperature, String unit) {}
}迁移指南:从0.x到1.0
Step 1:更新依赖
<!-- 旧版 -->
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.1</version>
</dependency>
<!-- 新版 1.0 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>BOM依赖管理:
<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>Step 2:替换注入方式
// 迁移:把所有 @Autowired OpenAiChatClient 替换为 ChatClient.Builder
// 搜索关键词:OpenAiChatClient, ChatClient chatClient(非Builder注入的)
// 批量替换正则(IntelliJ IDEA)
// 搜索:@Autowired\s+private OpenAiChatClient (\w+);
// 替换:private final ChatClient $1;\n\npublic ServiceName(ChatClient.Builder builder) {\n this.$1 = builder.build();\n}Step 3:更新配置属性
# 0.x配置(旧)
spring:
ai:
openai:
api-key: xxx
chat:
model: gpt-4
# 1.0配置(新)
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o-mini
temperature: 0.7
max-tokens: 2000Step 4:处理FunctionCallback变化
// 旧版FunctionCallbackWrapper全部替换
// 新版推荐FunctionCallback.builder()方式
// 或者直接用@Bean + @Description注解升级后的新能力清单
升级到1.0之后,你获得了哪些新能力:
结构化输出示例(1.0的BeanOutputConverter):
@Service
public class StructuredOutputService {
private final ChatClient chatClient;
public StructuredOutputService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
/**
* 让LLM直接返回Java对象,不需要手动解析JSON
*/
public ProductAnalysis analyzeProduct(String productDescription) {
return chatClient.prompt()
.user("分析以下产品描述,提取结构化信息:\n" + productDescription)
.call()
.entity(ProductAnalysis.class); // 1.0新特性,直接返回实体类
}
record ProductAnalysis(
String name,
String category,
List<String> features,
String targetAudience,
String priceRange
) {}
}常见升级报错速查
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
NoSuchBeanDefinitionException: OpenAiChatClient | 1.0移除了具体Client的Bean自动注册 | 改为注入ChatClient.Builder |
Method not found: AiClient.generate() | 接口已废弃 | 改用ChatClient.prompt().call() |
Configuration property 'spring.ai.openai.chat.model' | 配置key变了 | 改为spring.ai.openai.chat.options.model |
Cannot access ChatResponse.getOutput() | 返回结构变了 | 改为response.getResult().getOutput() |
FunctionCallbackWrapper not found | 类已移除 | 改用FunctionCallback.builder() |
小结
Spring AI 1.0不是小版本更新,是一次认真的API设计重构。核心理念变化:
- 更Fluent:Builder + 链式调用,代码可读性大幅提升
- 更可扩展:Advisor机制让横切关注点有了标准化的位置
- 更工程化:ChatMemory、结构化输出、内置Advisor,减少重复造轮子
小张升级完之后说:"虽然改代码改了两天,但新代码比原来清晰多了,值了。"
