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
  1. 包路径变更 :
// 0.8.x
import org.springframework.ai.client.AiClient;

// 1.0.x
import org.springframework.ai.chat.client.ChatClient;
  1. API 变更 :
  1. ​AiClient​​ → ChatClient
  2. 配置属性前缀统一为 spring.ai
  3. 响应式支持增强
  1. 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>

五、注意事项

  1. Java 版本 :必须使用 JDK 17+
  2. Spring Boot 3.2+ 推荐用于生产环境
  3. 快照版本 :避免在生产中使用 -SNAPSHOT 版本
  4. 向量存储 :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

密钥获取步骤:

  1. 访问 ​阿里云百炼
  2. 创建API-KEY
  3. 开通对应模型服务
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

密钥获取步骤:

  1. 访问 ​百度智能云千帆
  2. 创建应用获取API Key和Secret Key
  3. 确保有足够的额度
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

密钥获取步骤:

  1. 访问 ​讯飞开放平台
  2. 创建应用获取AppID、API Key、API Secret
  3. 开通星火大模型服务

三、代码示例

通用配置类
@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}

快速开始步骤:

  1. 选择需要的模型平台,注册并获取API密钥
  2. 创建Spring Boot项目,添加对应依赖
  3. 配置application.yml中的密钥
  4. 编写业务代码调用AI服务
  5. 使用环境变量管理敏感信息

这样你就可以快速搭建支持国内主流大模型的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测试
  1. 创建POST请求到 http://localhost:8080/api/ai/ask
  2. 设置Header: Content-Type: application/json
  3. 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();
    }
}

运行和部署

  1. 设置环境变量 (推荐):
export OPENAI_API_KEY=your-api-key
# 或者Windows
set OPENAI_API_KEY=your-api-key
  1. 运行应用 :
mvn spring-boot:run
# 或
java -jar target/spring-ai-demo-0.0.1-SNAPSHOT.jar
  1. 访问地址 :

注意事项

  1. API密钥安全 :不要将API密钥硬编码在代码中,使用环境变量或配置服务器
  2. 速率限制 :注意API调用频率限制
  3. 错误处理 :添加适当的异常处理
  4. 成本控制 :监控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 客户端(通常基于 WebClientRestTemplate)。

核心组件交互流程如下:

[您的应用] -> [PromptTemplate] -> [ChatClient] -> [模型API] -> [Response]
                                      ↑
                               [配置:API Key, URL, 模型名等]
2. 核心组件深度拆解
a) 模型 (Model)
  • 定位 :代表一个具体的、可用的 AI 模型实例。
  • 抽象 :在 SpringAI 中,模型通常不直接作为一个顶级接口,而是通过 ChatClientEmbeddingClient 的配置来指定。
  • 关键配置参数 :
  • ​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 是一个接口,有不同的实现代表不同角色:
  1. ​SystemMessage​​:系统消息,设定模型的角色或行为准则。
  2. ​UserMessage​​:用户消息,用户的查询或指令。
  3. ​AssistantMessage​​:助手消息,通常是模型之前的回复,用于多轮对话上下文。
  4. ​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 包含:
  1. ​String text​​:生成的文本内容。
  2. ​Map<String, Object> info​​:生成元数据,如 token 使用量、结束原因等。
  • 元数据 (ChatResponseMetadata) :本次响应的整体信息,如请求的 ID、模型名称、总 token 消耗等。
  • 工具调用 (List<ToolCall>) :如果模型决定调用函数/工具,会在此返回调用请求的详细信息(函数名、参数)。
  • 作用 :提供结构化的输出,方便开发者提取文本、分析用量和进行后续处理(如执行函数调用)。
d) 客户端 (Client)
  • 定位 :与 AI 服务交互的核心门户,是 SpringAI 抽象的关键。
  • 核心接口 :
  • ​ChatClient​​ :
  1. 方法 :ChatResponse call(Prompt prompt) 是核心方法。
  2. 职责 :接收 Prompt 对象,调用底层模型 API,返回结构化的 ChatResponse
  3. 流式响应 :还提供 Stream<ChatResponse> stream(Prompt prompt) 方法用于流式输出。
  • ​EmbeddingClient​​ :
  1. 方法 :List<Double> embed(String text)EmbeddingResponse call(EmbeddingRequest request)
  2. 职责 :将文本转换为向量(Embedding),用于语义搜索、聚类等任务。
  • ​ImageClient​​ :用于图像生成(如 DALL-E)。
  • ​AudioClient​​ :用于语音转录/合成。
  • 实现机制 :
  • 每个接口都有针对不同供应商的实现,如 OpenAiChatClient, AzureOpenAiChatClient, OllamaChatClient
  • 开发者通过依赖注入 ChatClient 接口,由 Spring 根据配置自动装配具体的实现 Bean。
  • 配置高度统一,主要通过 application.propertiesapplication.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();
    }
}

流程解析 :

  1. 构建输入 :PromptTemplate 将变量填入模板,创建出包含 UserMessagePrompt 对象。
  2. 发起调用 :业务代码调用 chatClient.call(prompt)ChatClient 接口的具体实现(如 OpenAiChatClient)负责:
  1. Prompt 中的消息列表转换为供应商特定的 API 请求格式(如 OpenAI 的 messages JSON 数组)。
  2. 添加配置的模型参数。
  3. 通过 HTTP 调用远程 API。
  1. 处理输出 :客户端接收 API 的原始 JSON 响应,将其解析并封装成统一的 ChatResponse 对象返回。
  2. 提取内容 :业务代码从 ChatResponse 中安全、方便地获取生成的文本。

4. 架构设计的优势
  1. 高度抽象与统一 :通过 ChatClient 等接口,屏蔽了不同 AI 供应商 API 的差异。
  2. 配置驱动 :模型切换、参数调整大部分通过配置文件完成,符合 Spring 哲学。
  3. 模块化清晰 :PromptResponseClient 职责分离,代码结构清晰。
  4. 易于测试 :可以轻松 Mock ChatClient 接口进行单元测试。
  5. 生态集成 :天然融入 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

主要特性

  1. 一键切换模型提供者 :通过修改 ai.model.provider 配置即可切换
  2. 动态参数调整 :支持运行时调整温度、token数等参数
  3. 参数范围验证 :确保动态参数在安全范围内
  4. 多环境支持 :不同环境使用不同配置
  5. 类型安全 :配置类提供类型安全的配置访问
  6. 热更新支持 :结合 Spring Cloud Config 可实现配置热更新

这种配置化方案使得AI模型切换和参数调整变得非常简单,只需要修改YAML配置即可,无需修改代码。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐