[AI应用框架/Java] Spring AI 应用开发指南

1 概述:Spring AI 应用开发指南

简介

  • 在人工智能技术快速发展的当下,其相关技术已成为各行业企业内部技术栈的标配之一,而作为一款主流的企业级Java应用开发框架Spring,也紧跟时代潮流,推出了—— Spring AI 框架。

image

  • Spring AI 是 Spring 官方推出的 AI 工程化框架,旨在简化 Java 开发者集成 AI 模型(如 OpenAI、Azure OpenAI 等)的过程。

Spring AI 作为主流的AI大模型应用开发框架之一,它的推出标志着—— Spring 框架也正式进入大模型时代。
其提供了开发AI大模型所需的模型API,简化了AI大模型应用的开发工作。

  • Spring AI
  • URL : https://spring.io/projects/spring-ai
  • Slogan : Spring AI 是一个面向人工智能工程的应用框架。它的目标是将 Spring 生态系统的设计原则(例如可移植性和模块化设计)应用于人工智能领域,并推广使用 POJO 作为人工智能领域应用程序的构建模块。
  • License : Apache-2.0

image

主要特性

Spring AI 提供以下功能:

此功能集允许您实现常见的用例,例如“ Q&A over your documentation”或“ Chat with your documentation.”。

版本沿革

image

  • 最新版:
  • v2.0.0-M3 : 2026.3.17

https://github.com/spring-projects/spring-ai/releases/tag/v2.0.0-M3

  • v1.1.3 : 2026.3.17

https://github.com/spring-projects/spring-ai/releases/tag/v1.1.3

根据搜索结果,Spring AI 项目的版本演变历程如下。

请注意,所有信息均基于公开的网络资料整理:

1. 早期探索与孵化阶段 (Legacy)

  • 0.8.1 (2024年3月):这是早期公开记录的一个旧版本,属于项目孵化期的探索性版本。

2. 里程碑版本阶段 (Milestone Releases)

从2024年中旬开始,Spring AI 进入了快速迭代的里程碑版本发布期,旨在逐步完善核心功能并收集社区反馈。

  • 1.0.0-M1 (2024年5月30日):首个公开的里程碑版本,初步确立了项目架构。
  • 1.0.0-M2 (2024年7月):继续完善基础功能。
  • 1.0.0-M3 (2024年10月8日):增加了对更多模型的支持。
  • 1.0.0-M4 (2024年12月):功能进一步丰富。
  • 1.0.0-M5 (2025年1月9日):优化了 API 设计。
  • 1.0.0-M6 (2025年3月):此版本起,构件开始发布到 Maven Central 仓库,方便了开发者使用。
  • 1.0.0-M7 (2025年4月14日):接近正式版的功能冻结。
  • 1.0.0-M8 (2025年4月30日):最后一个里程碑版本,主要进行 Bug 修复和文档完善。

3. 候选版本阶段 (Release Candidate)

  • 1.0.0-RC1 (2025年5月13日):发布候选版本,标志着功能已基本稳定,邀请社区进行最后的测试。

4. 正式通用版本阶段 (General Availability - GA)

  • 1.0.0 GA (2025年5月20日):首个正式版本

    • 意义:标志着 Spring AI 正式进入【生产就绪状态】,API 趋于稳定。
    • 核心特性:提供了统一的 ChatClient 接口,支持 OpenAI、Anthropic、Azure、Google 等【主流模型厂商】;引入了 RAG(检索增强生成)基础支持、【对话记忆管理】以及基础的 Function Calling 能力。
    • 环境要求:主要支持 Spring Boot 3.4.x 及 JDK 17+。
  • 1.0.x 维护版本 (2025年下半年):

    • 在 1.0.0 之后,陆续发布了 1.0.1, 1.0.2, 1.0.3 等补丁版本,主要用于修复 Bug 和小幅改进(截至2025年10月,1.0.3 是当时的最新稳定分支)。
  • 1.1.0 GA (2025年11月12日):第二个重大正式版本

    • 背景:距离 1.0 发布仅半年,包含了超过 850 项变更(354个功能增强、241个Bug修复等)。
    • 核心升级
      • Agent 框架:引入了更高级的 AI Agent(智能体)构建能力,支持多 Agent 协作。
      • MCP (Model Context Protocol):深度集成了 MCP 协议,支持更标准的上下文交互。
      • 评估工具:新增了 AI 模型输出的评估(Evaluation)工具。
      • 新模型支持:增加了对更多新兴模型(如 DeepSeek 等)的自动配置支持。
      • 技术栈:更好地兼容 Spring Framework 6/7 和 Java 21+ 的新特性(如 AOT 编译优化)。

5. 当前状态 (截至 2026年3月)

  • 主线版本1.1.x 系列是目前推荐的生产环境使用版本。
  • 开发版本1.2.0-SNAPSHOT 或更高版本可能正在开发中,用于探索更新的 AI 特性。
  • 生态扩展:基于 Spring AI 的扩展项目(如 Spring AI Alibaba)也紧随其后发布了相应的 1.0 和 1.1 版本,专门针对阿里云百炼及通义千问模型进行了优化。

总结:Spring AI 从 2024 年初的孵化项目,经过约一年的快速迭代(8个里程碑版本),于 2025 年 5 月达到 1.0 成熟度,并在同年 11 月通过 1.1 版本大幅增强了 Agent 和协议支持能力,目前已发展成为 Java 生态中构建企业级 AI 应用的主流框架。

Spring AI: v1.1.3 vs. v2.0.0-M3

基于现有的公开资料和网络搜索结果,关于 Spring AI v1.1.3v2.0.0-M3 的区别,
由于 v2.0.0-M3 的具体发布说明(Release Notes)在当前的公开网络资源中尚未有详尽的独立文档(通常里程碑版本的详细变更集中在 M1 和 GA 前的 RC 阶段,且 M3 是一个非常新的版本或特定内部版本),
为此将基于 v1.1.x 系列的最终状态v2.0.0-M1(及后续 2.0 系列已知的重大架构变更) 的核心差异进行对比。

重要提示

  • v1.1.3 是 1.1.x 系列的一个稳定补丁版本(Patch Release),主要包含 Bug 修复和小幅优化。
  • v2.0.0-M3 是 2.0 系列的第三个里程碑版本(Milestone),属于非生产就绪的开发版本,其核心特征是破坏性更新(Breaking Changes)和底层架构的重构。

以下是两者的核心区别:

1. 基础运行环境要求(最显著的硬性区别)

这是两个版本之间最大的“分水岭”,直接决定了项目能否启动。

特性 Spring AI v1.1.3 (1.x 系列) Spring AI v2.0.0-M3 (2.x 系列)
最低 JDK 版本 JDK 17 (LTS) JDK 21 (LTS) (强制要求,利用 Java 21 新特性)
Jakarta EE 规范 Jakarta EE 9/10 Jakarta EE 11
Spring Framework Spring Framework 6.2+ Spring Framework 7.0+
Spring Boot 版本 Spring Boot 3.4.x (兼容 3.3+) Spring Boot 4.0.x (GA) (基于 Spring Framework 7)

影响:如果你当前的项目运行在 JDK 17 或 Spring Boot 3.x 上,无法直接升级到 v2.0.0-M3,必须同步升级整个技术栈。

2. 核心架构与 API 变更

v2.0 系列不仅仅是功能增加,更是一次架构重构,旨在解决 1.x 中的设计债务并适应 AI 领域的快速变化。

  • ChatClient API 的重构

    • v1.1.3: 使用较为初版的 ChatClient 接口,虽然支持流式和非流式,但在复杂上下文管理和拦截器链(Advisors)的配置上相对繁琐。
    • v2.0.0-M3: 对 ChatClient 进行了彻底的重新设计(Reactive-first 或更流畅的构建者模式),简化了多轮对话和复杂 Agent 工作流的代码结构。API 包名或方法签名可能发生变动(Breaking Change)。
  • Model Context Protocol (MCP) 的深度集成

    • v1.1.3: 引入了 MCP 的初步支持,允许暴露工具和资源,但配置较为手动。
    • v2.0.0-M3: MCP 成为核心一等公民。原生支持 MCP Server/Client 的自动配置,支持更复杂的工具发现机制动态资源加载,甚至可能内置了对 MCP 标准更新的即时支持(如 SSE 传输层的优化)。
  • 向量存储 (Vector Store) 的增强

    • v1.1.3: 支持主流向量库(Redis, PGVector, Milvus 等),但部分高级过滤功能(Metadata Filtering)在不同实现间表现不一致。
    • v2.0.0-M3: 统一了向量存储的过滤表达式语言(Filter Expression Language),特别是针对 Redis Vector Store 进行了史诗级增强(支持混合搜索、范围查询优化),并可能引入了对新型向量数据库的原生支持。

3. 功能特性的演进

功能领域 v1.1.3 (稳定版特性) v2.0.0-M3 (前沿实验特性)
Agent 框架 提供基础的 React Agent 和工作流支持,需较多样板代码。 原生 Agent 编排。引入更高级的 AgentBuilder,支持多 Agent 协作、更智能的状态管理和内置的“反思”机制。
模型支持 支持 OpenAI, Azure, Anthropic, Google, Ollama 等主流厂商。 新增/更新模型适配。默认集成更新的模型 SDK(如 OpenAI Java SDK 最新版),可能原生支持 Claude 3.5/4, GPT-4o-mini 等新模型的特定功能(如缓存、JSON Mode 优化)。
观察性与评估 基础的 Micrometer 指标和简单的评估接口。 深度可观测性。与 Spring Observability 深度整合,提供针对 LLM Token 消耗、延迟、幻觉率的细粒度追踪;评估框架(Evaluation Harness)更加完善。
RAG 管道 基础的 ETL 和检索流程。 智能 RAG。支持更复杂的预处理管道,自动分块策略优化,以及基于语义的动态检索策略。

4. 稳定性与适用场景

  • v1.1.3:

    • 定位: 生产就绪 (Production Ready)
    • 适用: 企业级正式项目,需要长期支持(LTS)和稳定 API 的场景。
    • 优点: 社区案例多,坑已踩完,文档完善,兼容性好(JDK 17/SB 3.4)。
  • v2.0.0-M3:

    • 定位: 里程碑测试版 (Milestone)
    • 适用: 技术预研、尝鲜、新功能验证、非核心业务系统。
    • 风险: API 随时可能变动,不建议用于生产环境。可能存在未发现的 Bug。
    • 优点: 能体验到最新的架构设计、更好的性能(Java 21 虚拟线程支持可能更深入)和未来标准的支持。

总结建议

  • 如果你的项目正在运行即将上线,请坚守 v1.1.3(或等待 1.1.x 的后续补丁)。不要为了新功能而盲目升级到底层依赖完全不同的 2.0 里程碑版本。
  • 如果你正在启动一个新项目,且团队有能力处理 JDK 21 和 Spring Boot 4 的迁移成本,并希望利用最新的 AI 架构特性(如更强大的 Agent 和 MCP),可以尝试 v2.0.0-M3,但需做好后续跟随版本迭代修改代码的准备(因为 M3 到 GA 期间 API 仍可能调整)。

注意:由于 v2.0.0-M3 是非常新的里程碑版本,具体的 Bug 修复列表和微小的 API 变动,建议直接查阅 Spring AI 官方 GitHub 仓库的 release-notesCHANGELOG 文件以获取最准确的逐行对比。

前置基础知识

  • Java 基础
  • 常用的技术框架: Spring Boot 等
  • 常用的数据库: MySQL 等

2 快速入门

step1 准备工作

  • 在开始编码前,请确保具备以下条件:

或 注册兼容OpenAI的第三方厂商: 硅基流动 / ...

  • Java 环境:JDK 17 或更高版本。例如: OpenJDK 17.0.2
  • 项目构建工具:Maven 或 Gradle。例如: Maven

step2 创建项目

  • Spring Web:用于构建 Web 接口。例如: Spring Web
  • OpenAI:Spring AI 的 OpenAI Starter。

image

image

Maven 核心依赖配置示例:

<dependency>
	<groupId>org.springframework.ai</groupId>
	<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

step3 配置属性(application.yaml)

  • application.yamlapplication.properties 中配置你的 API Key。如果在中国国内使用,通常需要配置代理地址
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      # base-url: https://api.openai-proxy.com # 如需使用代理,请在此配置

image

step4 编写代码实现对话

  • 注入 ChatModel(或旧版本中的 ChatClient)来调用 OpenAI 接口。
@RestController
public class ChatController {
    private final ChatModel chatModel;

    @Autowired
    public ChatController(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @GetMapping("/ai/generate")
    public String generate(@RequestParam(value = "message") String message) {
        return chatModel.call(message);
    }
}

image

step5 启动运行、调用接口

  • com.johnnyzen.spring_ai_examples.SpringAiExamplesApplication

image

  • 调用接口
curl -X GET http://127.0.0.1:8080/spring-ai/what-is-the-meaning-of-life

curl -X GET http://127.0.0.1:8080/spring-ai/what-is-the-meaning-of-life?language=en

image

//message="总结最近一周的AI Agent前沿进展"
curl -X GET http://127.0.0.1:8080/spring-ai/ai/generate?message=%e6%80%bb%e7%bb%93%e6%9c%80%e8%bf%91%e4%b8%80%e5%91%a8%e7%9a%84AI+Agent%e5%89%8d%e6%b2%bf%e8%bf%9b%e5%b1%95

3 核心组件、原理与架构

3.1 Chat API:Chat Model API

概述

  • Chat Model API 为开发人员提供了将 AI 驱动的聊天功能集成到其应用程序中的能力。它利用预训练的语言模型,如 GPT(生成式预训练转换器),以自然语言生成对用户输入的人类般响应。

  • 大模型平台(如:阿里百炼/DashScope)API 通过向 AI 模型发送提示或部分对话来工作,然后 AI 模型基于其训练数据和对自然语言模式的理解生成完成或对话的延续。完成的响应随后返回给应用程序,应用程序可以将其呈现给用户或用于进一步处理。

  • Spring AI Chat Model API 设计为一个简单且可移植的接口,用于与各种 AI 模型交互,允许开发人员以最小的代码更改在不同模型之间切换。这种设计与 Spring 的模块化和可互换性理念保持一致。

  • 同时,借助 Prompt 用于输入封装和 ChatResponse 用于输出处理的配套类,Chat Model API 统一了与 AI 模型的通信。它管理请求准备和响应解析的复杂性,提供直接和简化的 API 交互。

注:您可以在 (百炼) Model 集成 部分找到更多关于模型集成实现的信息。

API 概述

org.springframework.ai.chat.model.ChatModel 接口定义

  • org.springframework.ai:spring-ai-model:1.1.3 源码为例

https://github.com/spring-projects/spring-ai/tree/v1.1.3/spring-ai-model/src/

package org.springframework.ai.chat.model;

import java.util.Arrays;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.Model;
import reactor.core.publisher.Flux;

public interface ChatModel extends Model<Prompt, ChatResponse>, StreamingChatModel {
    default String call(String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        Generation generation = this.call(prompt).getResult();
        return generation != null ? generation.getOutput().getText() : "";
    }

    default String call(Message... messages) {
        Prompt prompt = new Prompt(Arrays.asList(messages));
        Generation generation = this.call(prompt).getResult();
        return generation != null ? generation.getOutput().getText() : "";
    }

    ChatResponse call(Prompt prompt);

    default ChatOptions getDefaultOptions() {
        return ChatOptions.builder().build();
    }

    default Flux<ChatResponse> stream(Prompt prompt) {
        throw new UnsupportedOperationException("streaming is not supported");//不支持流式响应
    }
}

String 参数的 call() 方法简化了初始使用,避免了更复杂的 PromptChatResponse 类的复杂性。在实际应用程序中,更常见的是使用接受 Prompt 实例并返回 ChatResponsecall() 方法。

org.springframework.ai.model.Model
  • Model
/*
 * Copyright 2023-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.ai.model;

/**
 * The Model interface provides a generic API for invoking AI models. It is designed to //模型接口提供了一个用于调用 AI 模型的通用 API。它的设计目的是
 * handle the interaction with various types of AI models by abstracting the process of //通过抽象化以下过程来处理与各种类型人工智能模型的交互:
 * sending requests and receiving responses. The interface uses Java generics to //发送请求和接收响应。该接口使用 Java 泛型。
 * accommodate different types of requests and responses, enhancing flexibility and //适应不同类型的请求和响应,增强灵活性和
 * adaptability across different AI model implementations. //适应不同人工智能模型实现方式的能力。
 *
 * @param <TReq> the generic type of the request to the AI model //向 AI 模型发出的请求的通用类型
 * @param <TRes> the generic type of the response from the AI model //AI 模型响应的通用类型
 * @author Mark Pollack
 * @since 0.8.0
 */
 //
public interface Model<TReq extends ModelRequest<?>, TRes extends ModelResponse<?>> {

	/**
	 * Executes a method call to the AI model. //向 AI 模型执行方法调用。
	 * @param request the request object to be sent to the AI model //要发送给 AI 模型的请求对象
	 * @return the response from the AI model //AI模型的响应
	 */
	TRes call(TReq request);

}

org.springframework.ai.chat.model.StreamingChatModel 接口定义

  • 以``为例分析源码:
package org.springframework.ai.chat.model;

import java.util.Arrays;
import java.util.Optional;
import org.springframework.ai.chat.messages.AbstractMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.StreamingModel;
import reactor.core.publisher.Flux;

@FunctionalInterface
public interface StreamingChatModel extends StreamingModel<Prompt, ChatResponse> {
    default Flux<String> stream(String message) {
        Prompt prompt = new Prompt(message);
        return this.stream(prompt).map((response) -> (String)Optional.ofNullable(response.getResult()).map(Generation::getOutput).map(AbstractMessage::getText).orElse(""));
    }

    default Flux<String> stream(Message... messages) {
        Prompt prompt = new Prompt(Arrays.asList(messages));
        return this.stream(prompt).map((response) -> (String)Optional.ofNullable(response.getResult()).map(Generation::getOutput).map(AbstractMessage::getText).orElse(""));
    }

    Flux<ChatResponse> stream(Prompt prompt);
}

stream()`` 方法接受类似于 ChatModel 的 String 或 Prompt参数,但它使用响应式Flux API` 流式传输响应。

org.springframework.ai.chat.prompt.Prompt 接口定义

  • Prompt

Prompt 是一个 ModelRequest,它封装了一个 Message 对象列表和可选的模型请求选项。以下列表显示了 Prompt 类的截断版本,不包括构造函数和其他实用方法:
Prompt : https://github.com/spring-projects/spring-ai/tree/v1.1.3/spring-ai-model/src/main/java/org/springframework/ai/chat/prompt/Prompt.java

package org.springframework.ai.chat.prompt;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.model.ModelRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

public class Prompt implements ModelRequest<List<Message>> {
    private final List<Message> messages;
    @Nullable
    private ChatOptions chatOptions;

    public Prompt(String contents) {
        this((Message)(new UserMessage(contents)));
    }

    public Prompt(Message message) {
        this(Collections.singletonList(message));
    }

    public Prompt(List<Message> messages) {
        this((List)messages, (ChatOptions)null);
    }

    public Prompt(Message... messages) {
        this((List)Arrays.asList(messages), (ChatOptions)null);
    }

    public Prompt(String contents, @Nullable ChatOptions chatOptions) {
        this((Message)(new UserMessage(contents)), chatOptions);
    }

    public Prompt(Message message, @Nullable ChatOptions chatOptions) {
        this(Collections.singletonList(message), chatOptions);
    }

    public Prompt(List<Message> messages, @Nullable ChatOptions chatOptions) {
        Assert.notNull(messages, "messages cannot be null");
        Assert.noNullElements(messages, "messages cannot contain null elements");
        this.messages = messages;
        this.chatOptions = chatOptions;
    }

    public String getContents() {
        StringBuilder sb = new StringBuilder();

        for(Message message : this.getInstructions()) {
            sb.append(message.getText());
        }

        return sb.toString();
    }

    @Nullable
    public ChatOptions getOptions() {
        return this.chatOptions;
    }

    public List<Message> getInstructions() {
        return this.messages;
    }

    public SystemMessage getSystemMessage() {
        for(int i = 0; i <= this.messages.size() - 1; ++i) {
            Message message = (Message)this.messages.get(i);
            if (message instanceof SystemMessage systemMessage) {
                return systemMessage;
            }
        }

        return new SystemMessage("");
    }

    public UserMessage getUserMessage() {
        for(int i = this.messages.size() - 1; i >= 0; --i) {
            Message message = (Message)this.messages.get(i);
            if (message instanceof UserMessage userMessage) {
                return userMessage;
            }
        }

        return new UserMessage("");
    }

    public Message getLastUserOrToolResponseMessage() {
        for(int i = this.messages.size() - 1; i >= 0; --i) {
            Message message = (Message)this.messages.get(i);
            if (message instanceof UserMessage || message instanceof ToolResponseMessage) {
                return message;
            }
        }

        return new UserMessage("");
    }

    public List<SystemMessage> getSystemMessages() {
        List<SystemMessage> systemMessages = new ArrayList();

        for(Message message : this.messages) {
            if (message instanceof SystemMessage systemMessage) {
                systemMessages.add(systemMessage);
            }
        }

        return systemMessages;
    }

    public List<UserMessage> getUserMessages() {
        List<UserMessage> userMessages = new ArrayList();

        for(Message message : this.messages) {
            if (message instanceof UserMessage userMessage) {
                userMessages.add(userMessage);
            }
        }

        return userMessages;
    }

    public String toString() {
        String var10000 = String.valueOf(this.messages);
        return "Prompt{messages=" + var10000 + ", modelOptions=" + String.valueOf(this.chatOptions) + "}";
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        } else if (!(o instanceof Prompt)) {
            return false;
        } else {
            Prompt prompt = (Prompt)o;
            return Objects.equals(this.messages, prompt.messages) && Objects.equals(this.chatOptions, prompt.chatOptions);
        }
    }

    public int hashCode() {
        return Objects.hash(new Object[]{this.messages, this.chatOptions});
    }

    public Prompt copy() {
        return new Prompt(this.instructionsCopy(), null == this.chatOptions ? null : this.chatOptions.copy());
    }

    private List<Message> instructionsCopy() {
        List<Message> messagesCopy = new ArrayList();
        this.messages.forEach((message) -> {
            if (message instanceof UserMessage userMessage) {
                messagesCopy.add(userMessage.copy());
            } else if (message instanceof SystemMessage systemMessage) {
                messagesCopy.add(systemMessage.copy());
            } else if (message instanceof AssistantMessage assistantMessage) {
                messagesCopy.add(AssistantMessage.builder().content(assistantMessage.getText()).properties(assistantMessage.getMetadata()).toolCalls(assistantMessage.getToolCalls()).build());
            } else {
                if (!(message instanceof ToolResponseMessage)) {
                    throw new IllegalArgumentException("Unsupported message type: " + message.getClass().getName());
                }

                ToolResponseMessage toolResponseMessage = (ToolResponseMessage)message;
                messagesCopy.add(ToolResponseMessage.builder().responses(new ArrayList(toolResponseMessage.getResponses())).metadata(new HashMap(toolResponseMessage.getMetadata())).build());
            }

        });
        return messagesCopy;
    }

    public Prompt augmentSystemMessage(Function<SystemMessage, SystemMessage> systemMessageAugmenter) {
        ArrayList<Message> messagesCopy = new ArrayList(this.messages);
        boolean found = false;

        for(int i = 0; i < messagesCopy.size(); ++i) {
            Message message = (Message)messagesCopy.get(i);
            if (message instanceof SystemMessage systemMessage) {
                messagesCopy.set(i, (Message)systemMessageAugmenter.apply(systemMessage));
                found = true;
                break;
            }
        }

        if (!found) {
            messagesCopy.add(0, (Message)systemMessageAugmenter.apply(new SystemMessage("")));
        }

        return new Prompt(messagesCopy, null == this.chatOptions ? null : this.chatOptions.copy());
    }

    public Prompt augmentSystemMessage(String newSystemText) {
        return this.augmentSystemMessage((Function)((systemMessage) -> systemMessage.mutate().text(newSystemText).build()));
    }

    public Prompt augmentUserMessage(Function<UserMessage, UserMessage> userMessageAugmenter) {
        ArrayList<Message> messagesCopy = new ArrayList(this.messages);

        for(int i = messagesCopy.size() - 1; i >= 0; --i) {
            Message message = (Message)messagesCopy.get(i);
            if (message instanceof UserMessage userMessage) {
                messagesCopy.set(i, (Message)userMessageAugmenter.apply(userMessage));
                break;
            }

            if (i == 0) {
                messagesCopy.add((Message)userMessageAugmenter.apply(new UserMessage("")));
            }
        }

        return new Prompt(messagesCopy, null == this.chatOptions ? null : this.chatOptions.copy());
    }

    public Prompt augmentUserMessage(String newUserText) {
        return this.augmentUserMessage((Function)((userMessage) -> userMessage.mutate().text(newUserText).build()));
    }

    public Builder mutate() {
        Builder builder = (new Builder()).messages(this.instructionsCopy());
        if (this.chatOptions != null) {
            builder.chatOptions(this.chatOptions.copy());
        }

        return builder;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        @Nullable
        private String content;
        @Nullable
        private List<Message> messages;
        @Nullable
        private ChatOptions chatOptions;

        public Builder content(@Nullable String content) {
            this.content = content;
            return this;
        }

        public Builder messages(Message... messages) {
            if (messages != null) {
                this.messages = Arrays.asList(messages);
            }

            return this;
        }

        public Builder messages(List<Message> messages) {
            this.messages = messages;
            return this;
        }

        public Builder chatOptions(ChatOptions chatOptions) {
            this.chatOptions = chatOptions;
            return this;
        }

        public Prompt build() {
            if (StringUtils.hasText(this.content) && !CollectionUtils.isEmpty(this.messages)) {
                throw new IllegalArgumentException("content and messages cannot be set at the same time");
            } else {
                if (StringUtils.hasText(this.content)) {
                    this.messages = List.of(new UserMessage(this.content));
                }

                return new Prompt(this.messages, this.chatOptions);
            }
        }
    }
}

org.springframework.ai.chat.messages.Message 接口定义

  • Message

Message 接口有各种实现,对应于 AI 模型可以处理的消息类别。
Message 接口封装了 Prompt 文本内容、元数据属性集合和称为 MessageType 的分类

package org.springframework.ai.chat.messages;

import org.springframework.ai.content.Content;

public interface Message extends Content {
    MessageType getMessageType();
}
Content
  • Content
package org.springframework.ai.content.Content;

public interface Content {

    String getText();

    Map<String, Object> getMetadata();
}
MediaContent
  • MediaContent

多模态消息类型还实现了 MediaContent 接口,提供 Media 内容对象列表。

public interface MediaContent extends Content {
    Collection<Media> getMedia();
}
MessageType
  • MessageType

org.springframework.ai.chat.messages.MessageType
这是 Spring AI 聊天模块的基础类型,通过字符串构造函数将枚举常量映射为对应的字符串值,便于序列化和传输

package org.springframework.ai.chat.messages;

public enum MessageType {
    USER("user"),//用户发送的消息
    ASSISTANT("assistant"),//AI 助手回复的消息
    SYSTEM("system"),//系统指令消息(用于设定对话上下文)
    TOOL("tool");//工具调用相关的消息

    private final String value;

    private MessageType(String value) {
        this.value = value;
    }

    public static MessageType fromValue(String value) {
        for(MessageType messageType : values()) {
            if (messageType.getValue().equals(value)) {
                return messageType;
            }
        }

        throw new IllegalArgumentException("Invalid MessageType value: " + value);
    }

    public String getValue() {
        return this.value;
    }
}
Spring AI Message API

image

聊天完成端点根据对话角色区分消息类别,有效地由 MessageType 映射。
例如,OpenAI 识别不同对话角色的消息类别,如 systemuserfunctionassistant
虽然术语 MessageType 可能暗示特定的消息格式,但在这种情况下,它实际上指定了消息在对话中扮演的角色。
对于不使用特定角色的 AI 模型,UserMessage 实现作为标准类别,通常表示用户生成的查询或指令。要了解 PromptMessage 的实际应用和关系,特别是在这些角色或消息类别的上下文中,请参阅提示部分中的详细解释。

Chat Options

  • Chat Options

org.springframework.ai.chat.prompt.ChatOptions
表示可以传递给 AI 模型的选项。ChatOptions 类是 ModelOptions 的子类,用于定义可以传递给 AI 模型的几个便携选项。ChatOptions 类定义如下

public interface ChatOptions extends ModelOptions {

    String getModel();
    Float getFrequencyPenalty();
    Integer getMaxTokens();
    Float getPresencePenalty();
    List<String> getStopSequences();
    Float getTemperature();
    Integer getTopK();
    Float getTopP();
    ChatOptions copy();
}
  • 此外,每个特定于模型的 ChatModel/StreamingChatModel 实现都可以有自己的选项,可以传递给 AI 模型。例如,OpenAI Chat Completion 模型有自己的选项,如 logitBiasseeduser

这是一个强大的功能,允许开发人员在启动应用程序时使用特定于模型的选项,然后使用 Prompt 请求在运行时覆盖它们。

  • Spring AI 提供了一个复杂的系统来配置和使用聊天模型。它允许在启动时设置默认配置,同时还提供了在每次请求的基础上覆盖这些设置的灵活性。这种方法使开发人员能够轻松地使用不同的 AI 模型并根据需要调整参数,所有这些都在 Spring AI 框架提供的一致接口内。

以下是 Spring AI 处理聊天模型配置和执行的流程:

image

  1. 启动配置:ChatModel/StreamingChatModel 使用"启动"聊天选项初始化。这些选项在 ChatModel 初始化期间设置,旨在提供默认配置。
  2. 运行时配置:对于每个请求,Prompt 可以包含运行时聊天选项:这些可以覆盖启动选项。
  3. 选项合并过程:"合并选项"步骤结合了启动和运行时选项。如果提供了运行时选项,它们优先于启动选项。
  4. 输入处理:"转换输入"步骤将输入指令转换为本机、特定于模型的格式。
  5. 输出处理:"转换输出"步骤将模型的响应转换为标准化的 ChatResponse 格式。

启动和运行时选项的分离允许全局配置和请求特定的调整。

ChatResponse

  • ChatResponse

org.springframework.ai.chat.model.ChatResponse

public class ChatResponse implements ModelResponse<Generation> {

    private final ChatResponseMetadata chatResponseMetadata;
    private final List<Generation> generations;

    @Override
    public ChatResponseMetadata getMetadata() {...}

    @Override
    public List<Generation> getResults() {...}

    // other methods omitted
}

ChatResponse 类保存 AI 模型的输出,每个 Generation 实例包含来自单个提示的潜在多个输出之一。ChatResponse 类还携带有关 AI 模型响应的 ChatResponseMetadata 元数据。

Generation

  • Generation

org.springframework.ai.chat.model.Generation
Generation 类从 ModelResult 扩展,表示模型输出(助手消息)和相关元数据:

public class Generation implements ModelResult<AssistantMessage> {

    private final AssistantMessage assistantMessage;
    private ChatGenerationMetadata chatGenerationMetadata;

    @Override
    public AssistantMessage getOutput() {...}

    @Override
    public ChatGenerationMetadata getMetadata() {...}

    // other methods omitted
}

可用的聊天模型

此图说明了统一的接口 ChatModel 和 StreamingChatModel 用于与来自不同提供商的各种 AI 聊天模型交互,允许轻松集成和在不同 AI 服务之间切换,同时为客户端应用程序维护一致的 API。

image

  • 可用的聊天模型实现包括:
  • OpenAI 聊天完成(支持流式传输、多模态和函数调用)
  • Microsoft Azure Open AI 聊天完成(支持流式传输和函数调用)
  • Ollama 聊天完成(支持流式传输、多模态和函数调用)
  • Hugging Face 聊天完成(不支持流式传输)
  • Google Vertex AI Gemini 聊天完成(支持流式传输、多模态和函数调用)
  • Amazon Bedrock
  • Mistral AI 聊天完成(支持流式传输和函数调用)
  • Anthropic 聊天完成(支持流式传输和函数调用)

提示:在聊天模型比较部分找到可用聊天模型的详细比较。

Chat Model API

  • Spring AI Chat Model API 构建在 Spring AI Generic Model API 之上,提供聊天特定的抽象和实现。这允许轻松集成和在不同 AI 服务之间切换,同时为客户端应用程序维护一致的 API。

image

3.2 Chat API:Chat Client API

关于 ChatClient

  • ChatClient 提供了一个流畅的 API 用于与 AI 模型进行通信。它同时支持同步和流式编程模型。
  • ChatClient API 提供了构建 Prompt 各个组成部分的方法,这些 Prompt 将作为输入传递给 AI 模型。

从 API 的角度来看,Prompt 包含了一系列消息。
AI 模型处理两种主要类型的消息:用户消息(来自用户的直接输入)和系统消息(由系统生成以指导对话)。
这些消息通常包含占位符,这些占位符会在运行时根据用户输入进行替换,以定制 AI 模型对用户输入的响应。
还可以指定 Prompt 选项,例如要使用的 AI 模型名称和 topK 或创造性的 temperature 设置。

基于 ChatModel 创建 ChatClient

  • ChatClient 是使用 ChatClient.Builder 对象创建的。ChatBuilder 中需要传入一个 ChatModel,您可以集成相关的 Spring Boot Starter 或者使用编程方式自动创建一个 ChatModel 传入。

image

ChatClient 是基于构造函数中传入的 ChatModel 创建的。

使用自动配置的 ChatClient.Builder

  • 在最简单的用例中,Spring AI 提供了 Spring Boot Starter,为您创建一个原型ChatClient.Builder bean,您可以将其注入到您的类中。以下是一个简单的示例,展示如何获取对简单用户请求的 String 响应:
@RestController
class MyController {
    private final ChatClient chatClient;

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

    @GetMapping("/ai")
    String generation (String userInput) {
        return this.chatClient.prompt()
            .user(userInput)
            .call()
            .content();
    }
}

在这个简单的示例中,用户输入设置了用户消息的内容。`call()`` 方法向 AI 模型发送请求,content() 方法返回 AI 模型的响应作为 String。

Y 推荐文献

  • Spring AI
  • 学习资源

X 参考文献

posted @ 2026-03-20 15:08  数据知音  阅读(89)  评论(0)    收藏  举报