【Spring AI实战】第1章 Spring AI 环境搭建与核心架构
1. SpringAI 版本选型、Spring Boot 版本适配规则
Spring AI 的版本选型和 Spring Boot 的适配规则如下:
一、版本选型策略
1. Spring AI 版本演进
- 0.8.x :早期版本,API 变动较大
- 1.0.0 (2024年8月发布):首个稳定版本,API 基本稳定
- 当前推荐 :1.0.x 系列(如 1.0.1、1.0.2)
2. Spring Boot 版本要求
|
SPRING AI 版本 |
SPRING BOOT 版本要求 |
说明 |
|
1.0.x 系列 |
Spring Boot 3.2.x |
官方推荐组合 |
|
1.0.x 系列 |
Spring Boot 3.1.x |
支持,但建议升级到 3.2.x |
|
0.8.x 系列 |
Spring Boot 3.1.x |
已过时,不推荐新项目使用 |
二、推荐组合
生产环境推荐
<!-- Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version> <!-- 或更高 -->
</parent>
<!-- Spring AI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Spring AI 主要依赖
<!-- OpenAI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Azure OpenAI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Ollama -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
三、版本适配规则
1. Spring Boot 3.2.x + Spring AI 1.0.x
- 最佳实践 :功能完整,性能最优
- Java 要求 :JDK 17+
- 示例配置 :
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4
temperature: 0.7
2. 向后兼容性
- Spring AI 1.0.x 保持对 Spring Boot 3.1.x 的兼容
- 但某些新特性(如向量存储增强)需要 Spring Boot 3.2+
3. 版本冲突处理
<!-- 使用 BOM 管理版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
四、升级指南
从 0.8.x 升级到 1.0.x
- 包路径变更 :
// 0.8.x
import org.springframework.ai.client.AiClient;
// 1.0.x
import org.springframework.ai.chat.client.ChatClient;
- API 变更 :
-
AiClient →ChatClient - 配置属性前缀统一为
spring.ai - 响应式支持增强
- Maven/Gradle 配置更新 :
<!-- 移除旧版本 -->
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
</dependency>
<!-- 添加新版本 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
五、注意事项
- Java 版本 :必须使用 JDK 17+
- Spring Boot 3.2+ 推荐用于生产环境
- 快照版本 :避免在生产中使用
-SNAPSHOT版本 - 向量存储 :Spring AI 1.0+ 需要相应存储的适配器
六、最新版本检查
# 检查最新版本
curl -s https://api.github.com/repos/spring-projects/spring-ai/releases/latest | grep tag_name
# 或访问
# https://github.com/spring-projects/spring-ai/releases
# https://spring.io/projects/spring-ai
总结建议 :
- 新项目:Spring Boot 3.2.5+ + Spring AI 1.0.2+
- 现有项目:逐步升级到上述组合
- 关注 Spring AI 官方发布公告,及时更新小版本修复
2. 国内环境快速搭建:阿里云/百度/讯飞大模型密钥配置
我来为你介绍SpringAI在国内环境的快速搭建方法,特别是针对阿里云、百量和讯飞大模型的配置。
一、环境准备
项目初始化
<!-- Spring Boot 3.x + Spring AI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>0.8.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
添加对应模型依赖
<!-- 阿里云通义千问 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-alibaba-ai</artifactId>
</dependency>
<!-- 百度文心一言 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-baidu-qianfan</artifactId>
</dependency>
<!-- 讯飞星火 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-iflytek-ai</artifactId>
</dependency>
二、各平台密钥配置
1. 阿里云通义千问配置
# application.yml
spring:
ai:
alibaba:
ai:
chat:
enabled: true
api-key: ${ALIBABA_API_KEY}
# 从阿里云控制台获取
# https://dashscope.console.aliyun.com/
# 可选模型
chat:
options:
model: qwen-turbo # 可选:qwen-plus, qwen-max, qwen-turbo
temperature: 0.7
max-tokens: 2000
密钥获取步骤:
- 访问 阿里云百炼
- 创建API-KEY
- 开通对应模型服务
2. 百度千帆(文心一言)配置
spring:
ai:
baidu:
qianfan:
chat:
enabled: true
api-key: ${BAIDU_API_KEY}
secret-key: ${BAIDU_SECRET_KEY}
# 可选配置
chat:
options:
model: ERNIE-4.0-8K # 可选:ERNIE-3.5-8K, ERNIE-Speed-8K
temperature: 0.7
top-p: 0.8
密钥获取步骤:
- 访问 百度智能云千帆
- 创建应用获取API Key和Secret Key
- 确保有足够的额度
3. 讯飞星火配置
spring:
ai:
iflytek:
ai:
chat:
enabled: true
api-key: ${IFLYTEK_API_KEY}
api-secret: ${IFLYTEK_API_SECRET}
app-id: ${IFLYTEK_APP_ID}
chat:
options:
model: generalv3.5 # 可选:generalv3, generalv2, general
temperature: 0.7
max-tokens: 4096
密钥获取步骤:
- 访问 讯飞开放平台
- 创建应用获取AppID、API Key、API Secret
- 开通星火大模型服务
三、代码示例
通用配置类
@Configuration
public class AIConfig {
// 阿里云通义千问
@Bean
@ConditionalOnProperty(name = "spring.ai.alibaba.ai.chat.enabled", havingValue = "true")
public ChatClient alibabaChatClient(AlibabaChatProperties properties) {
return new AlibabaChatClient(properties);
}
// 百度千帆
@Bean
@ConditionalOnProperty(name = "spring.ai.baidu.qianfan.chat.enabled", havingValue = "true")
public ChatClient baiduChatClient(BaiduQianfanChatProperties properties) {
return new BaiduQianfanChatClient(properties);
}
// 讯飞星火
@Bean
@ConditionalOnProperty(name = "spring.ai.iflytek.ai.chat.enabled", havingValue = "true")
public ChatClient iflytekChatClient(IflytekAiChatProperties properties) {
return new IflytekAiChatClient(properties);
}
}
服务层封装
@Service
public class AIService {
private final Map<String, ChatClient> chatClients;
public AIService(List<ChatClient> clients) {
this.chatClients = clients.stream()
.collect(Collectors.toMap(
client -> client.getClass().getSimpleName(),
Function.identity()
));
}
public String chatWithAlibaba(String prompt) {
ChatClient client = chatClients.get("AlibabaChatClient");
return client.call(prompt);
}
public String chatWithBaidu(String prompt) {
ChatClient client = chatClients.get("BaiduQianfanChatClient");
return client.call(prompt);
}
public String chatWithIflytek(String prompt) {
ChatClient client = chatClients.get("IflytekAiChatClient");
return client.call(prompt);
}
// 流式响应
public Flux<String> streamChat(String prompt, String provider) {
ChatClient client = chatClients.get(provider + "ChatClient");
return client.stream(prompt)
.map(ChatResponse::getResults)
.flatMap(list -> Flux.fromIterable(list))
.map(Generation::getOutput)
.map(AssistantMessage::getContent);
}
}
控制器示例
@RestController
@RequestMapping("/api/ai")
public class AIController {
@Autowired
private AIService aiService;
@PostMapping("/chat")
public ResponseEntity<String> chat(
@RequestParam String provider,
@RequestBody ChatRequest request) {
String response = switch (provider.toLowerCase()) {
case "alibaba" -> aiService.chatWithAlibaba(request.getPrompt());
case "baidu" -> aiService.chatWithBaidu(request.getPrompt());
case "iflytek" -> aiService.chatWithIflytek(request.getPrompt());
default -> throw new IllegalArgumentException("不支持的AI提供商");
};
return ResponseEntity.ok(response);
}
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(
@RequestParam String provider,
@RequestParam String prompt) {
return aiService.streamChat(prompt, provider);
}
}
四、多模型切换策略
配置优先级策略
@Component
public class AIModelRouter {
@Value("${ai.provider.priority:alibaba,baidu,iflytek}")
private List<String> priority;
private final Map<String, ChatClient> clients;
public String getResponseWithFallback(String prompt) {
for (String provider : priority) {
try {
ChatClient client = clients.get(provider + "ChatClient");
return client.call(prompt);
} catch (Exception e) {
log.warn("{} 服务失败,尝试下一个", provider);
continue;
}
}
throw new RuntimeException("所有AI服务都不可用");
}
}
配置示例
ai:
provider:
priority: alibaba,baidu,iflytek
timeout: 10000 # 10秒超时
retry:
max-attempts: 3
backoff-delay: 1000
五、安全配置建议
使用环境变量
# .env 文件
ALIBABA_API_KEY=sk-xxx
BAIDU_API_KEY=xxx
BAIDU_SECRET_KEY=xxx
IFLYTEK_API_KEY=xxx
IFLYTEK_API_SECRET=xxx
IFLYTEK_APP_ID=xxx
Spring Cloud Config 配置
# config-server 配置加密
encrypt:
key: ${ENCRYPT_KEY}
# 客户端配置
spring:
cloud:
config:
uri: http://config-server:8888
ai:
alibaba:
ai:
chat:
api-key: '{cipher}加密后的密钥'
六、常见问题解决
网络代理配置
@Configuration
public class ProxyConfig {
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("proxy-host", 8080));
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setProxy(proxy);
return factory;
}
}
超时配置
spring:
ai:
# 全局超时配置
client:
connect-timeout: 5000
read-timeout: 30000
# 各平台单独配置
alibaba:
ai:
chat:
options:
timeout: 30000
监控和日志
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
logging:
level:
org.springframework.ai: DEBUG
org.springframework.web.client: DEBUG
七、Docker快速部署
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# docker-compose.yml
version: '3.8'
services:
spring-ai:
build: .
ports:
- "8080:8080"
environment:
- ALIBABA_API_KEY=${ALIBABA_API_KEY}
- BAIDU_API_KEY=${BAIDU_API_KEY}
- BAIDU_SECRET_KEY=${BAIDU_SECRET_KEY}
- IFLYTEK_API_KEY=${IFLYTEK_API_KEY}
- IFLYTEK_API_SECRET=${IFLYTEK_API_SECRET}
- IFLYTEK_APP_ID=${IFLYTEK_APP_ID}
快速开始步骤:
- 选择需要的模型平台,注册并获取API密钥
- 创建Spring Boot项目,添加对应依赖
- 配置application.yml中的密钥
- 编写业务代码调用AI服务
- 使用环境变量管理敏感信息
这样你就可以快速搭建支持国内主流大模型的SpringAI应用了。
3. 第一个SpringAI程序:实现AI问答接口
项目搭建
1.1 创建项目
使用 Spring Initializr (https://start.spring.io/ ) 选择:
- Spring Boot 3.2+
- 依赖:Spring Web, Spring AI
或使用 Maven/Gradle:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
<spring-ai.version>0.8.1</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- 或者使用其他AI提供商 -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置API密钥
application.yml
spring:
application:
name: spring-ai-demo
# OpenAI配置
ai:
openai:
api-key: ${OPENAI_API_KEY:your-api-key-here}
chat:
options:
model: gpt-3.5-turbo
temperature: 0.7
max-tokens: 1000
# 或者使用Ollama本地部署(免费)
#spring:
# ai:
# ollama:
# base-url: http://localhost:11434
# chat:
# options:
# model: llama2
创建问答接口
3.1 请求/响应DTO
// QuestionRequest.java
public class QuestionRequest {
private String question;
private String systemPrompt;
// 构造器、getter、setter
public QuestionRequest() {}
public QuestionRequest(String question) {
this.question = question;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getSystemPrompt() {
return systemPrompt;
}
public void setSystemPrompt(String systemPrompt) {
this.systemPrompt = systemPrompt;
}
}
// AiResponse.java
public class AiResponse {
private String answer;
private String model;
private Long tokensUsed;
// 构造器、getter、setter
public AiResponse() {}
public AiResponse(String answer) {
this.answer = answer;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public Long getTokensUsed() {
return tokensUsed;
}
public void setTokensUsed(Long tokensUsed) {
this.tokensUsed = tokensUsed;
}
}
3.2 服务层
// AiService.java
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class AiService {
private final ChatClient chatClient;
public AiService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String askSimpleQuestion(String question) {
return chatClient.call(question);
}
public AiResponse askQuestionWithSystemPrompt(QuestionRequest request) {
// 创建系统提示
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(
request.getSystemPrompt() != null ?
request.getSystemPrompt() :
"你是一个有用的AI助手,请用中文回答问题。"
);
Message systemMessage = systemPromptTemplate.createMessage();
// 创建用户消息
UserMessage userMessage = new UserMessage(request.getQuestion());
// 创建提示
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
// 调用AI
ChatResponse response = chatClient.call(prompt);
// 构建响应
AiResponse aiResponse = new AiResponse();
aiResponse.setAnswer(response.getResult().getOutput().getContent());
aiResponse.setModel("gpt-3.5-turbo"); // 实际可以从配置读取
aiResponse.setTokensUsed(response.getMetadata().getUsage().getTotalTokens());
return aiResponse;
}
public String askWithTemplate(String question, String template) {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(template);
Message systemMessage = systemPromptTemplate.createMessage();
UserMessage userMessage = new UserMessage(question);
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
}
3.3 控制器
// AiController.java
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/api/ai")
public class AiController {
private final AiService aiService;
public AiController(AiService aiService) {
this.aiService = aiService;
}
@PostMapping("/ask")
public ResponseEntity<AiResponse> askQuestion(@RequestBody QuestionRequest request) {
AiResponse response = aiService.askQuestionWithSystemPrompt(request);
return ResponseEntity.ok(response);
}
@GetMapping("/ask")
public ResponseEntity<AiResponse> askQuestion(@RequestParam String question) {
QuestionRequest request = new QuestionRequest(question);
request.setSystemPrompt("你是一个有用的AI助手,请用中文回答问题。");
AiResponse response = aiService.askQuestionWithSystemPrompt(request);
return ResponseEntity.ok(response);
}
@PostMapping("/simple")
public ResponseEntity<String> simpleAsk(@RequestParam String question) {
String answer = aiService.askSimpleQuestion(question);
return ResponseEntity.ok(answer);
}
@PostMapping("/translate")
public ResponseEntity<String> translate(
@RequestParam String text,
@RequestParam(defaultValue = "中文") String targetLanguage) {
String template = "你是一个专业的翻译助手。请将用户输入的内容翻译成{language},保持原意不变。";
String translated = aiService.askWithTemplate(
text,
template.replace("{language}", targetLanguage)
);
return ResponseEntity.ok(translated);
}
@PostMapping("/summarize")
public ResponseEntity<String> summarize(@RequestParam String text) {
String template = "你是一个文本摘要助手。请用简洁的语言总结以下内容,不超过100字:";
String summary = aiService.askWithTemplate(text, template);
return ResponseEntity.ok(summary);
}
}
3.4 主应用类
// SpringAiApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringAiApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiApplication.class, args);
}
}
测试接口
启动应用后,可以使用以下方式测试:
4.1 使用curl测试
# GET请求
curl "http://localhost:8080/api/ai/ask?question=Java是什么?"
# POST请求
curl -X POST http://localhost:8080/api/ai/ask \
-H "Content-Type: application/json" \
-d '{
"question": "Spring Boot有什么优势?",
"systemPrompt": "你是一个Java技术专家,请详细解释。"
}'
# 翻译功能
curl -X POST "http://localhost:8080/api/ai/translate?text=Hello World&targetLanguage=中文"
# 摘要功能
curl -X POST "http://localhost:8080/api/ai/summarize?text=Spring Boot是一个用于创建独立的、生产级别的Spring应用的框架..."
4.2 使用Postman测试
- 创建POST请求到
http://localhost:8080/api/ai/ask - 设置Header:
Content-Type: application/json - Body (raw JSON):
{
"question": "如何学习Spring Boot?",
"systemPrompt": "你是一个经验丰富的Java导师,请给出学习建议。"
}
进阶功能
5.1 添加流式响应
// 在AiController中添加
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamAsk(@RequestParam String question) {
Prompt prompt = new Prompt(question);
return chatClient.stream(prompt)
.map(chatResponse -> chatResponse.getResult().getOutput().getContent());
}
5.2 添加配置类
// AiConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@Configuration
public class AiConfig {
@Value("${spring.ai.openai.api-key}")
private String apiKey;
@Bean
public OpenAiChatOptions openAiChatOptions() {
return OpenAiChatOptions.builder()
.withModel("gpt-3.5-turbo")
.withTemperature(0.7)
.withMaxTokens(1000)
.build();
}
}
运行和部署
- 设置环境变量 (推荐):
export OPENAI_API_KEY=your-api-key
# 或者Windows
set OPENAI_API_KEY=your-api-key
- 运行应用 :
mvn spring-boot:run
# 或
java -jar target/spring-ai-demo-0.0.1-SNAPSHOT.jar
- 访问地址 :
- 应用主页:http://localhost:8080
- API文档(如果添加了SpringDoc):http://localhost:8080/swagger-ui.html
注意事项
- API密钥安全 :不要将API密钥硬编码在代码中,使用环境变量或配置服务器
- 速率限制 :注意API调用频率限制
- 错误处理 :添加适当的异常处理
- 成本控制 :监控API使用成本,特别是使用OpenAI等付费服务时
这个简单的Spring AI程序提供了基本的问答功能,你可以根据需要扩展更多功能,如:
- 添加对话历史管理
- 实现多轮对话
- 集成向量数据库
- 添加文件上传和处理功能
- 实现RAG(检索增强生成)
4. SpringAI 核心架构拆解:模型、Prompt、Response、Client 核心组件
SpringAI 是一个用于简化 AI 应用开发的 Spring 项目,它通过提供一系列抽象和标准接口,让开发者能更便捷地集成各种大语言模型和 AI 服务。其核心架构围绕几个关键组件构建,下面我们来详细拆解:
1. 核心架构分层与组件概览
SpringAI 采用了典型的分层和面向接口的设计,主要分为以下几层:
- 应用层/业务层 :您的业务代码。
- 抽象层 :SpringAI 定义的核心接口(
ChatClient,EmbeddingClient,PromptTemplate等)。 - 实现层 :针对不同 AI 提供商(OpenAI, Azure OpenAI, Ollama, Anthropic 等)的具体实现。
- 通信层 :底层 HTTP 客户端(通常基于
WebClient或RestTemplate)。
核心组件交互流程如下:
[您的应用] -> [PromptTemplate] -> [ChatClient] -> [模型API] -> [Response]
↑
[配置:API Key, URL, 模型名等]
2. 核心组件深度拆解
a) 模型 (Model)
- 定位 :代表一个具体的、可用的 AI 模型实例。
- 抽象 :在 SpringAI 中,模型通常不直接作为一个顶级接口,而是通过
ChatClient或EmbeddingClient的配置来指定。 - 关键配置参数 :
-
spring.ai.openai.chat.model:指定使用的模型名称,如gpt-4o,gpt-3.5-turbo。 -
spring.ai.openai.base-url:模型服务的端点地址(便于连接 OpenAI 兼容的 API)。 - 其他模型特定参数,如
temperature,topP,maxTokens等,可通过ChatOptions进行配置。
- 作用 :模型是能力的提供者,所有交互的“大脑”。SpringAI 通过客户端配置将其抽象化,使得切换模型(例如从 OpenAI 切换到 Ollama)时,业务代码改动极小。
b) 提示词 (Prompt)
- 定位 :封装了发送给模型的输入信息,是交互的起点。
- 核心类 :
org.springframework.ai.core.Prompt - 数据结构 :一个
Prompt对象包含:
- 消息列表 (
List<Message>) :这是核心内容。Message是一个接口,有不同的实现代表不同角色:
-
SystemMessage:系统消息,设定模型的角色或行为准则。 -
UserMessage:用户消息,用户的查询或指令。 -
AssistantMessage:助手消息,通常是模型之前的回复,用于多轮对话上下文。 -
FunctionMessage /ToolMessage:用于函数/工具调用的消息。
- 可选参数 (
PromptExecutionOptions) :本次请求特有的参数,会覆盖客户端的默认配置。
- 工具
PromptTemplate:
- 定位 :用于动态生成
Prompt的模板引擎。 - 作用 :将变量嵌入文本模板,生成最终的提示词。支持多种方言(如 Mustache, FreeMarker, 默认是简单的
{variable}风格)。 - 示例 :
PromptTemplate template = new PromptTemplate("请为{product}写一段{style}风格的广告语。");
Prompt prompt = template.create(Map.of("product", "智能手机", "style", "科幻"));
c) 响应 (Response)
- 定位 :封装模型对
Prompt的处理结果。 - 核心类 :
org.springframework.ai.core.AiResponse(在最新版本中,ChatClient直接返回ChatResponse)。 - 数据结构 :以
ChatResponse为例,它包含:
- 结果列表 (
List<Generation>) :一次调用可能生成多个候选结果(通过n参数控制)。Generation包含:
-
String text:生成的文本内容。 -
Map<String, Object> info:生成元数据,如 token 使用量、结束原因等。
- 元数据 (
ChatResponseMetadata) :本次响应的整体信息,如请求的 ID、模型名称、总 token 消耗等。 - 工具调用 (
List<ToolCall>) :如果模型决定调用函数/工具,会在此返回调用请求的详细信息(函数名、参数)。
- 作用 :提供结构化的输出,方便开发者提取文本、分析用量和进行后续处理(如执行函数调用)。
d) 客户端 (Client)
- 定位 :与 AI 服务交互的核心门户,是 SpringAI 抽象的关键。
- 核心接口 :
ChatClient :
- 方法 :
ChatResponse call(Prompt prompt)是核心方法。 - 职责 :接收
Prompt对象,调用底层模型 API,返回结构化的ChatResponse。 - 流式响应 :还提供
Stream<ChatResponse> stream(Prompt prompt)方法用于流式输出。
EmbeddingClient :
- 方法 :
List<Double> embed(String text)或EmbeddingResponse call(EmbeddingRequest request)。 - 职责 :将文本转换为向量(Embedding),用于语义搜索、聚类等任务。
-
ImageClient :用于图像生成(如 DALL-E)。 -
AudioClient :用于语音转录/合成。
- 实现机制 :
- 每个接口都有针对不同供应商的实现,如
OpenAiChatClient,AzureOpenAiChatClient,OllamaChatClient。 - 开发者通过依赖注入
ChatClient接口,由 Spring 根据配置自动装配具体的实现 Bean。 - 配置高度统一,主要通过
application.properties或application.yml完成。
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
model: gpt-4o
options:
temperature: 0.7
3. 组件协同工作流程示例
@Service
public class MyAIService {
private final ChatClient chatClient; // 注入抽象的客户端
public MyAIService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String generateAd(String product, String style) {
// 1. 使用 PromptTemplate 构建 Prompt
PromptTemplate template = new PromptTemplate("请为{product}写一段{style}风格的广告语。");
Prompt prompt = template.create(Map.of("product", product, "style", style));
// 2. 通过 Client 调用模型
ChatResponse response = chatClient.call(prompt);
// 3. 从 Response 中提取结果
return response.getResult().getOutput().getContent();
}
}
流程解析 :
- 构建输入 :
PromptTemplate将变量填入模板,创建出包含UserMessage的Prompt对象。 - 发起调用 :业务代码调用
chatClient.call(prompt)。ChatClient接口的具体实现(如OpenAiChatClient)负责:
- 将
Prompt中的消息列表转换为供应商特定的 API 请求格式(如 OpenAI 的messagesJSON 数组)。 - 添加配置的模型参数。
- 通过 HTTP 调用远程 API。
- 处理输出 :客户端接收 API 的原始 JSON 响应,将其解析并封装成统一的
ChatResponse对象返回。 - 提取内容 :业务代码从
ChatResponse中安全、方便地获取生成的文本。
4. 架构设计的优势
- 高度抽象与统一 :通过
ChatClient等接口,屏蔽了不同 AI 供应商 API 的差异。 - 配置驱动 :模型切换、参数调整大部分通过配置文件完成,符合 Spring 哲学。
- 模块化清晰 :
Prompt、Response、Client职责分离,代码结构清晰。 - 易于测试 :可以轻松 Mock
ChatClient接口进行单元测试。 - 生态集成 :天然融入 Spring Boot 的自动配置、依赖注入、监控等生态。
总结
SpringAI 的核心架构可以概括为:以 Client 为门户,以 Prompt 为标准化输入,以 Response 为标准化输出,通过配置指向具体的 Model 。这套设计使得在 Spring 应用中集成和切换 AI 能力变得像使用数据库 (JdbcTemplate) 或消息队列 (JmsTemplate) 一样简单和熟悉,极大地提升了开发效率。
5. 配置化开发:yml一键切换大模型、动态调整参数
Spring AI 提供了配置化的方式来实现大模型切换和参数动态调整。以下是完整的配置化开发方案:
项目依赖配置
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
</dependency>
YAML 配置文件
# application.yml
spring:
ai:
# 模型提供者配置
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com/v1
chat:
options:
model: gpt-4-turbo
temperature: 0.7
max-tokens: 2000
azure:
openai:
api-key: ${AZURE_OPENAI_KEY}
endpoint: https://your-resource.openai.azure.com/
chat:
options:
deployment-name: gpt-4
temperature: 0.8
max-tokens: 1000
ollama:
base-url: http://localhost:11434
chat:
options:
model: llama3
temperature: 0.5
num-predict: 512
# 自定义配置
ai:
model:
# 当前使用的模型提供者
provider: openai # 可选: openai, azure, ollama
# 动态参数配置
dynamic:
enabled: true
# 参数范围限制
limits:
temperature:
min: 0.0
max: 2.0
max-tokens:
min: 1
max: 4000
配置类定义
@Configuration
@ConfigurationProperties(prefix = "ai.model")
@Data
public class AiModelConfig {
private String provider = "openai";
private DynamicConfig dynamic = new DynamicConfig();
@Data
public static class DynamicConfig {
private boolean enabled = false;
private Limits limits = new Limits();
@Data
public static class Limits {
private Range temperature = new Range(0.0, 2.0);
private Range maxTokens = new Range(1, 4000);
@Data
@AllArgsConstructor
public static class Range {
private Double min;
private Double max;
public void validate(String paramName, Double value) {
if (value < min || value > max) {
throw new IllegalArgumentException(
String.format("%s must be between %s and %s",
paramName, min, max));
}
}
}
}
}
}
模型工厂类
@Component
@Slf4j
public class AiModelFactory {
@Autowired
private AiModelConfig aiModelConfig;
@Autowired(required = false)
private OpenAiChatClient openAiChatClient;
@Autowired(required = false)
private AzureOpenAiChatClient azureOpenAiChatClient;
@Autowired(required = false)
private OllamaChatClient ollamaChatClient;
public ChatClient getChatClient() {
return getChatClient(aiModelConfig.getProvider());
}
public ChatClient getChatClient(String provider) {
return switch (provider.toLowerCase()) {
case "openai" -> {
log.info("Using OpenAI provider");
yield openAiChatClient;
}
case "azure" -> {
log.info("Using Azure OpenAI provider");
yield azureOpenAiChatClient;
}
case "ollama" -> {
log.info("Using Ollama provider");
yield ollamaChatClient;
}
default -> throw new IllegalArgumentException(
"Unsupported AI provider: " + provider);
};
}
public ChatClient getChatClientWithOptions(ChatOptions options) {
ChatClient client = getChatClient();
if (client instanceof OpenAiChatClient openAiClient) {
return openAiClient.withDefaultOptions(options);
} else if (client instanceof AzureOpenAiChatClient azureClient) {
return azureClient.withDefaultOptions(options);
} else if (client instanceof OllamaChatClient ollamaClient) {
return ollamaClient.withDefaultOptions(options);
}
return client;
}
}
动态参数服务
@Service
@Slf4j
public class DynamicAiService {
@Autowired
private AiModelFactory aiModelFactory;
@Autowired
private AiModelConfig aiModelConfig;
/**
* 动态调用AI服务
*/
public String generate(String prompt, Map<String, Object> dynamicParams) {
ChatOptions options = buildChatOptions(dynamicParams);
ChatClient client = aiModelFactory.getChatClientWithOptions(options);
return client.call(prompt);
}
/**
* 构建动态参数
*/
private ChatOptions buildChatOptions(Map<String, Object> params) {
ChatOptionsBuilder builder = ChatOptionsBuilder.builder();
if (aiModelConfig.getDynamic().isEnabled()) {
// 动态设置温度
if (params.containsKey("temperature")) {
Double temperature = convertToDouble(params.get("temperature"));
aiModelConfig.getDynamic().getLimits().getTemperature()
.validate("temperature", temperature);
builder.withTemperature(temperature.floatValue());
}
// 动态设置最大token数
if (params.containsKey("maxTokens")) {
Integer maxTokens = convertToInteger(params.get("maxTokens"));
aiModelConfig.getDynamic().getLimits().getMaxTokens()
.validate("maxTokens", maxTokens.doubleValue());
builder.withMaxTokens(maxTokens);
}
// 其他动态参数
if (params.containsKey("topP")) {
builder.withTopP(convertToFloat(params.get("topP")));
}
if (params.containsKey("frequencyPenalty")) {
builder.withFrequencyPenalty(convertToFloat(params.get("frequencyPenalty")));
}
if (params.containsKey("presencePenalty")) {
builder.withPresencePenalty(convertToFloat(params.get("presencePenalty")));
}
}
return builder.build();
}
/**
* 切换模型提供者
*/
public void switchProvider(String provider) {
if (!List.of("openai", "azure", "ollama").contains(provider.toLowerCase())) {
throw new IllegalArgumentException("Invalid provider: " + provider);
}
aiModelConfig.setProvider(provider);
log.info("Switched AI provider to: {}", provider);
}
/**
* 获取当前配置
*/
public Map<String, Object> getCurrentConfig() {
Map<String, Object> config = new HashMap<>();
config.put("provider", aiModelConfig.getProvider());
config.put("dynamicEnabled", aiModelConfig.getDynamic().isEnabled());
config.put("limits", aiModelConfig.getDynamic().getLimits());
return config;
}
// 辅助转换方法
private Double convertToDouble(Object value) {
if (value instanceof Number number) {
return number.doubleValue();
}
return Double.parseDouble(value.toString());
}
private Integer convertToInteger(Object value) {
if (value instanceof Number number) {
return number.intValue();
}
return Integer.parseInt(value.toString());
}
private Float convertToFloat(Object value) {
if (value instanceof Number number) {
return number.floatValue();
}
return Float.parseFloat(value.toString());
}
}
REST 控制器
@RestController
@RequestMapping("/api/ai")
@Slf4j
public class AiController {
@Autowired
private DynamicAiService dynamicAiService;
/**
* 普通AI调用
*/
@PostMapping("/chat")
public ResponseEntity<AiResponse> chat(@RequestBody ChatRequest request) {
String response = dynamicAiService.generate(request.getPrompt(), request.getParams());
return ResponseEntity.ok(new AiResponse(response));
}
/**
* 动态参数AI调用
*/
@PostMapping("/chat/dynamic")
public ResponseEntity<AiResponse> chatWithDynamicParams(
@RequestBody DynamicChatRequest request) {
String response = dynamicAiService.generate(
request.getPrompt(),
request.getParams()
);
return ResponseEntity.ok(new AiResponse(response));
}
/**
* 切换模型提供者
*/
@PostMapping("/switch-provider")
public ResponseEntity<Map<String, Object>> switchProvider(
@RequestParam String provider) {
dynamicAiService.switchProvider(provider);
return ResponseEntity.ok(Collections.singletonMap(
"message", "Provider switched to: " + provider));
}
/**
* 获取当前配置
*/
@GetMapping("/config")
public ResponseEntity<Map<String, Object>> getConfig() {
return ResponseEntity.ok(dynamicAiService.getCurrentConfig());
}
/**
* 更新动态参数配置
*/
@PostMapping("/config/dynamic")
public ResponseEntity<Map<String, Object>> updateDynamicConfig(
@RequestBody DynamicConfigUpdate update) {
// 实现配置更新逻辑
return ResponseEntity.ok(Collections.singletonMap(
"message", "Dynamic config updated"));
}
// DTO 类
@Data
public static class ChatRequest {
private String prompt;
private Map<String, Object> params = new HashMap<>();
}
@Data
public static class DynamicChatRequest {
private String prompt;
private Map<String, Object> params = new HashMap<>();
}
@Data
public static class AiResponse {
private String content;
private long timestamp;
public AiResponse(String content) {
this.content = content;
this.timestamp = System.currentTimeMillis();
}
}
@Data
public static class DynamicConfigUpdate {
private Boolean enabled;
private Map<String, Object> limits;
}
}
热更新配置(可选)
@Configuration
@RefreshScope
public class AiRefreshConfig {
@Bean
@RefreshScope
public AiModelConfig aiModelConfig() {
return new AiModelConfig();
}
@Bean
@RefreshScope
public DynamicAiService dynamicAiService(AiModelConfig aiModelConfig) {
return new DynamicAiService(aiModelConfig);
}
}
使用示例
@Service
public class BusinessService {
@Autowired
private DynamicAiService aiService;
public void processWithDefault() {
String response = aiService.generate("Hello, AI!", Map.of());
System.out.println(response);
}
public void processWithDynamicParams() {
Map<String, Object> params = new HashMap<>();
params.put("temperature", 0.9); // 更高的创造性
params.put("maxTokens", 500); // 限制输出长度
String response = aiService.generate("写一首诗", params);
System.out.println(response);
}
public void switchToOllama() {
aiService.switchProvider("ollama");
// 现在所有请求都会使用 Ollama
}
}
环境特定配置
# application-dev.yml
spring:
ai:
openai:
chat:
options:
model: gpt-3.5-turbo # 开发环境使用便宜模型
temperature: 0.8
# application-prod.yml
spring:
ai:
openai:
chat:
options:
model: gpt-4-turbo # 生产环境使用更强大的模型
temperature: 0.7
主要特性
- 一键切换模型提供者 :通过修改
ai.model.provider配置即可切换 - 动态参数调整 :支持运行时调整温度、token数等参数
- 参数范围验证 :确保动态参数在安全范围内
- 多环境支持 :不同环境使用不同配置
- 类型安全 :配置类提供类型安全的配置访问
- 热更新支持 :结合 Spring Cloud Config 可实现配置热更新
这种配置化方案使得AI模型切换和参数调整变得非常简单,只需要修改YAML配置即可,无需修改代码。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)