Spring AI 初识
本文将详细介绍基于Spring Boot 3.4.2和Spring AI 1.0.0-M5构建的现代化AI应用项目。该项目展示了如何将AI能力无缝集成到Spring Boot应用中,提供了多种AI交互模式,包括简单对话、结构化数据响应和流式响应等。
官方文档链接:https://docs.spring.io/spring-ai/reference/api/chatmodel.html
技术栈分析
核心技术栈
- Java 17:采用最新的LTS版本,提供更好的性能和安全性
- Spring Boot 3.4.2:最新的Spring Boot版本,支持现代化开发模式
- Spring AI 1.0.0-M5:Spring官方AI框架,提供统一的AI服务抽象
依赖版本详情
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
</dependencies>
项目架构设计
分层架构
src/main/java/com/example/springbootv3_0demo/
├── controller/ # 控制器层
│ └── AiController.java
├── service/ # 服务层
│ └── AiService.java
├── advice/ # 异常处理
│ └── GlobalExceptionAdvice.java
└── resources/ # 配置文件
└── application.properties
核心组件分析
1. AiService - AI服务核心
@Service
public class AiService {
private final ChatModel chatModel;
@Autowired
public AiService(ChatModel chatModel) {
this.chatModel = chatModel;
}
}
主要功能:
- 简单对话生成
- 系统提示模板对话
- 结构化数据响应
- 流式响应处理
2. AiController - REST API接口
@RestController
@RequestMapping("/api/ai")
public class AiController {
@Autowired
private AiService aiService;
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return aiService.generate(message);
}
}
API接口:
/api/ai/chat- 简单对话/api/ai/chat-with-prompt- 系统提示对话/api/ai/chat-rest-response- 结构化数据响应/api/ai/chatActorsFilms- 电影数据查询/api/ai/chat-stream- 流式响应
核心功能实现
1. 简单对话功能
public String generate(String message) {
return chatModel.call(message);
}
特点:
- 直接调用ChatModel的call方法
- 适用于简单的问答场景
- 返回纯文本响应
2. 系统提示模板对话
public String generateWithSystemPrompt(String userMessage) {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("""
你是一个资深{domain}专家,回答需满足以下要求:
1. 语言风格:{tone}
2. 回答长度:{length}
3. 用户问题:{userMessage}
""");
Prompt prompt = new Prompt(
systemPromptTemplate.createMessage(Map.of(
"domain", "科技",
"tone", "幽默",
"length", "不超过100字",
"userMessage", userMessage
))
);
return chatModel.call(prompt).getResult().getOutput().getText();
}
特点:
- 使用SystemPromptTemplate构建系统提示
- 支持参数化模板
- 可控制AI回答的风格和长度
- 专业化场景定制
3. 结构化数据响应
public record RestResponse(String title, String poetry, String genre, String theme) {
}
public RestResponse restResponse(String genre, String theme) {
BeanOutputConverter<RestResponse> converter = new BeanOutputConverter<>(RestResponse.class);
String promptString = """
Write me {genre} poetry about {theme}
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(promptString);
promptTemplate.add("genre", genre);
promptTemplate.add("theme", theme);
promptTemplate.add("format", converter.getFormat());
Generation result = chatModel.call(promptTemplate.create()).getResult();
return converter.convert(result.getOutput().getText());
}
public record ActorsFilms(String country,Integer year, List<Films> movies) {
}
public ActorsFilms restResponseSimple() {
// 简洁的方式实现,转换成定义的数据结构
// return ChatClient.create(chatModel).prompt()
// .user(u -> u.text("Generate the filmography of 5 movies for {actor}.")
// .param("actor", "Tom Hanks"))
// .call()
// .entity(ActorsFilms.class);
String promptString = """
给出{country}{year}中最出色的五部电影及其导演。
""";
Map<String, Object> map = Map.of("country", "美国", "year", "2023");
return ChatClient.create(chatModel).prompt()
// 打印出执行过程日志
.advisors(new SimpleLoggerAdvisor())
.user(u -> u.text(promptString)
.params(map))
.call()
.entity(ActorsFilms.class);
}
特点:
- 使用JDK 17 Record定义数据结构
- 通过BeanOutputConverter实现类型转换,可实现RESTful API的响应格式
- 支持复杂对象映射
- 自动JSON序列化/反序列化
4. 流式响应处理
public Flux<String> streamResponse() {
Flux<String> output = ChatClient.create(chatModel).prompt()
.user("讲一个笑话")
.stream()
.content();
return output;
}
特点:
- 使用Reactor的Flux实现流式响应
- 支持实时数据流处理
- 提升用户体验
- 减少服务器压力
配置详解
application.properties
# 服务器配置
server.port=9001
# 应用名称
spring.application.name=springboot-ai
# Spring AI调试日志
logging.level.org.springframework.ai=DEBUG
logging.level.org.springframework.ai.chat.client.advisor=DEBUG
# 硅基流动api配置
spring.ai.openai.base-url=https://api.siliconflow.cn
spring.ai.openai.api-key=sk-elyllxj*******************iipkxmgkpelm
# 硅基流动api模型配置
spring.ai.openai.chat.options.model=moonshotai/Kimi-K2-Instruct
#spring.ai.openai.chat.options.responseFormat.type=json_object
配置说明:
- base-url:使用SiliconFlow作为AI服务提供商
- model:使用moonshotai/Kimi-K2-Instruct模型
- responseFormat:支持JSON格式响应(当前已注释)
- 日志级别:便于调试和监控
专业注意事项
1. 版本兼容性
- Spring Boot 3.4.2 要求 Java 17+
- Spring AI 1.0.0-M5 是里程碑版本,可能存在API变更
- 建议在正式环境使用稳定版本
2. 性能优化
异步处理
@GetMapping("/chat-stream")
public Flux<String> chatStreamResponse() {
return aiService.streamResponse();
}
优化策略:
- 使用流式响应减少内存占用
- 异步处理提升并发能力
- 合理设置超时时间
- 实施请求限流
3. 错误处理
// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionAdvice {
public static final String OPEN_AI_CLIENT_RAISED_EXCEPTION = "Open AI client raised exception";
@ExceptionHandler({OpenAiApiClientErrorException.class, NonTransientAiException.class})
ProblemDetail handleOpenAiHttpException(Exception ex) {
HttpStatus status = Optional
.ofNullable(HttpStatus.resolve(500))
.orElse(HttpStatus.BAD_REQUEST);
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(status, ex.getMessage());
problemDetail.setTitle(OPEN_AI_CLIENT_RAISED_EXCEPTION);
return problemDetail;
}
}
错误处理要点:
- 处理AI服务不可用情况
- 网络超时和连接异常
- API配额限制
- 数据格式错误
5. 监控和日志
# 详细的Spring AI日志
logging.level.org.springframework.ai=DEBUG
logging.level.org.springframework.ai.chat.client.advisor=DEBUG
完整的代码:
package com.example.springbootv3_0demo.service;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.Map;
/**
* @author admin
*/
@Service
public class AiService {
private final ChatModel chatModel;
@Autowired
public AiService(ChatModel chatModel) {
this.chatModel = chatModel;
}
// 简单的直接调用
public String generate(String message) {
return chatModel.call(message);
}
// 使用系统提示模板
public String generateWithSystemPrompt(String userMessage) {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("""
你是一个资深{domain}专家,回答需满足以下要求:
1. 语言风格:{tone}
2. 回答长度:{length}
3. 用户问题:{userMessage}
""");
Prompt prompt = new Prompt(
systemPromptTemplate.createMessage(Map.of(
"domain", "科技",
"tone", "幽默",
"length", "不超过100字",
"userMessage", userMessage
))
);
return chatModel.call(prompt).getResult().getOutput().getText();
}
public record RestResponse(String title, String poetry, String genre, String theme) {
}
public RestResponse restResponse(String genre, String theme) {
String systemPrompt = """
Write me {genre} poetry about {theme}
""";
// PromptTemplate promptTemplate = new PromptTemplate();
// BeanOutputParser<RestResponse> poetryDtoBeanOutputParser = new BeanOutputParser<>(RestResponse.class);
BeanOutputConverter<RestResponse> converter = new BeanOutputConverter<>(RestResponse.class);
String promptString = """
Write me {genre} poetry about {theme}
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(promptString);
promptTemplate.add("genre", genre);
promptTemplate.add("theme", theme);
promptTemplate.add("format", converter.getFormat());
Generation result = chatModel.call(promptTemplate.create()).getResult();
return converter.convert(result.getOutput().getText());
}
public record Films(String movie, String director) {
}
public record ActorsFilms(String country,Integer year, List<Films> movies) {
}
public ActorsFilms restResponseSimple() {
// return ChatClient.create(chatModel).prompt()
// .user(u -> u.text("Generate the filmography of 5 movies for {actor}.")
// .param("actor", "Tom Hanks"))
// .call()
// .entity(ActorsFilms.class);
String promptString = """
给出{country}{year}中最出色的五部电影及其导演。
""";
Map<String, Object> map = Map.of("country", "美国", "year", "2023");
return ChatClient.create(chatModel).prompt()
.advisors(new SimpleLoggerAdvisor())
.user(u -> u.text(promptString)
.params(map))
.call()
.entity(ActorsFilms.class);
}
public Flux<String> streamResponse(){
Flux<String> output = ChatClient.create(chatModel).prompt()
.user("讲一个笑话")
.stream()
.content();
return output;
}
}
控制层
package com.example.springbootv3_0demo.controller;
import com.example.springbootv3_0demo.service.AiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/ai")
public class AiController {
@Autowired
private AiService aiService;
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return aiService.generate(message);
}
@GetMapping("/chat-with-prompt")
public String chatWithPrompt(@RequestParam String message) {
return aiService.generateWithSystemPrompt(message);
}
@GetMapping("/chat-rest-response")
public AiService.RestResponse chatRestResponse(@RequestParam String genre, @RequestParam String theme){
return aiService.restResponse(genre, theme);
}
@GetMapping("/chatActorsFilms")
public AiService.ActorsFilms chatActorsFilms(){
return aiService.restResponseSimple();
}
@GetMapping("/chat-stream")
public Flux<String> chatStreamResponse(){
return aiService.streamResponse();
}
}

浙公网安备 33010602011771号