1. 引言:Spring AI 2.0发布背景与版本定位

2024年,Spring官方正式发布Spring AI 2.0重大版本升级,这标志着Spring生态在AI应用开发领域的全面成熟。作为Spring家族的官方AI框架,Spring AI 2.0基于Spring Boot 4.0 + Spring Framework 7.0 + Jakarta EE 11构建,在架构设计、功能特性、性能表现等方面实现了质的飞跃。

相比于1.x版本,Spring AI 2.0不再是简单的模型API封装,而是进化为一套完整的AI应用开发基础设施。它提供了从模型接入、提示工程、工具调用、RAG架构到可观测性的全链路解决方案,让Java开发者能够以熟悉的Spring编程范式快速构建生产级AI应用。

本文将从架构演进、核心特性、代码实战三个维度,带你全面掌握Spring AI 2.0的精髓。


2. 五大核心新特性详解

Spring AI 2.0带来了五大核心新特性,全面提升了AI应用开发的灵活性和生产级能力:

特性 1.x版本 2.0版本 核心价值
Tool Calling 分散式配置,各模型独立实现 统一ToolCallingAdvisor架构,自动注册 一次编写,多模型兼容
结构化输出 全局配置,粒度较粗 EntityParamSpec按调用粒度配置,支持strictMode 精准控制输出格式
HTTP Client 内置固定客户端,不可配置 完全可配置化,支持连接池、超时、自定义客户端 适应不同生产环境需求
Chat Memory 简单内存存储,驱逐策略单一 turn-boundary驱逐策略,持久化优化 对话管理更智能
可观测性 基本日志输出 OpenTelemetry集成,指标监控 生产级运维能力

3. 架构演进:从1.x到2.0的架构重构

3.1 模块化RAG架构

Spring AI 2.0对RAG(检索增强生成)架构进行了彻底的模块化重构,从1.x的黑盒式Pipeline进化为可编排的模块化架构:

Pre-Retrieval → Retrieval → Post-Retrieval → Augmentation
  • Pre-Retrieval(检索前):查询重写、query转换、意图识别
  • Retrieval(检索层):向量检索、关键词检索、混合检索
  • Post-Retrieval(检索后):文档重排序、去重、片段截取
  • Augmentation(增强层):Prompt组装、上下文注入

3.2 Advisor体系重构

2.0版本将Advisor体系提升为核心抽象,引入Marker接口模式,ToolCallingAdvisor成为第一公民:

  • Marker接口:Advisor标记接口,统一切面编程模型
  • ToolCallingAdvisor核心化:工具调用从附加功能变为核心能力
  • 渐进式工具披露:根据对话上下文动态决定暴露哪些工具

3.3 模型层优化

模型层进行了深度优化,Options体系全面重构,内部方法更好地封装:

  • 统一的Options配置模型
  • 各模型提供商的差异被有效隔离
  • 支持动态切换模型提供商

4. 核心能力深度解析

4.1 结构化输出:EntityParamSpec实战

EntityParamSpec是Spring AI 2.0结构化输出的核心,支持按调用粒度配置:

  • strictMode:严格模式,确保输出完全符合JSON Schema
  • additionalProperties:控制是否允许额外属性
  • 调用粒度配置:每次调用可独立配置结构化参数

4.2 工具调用:统一架构与渐进式披露

统一的ToolCallingAdvisor架构带来三大优势:

  1. 自动注册:标注@Tool注解的Bean自动注册为可用工具
  2. 渐进式披露:根据对话上下文智能选择暴露的工具集合
  3. MCP集成:支持Model Context Protocol,实现工具的跨模型复用

4.3 RAG模块化:从黑盒到可编排

模块化RAG让开发者可以:

  • 自定义检索前处理逻辑
  • 插拔式替换检索引擎
  • 灵活配置后处理管道
  • 动态调整Prompt增强策略

5. 破坏性变更与迁移指南

5.1 底座升级影响

Spring AI 2.0的底层依赖全面升级:

  • Spring Boot 3.x → Spring Boot 4.0
  • Spring Framework 6.x → Spring Framework 7.0
  • Jakarta EE 9 → Jakarta EE 11
  • Jackson 2 → Jackson 3

5.2 API变更清单

变更类型 1.x API 2.0替代方案
工具调用 internalToolExecutionEnabled ToolCallingAdvisor配置
工具调用 toolNames() 渐进式工具披露机制
工具解析 SpringBeanToolCallbackResolver 自动注册机制
SDK依赖 Azure OpenAI/OpenAI SDK模块 移除,改用统一API
Anthropic 自定义实现 切换官方SDK

5.3 三阶段迁移路径

阶段一:底座升级 - 先升级Spring Boot到4.0,确保基础应用正常运行
阶段二:API替换 - 逐步替换废弃API,使用新的ToolCallingAdvisor和EntityParamSpec
阶段三:架构优化 - 利用模块化RAG和可观测性能力优化生产部署


6. 生产级实战代码

6.1 快速上手:完整项目配置

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>4.0.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>spring-ai-2-demo</artifactId>
    <version>1.0.0</version>
    <name>Spring AI 2.0 Demo</name>
    
    <properties>
        <java.version>21</java.version>
        <spring-ai.version>2.0.0</spring-ai.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Spring AI 2.0 核心 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        
        <!-- OpenAI 模型接入 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        
        <!-- 向量存储 - PGVector -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        
        <!-- 可观测性 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bridge-otel</artifactId>
        </dependency>
        
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yml配置:

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4o
          temperature: 0.7
    vectorstore:
      pgvector:
        index-type: hnsw
        distance-type: cosine
        dimensions: 1536

  datasource:
    url: jdbc:postgresql://localhost:5432/vector_db
    username: postgres
    password: postgres

# 可观测性配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  tracing:
    sampling:
      probability: 1.0

6.2 结构化输出示例

实体定义与EntityParamSpec使用:

package com.example.springai2.dto;

import lombok.Data;
import java.util.List;

/**
 * 用户信息提取结果实体
 */
@Data
public class UserProfile {
    private String name;           // 姓名
    private Integer age;           // 年龄
    private String occupation;     // 职业
    private List<String> skills;   // 技能列表
    private String summary;        // 个人简介
}
package com.example.springai2.service;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallingAdvisor;
import org.springframework.ai.entityparam.EntityParamSpec;
import org.springframework.stereotype.Service;
import com.example.springai2.dto.UserProfile;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.List;

/**
 * 结构化输出服务
 */
@Service
public class StructuredOutputService {

    private final ChatClient chatClient;

    @Resource
    private ChatModel chatModel;

    public StructuredOutputService(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    /**
     * 提取用户信息 - 使用EntityParamSpec严格模式
     */
    public UserProfile extractUserProfile(String text) {
        // 按调用粒度配置EntityParamSpec
        EntityParamSpec<UserProfile> spec = EntityParamSpec.builder(
                        new TypeReference<UserProfile>() {})
                .strictMode(true)           // 启用严格模式
                .additionalProperties(false) // 禁止额外属性
                .build();

        return chatClient.prompt()
                .user("请从以下文本中提取用户信息:\n" + text)
                .entityParamSpec(spec)      // 指定结构化输出规范
                .call()
                .entity(UserProfile.class);
    }

    /**
     * 批量提取用户信息 - 列表形式
     */
    public List<UserProfile> extractUserProfilesBatch(String text) {
        EntityParamSpec<List<UserProfile>> spec = EntityParamSpec.builder(
                        new TypeReference<List<UserProfile>>() {})
                .strictMode(true)
                .build();

        return chatClient.prompt()
                .user("请从以下招聘简历文本中提取所有候选人的信息:\n" + text)
                .entityParamSpec(spec)
                .call()
                .entity(new TypeReference<List<UserProfile>>() {});
    }
}

6.3 工具调用进阶示例

@Tool注解定义工具:

package com.example.springai2.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 天气查询工具 - 自动注册为AI可用工具
 */
@Component
public class WeatherTools {

    /**
     * 查询指定城市的当前天气
     * @param city 城市名称
     * @return 天气信息
     */
    @Tool(name = "get_current_weather", 
          description = "获取指定城市的当前天气信息,包括温度、湿度、天气状况")
    public String getCurrentWeather(
            @ToolParam(description = "城市名称,如:北京、上海、深圳", required = true) 
            String city) {
        
        // 模拟天气查询,实际可对接第三方天气API
        return String.format("""
                城市:%s
                温度:26°C
                湿度:65%%
                天气:多云转晴
                风力:东风3级
                更新时间:%s
                """, city, LocalDateTime.now()
                    .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }

    /**
     * 查询未来天气预报
     * @param city 城市名称
     * @param days 预报天数
     */
    @Tool(name = "get_weather_forecast", 
          description = "获取指定城市未来几天的天气预报")
    public String getWeatherForecast(
            @ToolParam(description = "城市名称", required = true) String city,
            @ToolParam(description = "预报天数,最大7天", required = false) Integer days) {
        
        int forecastDays = days != null ? Math.min(days, 7) : 3;
        StringBuilder sb = new StringBuilder();
        sb.append(city).append("未来").append(forecastDays).append("天天气预报:\n");
        
        for (int i = 1; i <= forecastDays; i++) {
            sb.append(String.format("第%d天:晴转多云,温度%d-%d°C\n", 
                    i, 22 + i, 28 + i));
        }
        return sb.toString();
    }
}

工具调用服务配置:

package com.example.springai2.config;

import org.springframework.ai.tool.ToolCallingAdvisor;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.resolver.ToolCallbackResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;

/**
 * AI工具调用配置
 */
@Configuration
public class ToolCallingConfig {

    /**
     * 配置ToolCallingAdvisor - 自动注册所有@Tool注解的Bean
     * 渐进式工具披露:根据对话上下文智能选择工具
     */
    @Bean
    public ToolCallingAdvisor toolCallingAdvisor(ToolCallbackResolver resolver,
                                                  List<ToolCallback> toolCallbacks) {
        return ToolCallingAdvisor.builder()
                .toolCallbacks(toolCallbacks)
                .progressiveDisclosure(true)  // 启用渐进式工具披露
                .maxIterations(5)             // 最大工具调用迭代次数
                .build();
    }
}

6.4 模块化RAG配置示例

package com.example.springai2.rag;

import org.springframework.ai.rag.RetrievalAugmentation;
import org.springframework.ai.rag.preretrieval.QueryTransformer;
import org.springframework.ai.rag.retrieval.Retrieval;
import org.springframework.ai.rag.postretrieval.DocumentProcessor;
import org.springframework.ai.rag.augmentation.Augmentation;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;

/**
 * 模块化RAG配置 - Pre-Retrieval → Retrieval → Post-Retrieval → Augmentation
 */
@Configuration
public class ModularRagConfig {

    /**
     * 检索前处理:查询转换
     */
    @Bean
    public QueryTransformer queryRewritingTransformer() {
        return query -> {
            // 查询重写:将用户问题改写为更适合检索的形式
            String rewrittenQuery = "用户原始问题:" + query + 
                    "\n请提取关键信息点用于检索。";
            return rewrittenQuery;
        };
    }

    /**
     * 检索层:混合检索配置
     */
    @Bean
    public Retrieval hybridRetrieval(VectorStore vectorStore) {
        return Retrieval.builder()
                .vectorStore(vectorStore)
                .topK(5)                    // 返回最相关的5个文档
                .similarityThreshold(0.7)   // 相似度阈值
                .build();
    }

    /**
     * 检索后处理:文档重排序与过滤
     */
    @Bean
    public DocumentProcessor documentRerankProcessor() {
        return documents -> {
            // 后处理逻辑:去重、重排序、截取片段
            return documents.stream()
                    .distinct()
                    .limit(3)  // 只保留最相关的3个文档
                    .toList();
        };
    }

    /**
     * 增强层:Prompt组装
     */
    @Bean
    public Augmentation promptAugmentation() {
        return Augmentation.builder()
                .systemPromptText("""
                        你是一个专业的知识助手。请根据以下检索到的参考资料回答用户问题。
                        如果参考资料中没有相关信息,请明确告知用户。
                        
                        参考资料:
                        {documents}
                        """)
                .includeCitations(true)  // 包含引用来源
                .build();
    }

    /**
     * 完整的模块化RAG管道
     */
    @Bean
    public RetrievalAugmentation retrievalAugmentation(
            List<QueryTransformer> queryTransformers,
            Retrieval retrieval,
            List<DocumentProcessor> documentProcessors,
            Augmentation augmentation) {
        
        return RetrievalAugmentation.builder()
                .queryTransformers(queryTransformers)  // 检索前处理
                .retrieval(retrieval)                  // 检索层
                .documentProcessors(documentProcessors) // 检索后处理
                .augmentation(augmentation)            // 增强层
                .build();
    }
}
package com.example.springai2.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.rag.RetrievalAugmentation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * RAG问答接口
 */
@RestController
public class RagController {

    @Resource
    private RetrievalAugmentation retrievalAugmentation;

    @GetMapping("/rag/query")
    public String ragQuery(@RequestParam String question) {
        return retrievalAugmentation.call(question)
                .getAiResponse()
                .getResult()
                .getOutput()
                .getContent();
    }
}

7. 性能优化与最佳实践

7.1 HTTP客户端优化

Spring AI 2.0支持完全可配置的HTTP客户端,生产环境建议:

spring:
  ai:
    openai:
      http:
        client:
          connect-timeout: 10s
          read-timeout: 30s
          max-connections: 100
          max-connections-per-route: 20

7.2 流式聚合优化

对于流式响应,建议使用聚合处理减少网络往返:

// 流式响应聚合处理
List<String> chunks = new ArrayList<>();
chatClient.prompt()
    .user("请详细解释微服务架构")
    .stream()
    .content()
    .subscribe(chunks::add);

7.3 虚拟线程支持

Spring AI 2.0完美支持Spring Boot 4.0的虚拟线程特性:

spring:
  threads:
    virtual:
      enabled: true  # 启用虚拟线程,提升并发处理能力

7.4 缓存优化

对于重复查询,建议配置缓存减少API调用成本:

@Bean
public CacheManager aiResponseCache() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();
    cacheManager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(Duration.ofMinutes(30)));
    return cacheManager;
}

8. 总结与展望

Spring AI 2.0的发布,标志着Java生态在AI应用开发领域进入了新的阶段。从1.x到2.0的演进,不仅仅是功能的堆砌,更是架构设计理念的全面升级:

  • 统一的抽象层:ToolCallingAdvisor、EntityParamSpec等核心抽象,屏蔽了底层模型差异
  • 模块化的架构:RAG从黑盒Pipeline到可编排的四阶段架构,灵活性大幅提升
  • 生产级能力:可观测性、性能优化、持久化等能力全面补强
  • 渐进式设计:工具披露、功能升级都遵循渐进式原则,降低使用门槛

展望未来,Spring AI将继续沿着Spring化、生产级、生态化的方向演进。随着MCP协议的普及、多模态能力的增强、边缘AI的兴起,Spring AI有望成为企业级AI应用开发的事实标准。

对于Java开发者而言,现在正是拥抱Spring AI 2.0的最佳时机。它不仅能让你快速构建AI应用,更能让你以熟悉的Spring编程范式,优雅地应对AI时代的技术挑战。


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐