Spring AI 1.0.0 M6 发布

发布 | Mark Pollack | 2025 年 2 月 14 日 | ...

我们很高兴宣布发布 Spring AI 1.0.0 里程碑 6。为了庆祝此发布,我们创建了一个特别的AI 生成音乐播放列表,以增强您的博客阅读和编码体验!

与往常一样,此版本包含多项新功能和错误修复。我们继续从设计的角度审阅了代码库。虽然我们试图通过在一个发布周期内弃用方法和类来使过渡顺利,但我们知道存在一些破坏性变更,也可能存在一些我们不知道的变更,所以请大家谅解。有关详细信息,请参阅本文底部的破坏性变更部分。

新功能

🎵 工具时间到!

函数调用的整体设计和功能集有了显著改进,现在采用了更普遍的术语“工具调用”。非常感谢 Thomas Vitale 推动了这些改进。

现在有几种定义工具的方式

  • 使用 @Tool@ToolParam 注解的声明式方法工具
  • 使用 MethodToolCallback 的编程式方法工具
  • 使用 FunctionToolCallback 的基于函数的工具
  • 从 Spring beans 动态解析工具

以下是使用 @Tool 注解创建获取当前日期和时间的工具的示例。

import java.time.LocalDateTime;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;

class DateTimeTools {

    @Tool(description = "Get the current date and time in the user's timezone")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }
}

// Using the tool with ChatClient
String response = ChatClient.create(chatModel)
        .prompt("What day is tomorrow?")
        .tools(new DateTimeTools())
        .call()
        .content();

当模型需要知道当前日期和时间时,它会自动请求调用该工具。ChatClient 在内部处理工具执行并将结果返回给模型,然后模型使用此信息生成最终响应。JSON Schema 是使用类和工具注解自动生成的。

关于弃用,org.springframework.ai.model.function 包中的所有内容都已被弃用,新的接口和类可在 org.springframework.ai.tool 包中找到。

相关 API 已更新

  • FunctionCallingOptions 更名为 ToolCallingChatOptions
  • ChatClient.builder().defaultFunctions() 更名为 ChatClient.builder().defaultTools()
  • ChatClient.functions() 更名为 ChatClient.tools()

请参阅工具迁移指南了解更多信息。

工具调用功能还有其他多项改进,请查阅工具参考文档了解更多信息。

🎵 MCP,就这么简单 (1, 2, 3)

与其说是“十月惊喜”,不如说是去年的“十一月惊喜”:Model Context Protocol 发布了,并受到了 AI 社区的广泛好评。

简而言之,Model Context Protocol (MCP) 提供了一种统一的方式,将 AI 模型连接到不同的数据源和工具,使集成变得无缝且一致。它帮助您在大语言模型 (LLMs) 的基础上构建代理和复杂的工作流。由于 LLMs 经常需要与数据和工具集成,MCP 提供了

  • 不断增长的预构建集成列表,您的 LLM 可以直接接入
  • 在不同 LLM 提供商和供应商之间切换的灵活性
  • 工具发现和执行的标准化接口

spring-ai-mcp 实验项目于去年 11 月启动,并一直在发展。Spring AI 团队已与 David Soria Parra 以及 Anthropic 的其他成员合作,将该实验项目纳入官方的 MCP Java SDK。MCP Java SDK 具有以下核心功能

  • 同步和异步 MCP 客户端/服务器实现
  • 协议版本兼容性协商
  • 带有变更通知的工具发现和执行
  • 使用 URI 模板进行资源管理
  • 根列表管理和通知
  • Prompt 处理和管理
  • AI 模型交互的采样支持

还有多种传输选项

  • 基于 Stdio 的传输用于基于进程的通信
  • 基于 Java HttpClient 的 SSE 客户端传输
  • 基于 Servlet 的 SSE 服务器传输
  • Spring 特定的传输,用于响应式 HTTP 流的 WebFlux SSE 传输和用于基于 Servlet 的 HTTP 流的 WebMVC SSE 传输

请查阅 MCP Java SDK 文档,了解有关 SDK 入门的更多信息,并访问 MCP Java SDK GitHub 仓库以提交问题并参与讨论。

核心组件已移至 Anthropic Java SDK,与 Spring AI 的集成为开发人员提供了更轻松的体验,可以利用 Spring Boot 自动配置创建客户端和服务器实现。

这里有一个小的客户端示例,展示了如何在简单的聊天机器人应用程序中使用 Brave Search API

@SpringBootApplication
public class Application {
    @Bean
    public CommandLineRunner predefinedQuestions(
            ChatClient.Builder chatClientBuilder, 
            ToolCallbackProvider tools,
            ConfigurableApplicationContext context) {
        return args -> {
            var chatClient = chatClientBuilder
                    .defaultTools(tools)
                    .build();

            String question = "Does Spring AI support the Model Context Protocol?";
            System.out.println("ASSISTANT: " + 
                chatClient.prompt(question).call().content());
        };
    }
}

配置很简单

spring.ai.mcp.client.stdio.enabled=true
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json

服务器配置是

{
  "mcpServers": {
    "brave-search": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-brave-search"
      ],
      "env": {
      }
    }
  }
}

在您的依赖配置中,导入 Spring AI Bom 并添加 Spring Boot 客户端启动器,此处显示的是 Maven 配置。

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>

MCP 是一个很大的主题。参考文档包含更多信息,并且有多个示例

向量存储增强

VectorStore API 已得到改进,尽管伴随了一些破坏性变更。
VectorStore 接口的 delete 方法已简化为 void 操作,移除了之前的 Optional返回类型。开发者现在应该使用异常处理来管理删除失败,而不是检查返回值。请参阅参考文档了解更多信息。

基于过滤器的删除

现在您可以根据元数据条件而不是仅仅根据文档 ID 删除文档。

以下是一个示例

// Create and add documents to the store
Document bgDocument = new Document("content", 
    Map.of("country", "BG", "year", 2020));
Document nlDocument = new Document("content", 
    Map.of("country", "NL", "year", 2021));
vectorStore.add(List.of(bgDocument, nlDocument));

// Delete documents using string filter expression
vectorStore.delete("country == 'BG'");

// Or use the Expression-based API
Filter.Expression expr = // ... your filter expression
vectorStore.delete(expr);

此功能已在所有向量存储提供商中实现,包括 Chroma、Elasticsearch、PostgreSQL、Weaviate、Redis、Milvus 等。

原生客户端访问

新的 getNativeClient() API 允许开发人员在需要时访问底层的原生客户端实现

WeaviateVectorStore vectorStore = // get vector store
Optional<WeaviateClient> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
    WeaviateClient client = nativeClient.get();
    // Use native client capabilities
}

简单向量存储改进

SimpleVectorStore 实现现在支持使用 Spring Expression Language (SpEL) 进行元数据过滤

Postgres 向量存储改进

PostgreSQL 向量存储实现已得到改进,可以更灵活地处理不同的 ID 列类型。它现在支持以下类型,而不是强制 UUID 作为唯一的 Primary Key 类型:

  • UUID
  • TEXT
  • INTEGER
  • SERIAL
  • BIGSERIAL

这使得与现有数据库 schema 和不同的 ID 管理策略的集成更加容易。

已移除已弃用的 Amazon Bedrock Chat 模型

这些模型现已替换为 Amazon Bedrock Converse API,后者更灵活,并支持使用相同的 API 调用不同的模型。

🎵 一切都关于这些代理...

每个人都在谈论代理。我们近期不会构建代理框架,因为 Anthropic 的博客文章 “构建高效代理” 引起了团队的强烈共鸣。

摘自博客文章

在过去的一年里,我们与数十个在各行业构建大语言模型 (LLM) 代理的团队合作。最成功的实现并非使用了复杂的框架或专用库。相反,它们是使用简单、可组合的模式构建的。

代理系统的两个主要类别是工作流和代理

  • 工作流:通过预定义的代码路径协调 LLMs 和工具的系统
  • 代理:LLMs 动态指导其自身流程和工具使用,并保持对其如何完成任务的控制的系统

Spring AI 已经提供了“构建块:增强型 LLM”——这是一个通过检索、工具和内存等增强功能得到加强的 LLM。利用这个构建块,博客文章描述了几种构建高效代理的工作流

  1. 链式工作流 – 将任务分解为顺序步骤,其中每个 LLM 调用处理上一步的输出,确保结构化的进展。
  2. 评估器-优化器 – 使用评估器 LLM 评估另一个 LLM 的输出,通过提供反馈和优化响应来提高质量。
  3. 编排器-工作者 – 一个中央编排器 LLM 将任务委托给专门的工作者 LLMs,从而实现模块化和可扩展的任务执行。
  4. 并行化工作流 – 将任务分解为可以并行运行的独立子任务,提高效率并聚合多个视角。
  5. 路由工作流 – 对输入进行分类,并将其定向到合适的 LLM 模型或处理路径,以优化准确性和效率。

有关这些模式的详细实现,请参阅 Spring AI 的 使用 Spring AI 构建高效代理(第一部分) 和配套的示例

以下是第一个模式的示例

Spring AI 中的链式工作流

在本示例中,我们将创建一个链式工作流,通过以下步骤处理商业报告:

  1. 提取数值指标
  2. 标准化其格式
  3. 对其进行排序
  4. 以整洁的表格格式呈现它们

以下是使用 Spring AI 实现此模式的方法

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    // Sample business report with various metrics in natural language
    String report = """
            Q3 Performance Summary:
            Our customer satisfaction score rose to 92 points this quarter.
            Revenue grew by 45% compared to last year.
            Market share is now at 23% in our primary market.
            Customer churn decreased to 5% from 8%.
            New user acquisition cost is $43 per user.
            Product adoption rate increased to 78%.
            Employee satisfaction is at 87 points.
            Operating margin improved to 34%.
            """;

    @Bean
    public CommandLineRunner commandLineRunner(ChatClient.Builder chatClientBuilder) {
        return args -> {
            new ChainWorkflow(chatClientBuilder.build()).chain(report);
        };
    }
}

/**
 * Implements a prompt chaining workflow that breaks down complex tasks into a sequence
 * of simpler LLM calls, where each step's output feeds into the next step.
 */
public class ChainWorkflow {
    /**
     * System prompts that define each transformation step in the chain.
     * Each prompt acts as a gate that validates and transforms the output
     * before proceeding to the next step.
     */
    private static final String[] CHAIN_PROMPTS = {
        // Step 1: Extract numerical values
        """
        Extract only the numerical values and their associated metrics from the text.
        Format each as 'value: metric' on a new line.
        Example format:
        92: customer satisfaction
        45%: revenue growth""",
        
        // Step 2: Standardize to percentages
        """
        Convert all numerical values to percentages where possible.
        If not a percentage or points, convert to decimal (e.g., 92 points -> 92%).
        Keep one number per line.
        Example format:
        92%: customer satisfaction
        45%: revenue growth""",
        
        // Step 3: Sort in descending order
        """
        Sort all lines in descending order by numerical value.
        Keep the format 'value: metric' on each line.
        Example:
        92%: customer satisfaction
        87%: employee satisfaction""",
        
        // Step 4: Format as markdown
        """
        Format the sorted data as a markdown table with columns:
        | Metric | Value |
        |:--|--:|
        | Customer Satisfaction | 92% |"""
    };

    private final ChatClient chatClient;

    public ChainWorkflow(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    public String chain(String userInput) {
        String response = userInput;
        
        for (String prompt : CHAIN_PROMPTS) {
            response = chatClient.prompt(
                String.format("{%s}\n{%s}", prompt, response)
            ).call().content();
        }
        
        return response;
    }
}

关键的数据流是每一步的输出都成为下一步的输入。对于我们的示例报告,数据流如下:

  • 步骤 1 提取诸如“92:客户满意度”、“45%:收入增长”等指标
  • 步骤 2 标准化为百分比格式:“92%:客户满意度”
  • 步骤 3 按降序对值进行排序
  • 步骤 4 创建格式化的 Markdown 表格

完整的源代码以及其他代理模式的实现可在 spring-ai-examples 仓库和 使用 Spring AI 构建高效代理(第一部分) 中找到。

贡献者

许多贡献者进行了广泛的重构、错误修复和文档增强。如果您的 PR 尚未被处理,请耐心等待,我们会尽快处理。感谢以下贡献者:

破坏性变更

本次发布包含几项破坏性变更,这是我们持续改进 API 设计的结果。主要变更包括

  • 将函数相关的包和类重命名为工具相关的对应项(例如,org.springframework.ai.model.function 更改为 org.springframework.ai.tool
  • VectorStore API 的更新
  • 模型客户端配置属性的变更

有关破坏性变更的完整列表和详细的升级说明,请参阅参考文档中的升级说明

获取 Spring 资讯

订阅 Spring 资讯,保持联系

订阅

领先一步

VMware 提供培训和认证,助力您加速进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部