在这里插入图片描述

🪁🍁 希望本文能给您带来帮助,如果有任何问题,欢迎批评指正!🐅🐾🍁🐥



导航参见:
Spring AI实战:SpringBoot项目结合Spring AI开发——ChatClient API详解
Spring AI实战:SpringBoot项目结合Spring AI开发——提示词(Prompt)技术与工程实战详解
Spring AI实战:SpringBoot项目结合Spring AI开发——模型参数及ChatOptions API详解

一、背景

伴随着AI技术的蓬勃发展,Java开发者亟需将AI能力无缝集成到企业级应用中。市面上已经陆续出现了很多大模型开发框架,如Spring AI、LangChain4j等,其中,Spring AI学习成本较低,与SpringBoot适配度非常高,其也提高了很多标准化的API来支持LLM、图像识别和自然语言处理,极大地了降低了AI集成的门槛。因此,本文及后续文章将结合实战案例,详细解析Spring AI的核心功能与企业级应用技巧。

二、前置准备(附避坑指南)

2.1 基础运行环境

Spring AI 是基于 Spring Boot3.x 框架构建,Spring Boot官方提供了非常便捷的工具Spring Initializr帮助开发者快速的搭建Spring Boot应用程序,IDEA也集成了此工具。本文使用的开发工具IDEA+Spring Boot 3.4.3+Spring AI 1.0.0-SNAPSHOT+Maven

2.2 Maven依赖

2.2.1 添加 Spring Snapshot Repositories和BOM

注意:在集成 Spring AI 或其他较新的 Spring 生态系统组件时,添加 Spring MilestoneSnapshot存储库是为了确保能够访问到最新的开发版本、里程碑版本和快照版本,这些版本可能尚未发布到 Maven Central 这样的稳定仓库,但包含了最新的特性、修复和改进。目前Spring AI最新快照版本是1.0.0-SNAPSHOT没有发布到maven中央仓库,所以需要手动添加Spring Snapshot Repositories

<repositories>
    <!--这里使用的是快照版本-->
    <repository>
        <id>spring-snapshot</id>
        <name>Spring Snapshot</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
</repositories>

Spring AI BOM 声明了特定版本的 Spring AI依赖项的推荐版本。后续添加Spring AI相关模块无需指定具体的版本。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2.2.2 添加Spring AI依赖

这里可以按需导入大模型的依赖,或者同时导入

<!-- deepseek依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>

<!-- zhipuai依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-zhipuai</artifactId>
 </dependency>

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

<!-- ollama依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>

<!-- Spring AI Alibaba(通义大模型支持) -->
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-autoconfigure</artifactId>
    <version>1.0.0-M6.1</version>
</dependency>

2.2.3 pom.xml完整配置

将上面的分项配置汇总后,完整配置展示如下:

<properties>
    <java.version>17</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>3.4.3</spring-boot.version>
    <spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- deepseek依赖 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-deepseek</artifactId>
    </dependency>

    <!-- zhipuai依赖 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-zhipuai</artifactId>
    </dependency>

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

    <!-- ollama依赖 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-ollama</artifactId>
    </dependency>

    <!-- Spring AI Alibaba(通义大模型支持) -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-autoconfigure</artifactId>
        <version>1.0.0-M6.1</version>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
    	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <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>
<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
</repositories>

目前Spring AI还没有直接引入阿里的通义千问大模型,大家如果想引入的话可以按照上面的方式配置,但是还需要在主启动类里排掉两个自动配置文件,不然会造成bean的重复定义和加载,程序无法正常启动。

在这里插入图片描述

2.3 API key申请

Spring AI目前已经支持了很多大模型,详细信息大家可以去查阅官方文档:Spring AI 官方文档。我们在使用模型的时候,前提是需要申请API key的,当然大多数模型都是收费的,而且是按token计费的。

注册DeepSeek账号

在这里插入图片描述
点击右上角填写注册信息;图示如下:

在这里插入图片描述
注册完毕后进入DeepSeek开发平台 https://platform.deepseek.com/usage ;图示如下:

在这里插入图片描述

创建DeepSeek API key
点击API keys 创建DeepSeek API key;图示如下:

在这里插入图片描述
DeepSeek在线充值
通过 DeepSeek 官网或官方 App 进行聊天、写代码或查资料等操作是完全免费的。 然而,如需通过编程调用 DeepSeek API(例如,将模型接入公众号、开发翻译插件或集成到其他软件应用中),则需要消耗 API Token。在此情况下,用户需通过 DeepSeek 平台的充值中心 https://platform.deepseek.com/top_up 进行充值,方可使用 API 服务;图示如下:

在这里插入图片描述

注意:关于deepseek的token用量计算等其他信息,大家可以直接查阅deepseek官方文档。这里申请好API key之后,后续就可以在实战代码中的配置文件里进行配置,其他模型的API key申请过程也是类似的,这里就不做赘述。

2.4 yml文件配置

如果在2.3章节已经申请好了API key,那么就需要在yml配置文件里配置对应模型的API key,这是必填的配置,程序启动时会进行校验,下文源码分析章节中会有介绍。

spring:
  ai:
    # 禁用默认的 Chat Client
    chat:
      client:
        enabled: false
    deepseek:
      # API 密钥
      api-key: deepseek api-key
      # 可选:DeepSeek API 基础地址,默认是 https://api.deepseek.com
      # base-url: https://api.deepseek.com
      chat:
        options:
          # DeepSeek 使用的聊天模型,可选 deepseek-chat 或 deepseek-reasoner
          # deepseek-chat为聊天模型,deepseek-reasoner为推理模型,推理模型会生成推理过程,比较消耗token
          model: deepseek-chat
          # 模型的温度值,控制生成文本的随机性(0.0 = 最确定,1.0 = 最随机)
          temperature: 0.8

    zhipuai:
      api-key: zhipuai api-key
      base-url: https://open.bigmodel.cn/api/paas
      chat:
        options:
          model: glm-4-air

    openai:
      api-key: openai api-key
      base-url: https://api.openai.com/v1
      chat:
        options:
          model: gpt-3.5-turbo
          temperature: 0.7

    dashscope:
      api-key: dashscope api-key
      chat:
        options:
          model: qwen-max
          temperature: 0.8
          top-p: 0.9

三、ChatClient API

3.1 ChatClient 简介

ChatClient 提供了与 AI 模型通信Fluent API,它支持同步反应式(Reactive)编程模型。与 ChatModelMessageChatMemory等原子 API 相比,使用 ChatClient 可以将与 LLM 及其他组件交互的复杂性隐藏在背后,因为基于 LLM 的应用程序通常要多个组件协同工作(例如,提示词模板、聊天记忆、LLM Model、输出解析器、RAG 组件:嵌入模型和存储),并且通常涉及多个交互,因此协调它们会让编码变得繁琐。当然使用 ChatModel 等原子 API 可以为应用程序带来更多的灵活性,成本就是您需要编写大量样板代码。

ChatClient 类似于应用程序开发中的服务层,它为应用程序直接提供 AI 服务,开发者可以使用 ChatClient Fluent API 快速完成一整套 AI 交互流程的组装。

包括一些基础功能,如:

  • 定制和组装模型的输入(Prompt)
  • 格式化解析模型的输出(Structured Output)
  • 调整模型交互参数(ChatOptions)

还支持更多高级功能:

  • 聊天记忆(Chat Memory)
  • 工具/函数调用(Function Calling)
  • RAG

3.2 创建 ChatClient

使用 ChatClient.Builder 对象创建 ChatClient 实例,您可以自动注入由Spring Boot 自动配置创建的默认 ChatClient.Builder 实例,您也可以通过编程方式自行创建一个 ChatClient.Builder 实例并用它来得到 ChatClient 实例。

3.2.1 使用自动配置的 ChatClient.Builder

在使用自动配置时,整体pom配置参考前文中的第二章所述,pom依赖局部如下,这里可以任意引入一个模型依赖,例如本文这里引入的deepseek依赖,那么自动配置的就是依据DeepSeekChatModel创建出的ChatClient 实例。看到这里,您先别急,下文会有源码分析带您一探究竟。

<!-- ... -->
<dependencies>

	<!-- ... -->
	
    <!-- deepseek依赖 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-deepseek</artifactId>
    </dependency>

	<!-- ... -->
</dependencies>
<!-- ... -->
spring:
  ai:
    deepseek:
      # API 密钥
      api-key: deepseek api-key
      # 可选:DeepSeek API 基础地址,默认是 https://api.deepseek.com
      # base-url: https://api.deepseek.com
      chat:
        options:
          # DeepSeek 使用的聊天模型,可选 deepseek-chat 或 deepseek-reasoner
          # deepseek-chat为聊天模型,deepseek-reasoner为推理模型,推理模型会生成推理过程,比较消耗token
          model: deepseek-chat
          # 模型的温度值,控制生成文本的随机性(0.0 = 最确定,1.0 = 最随机)
          temperature: 0.8

这就是使用的 Spring Boot 自动装配默认生成的 ChatClient.Builder 的 bean,把它注入到您自己的类中。这里是根据用户提问并从模型得到文本回答的简单例子:

@RestController
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/chat")
    public String chat(@RequestParam("input") String input) {
        return this.chatClient.prompt()
                .user(input)
                .call()
                .content();
    }
}

在这个示例中,首先设置了用户消息的内容,call 方法向 AI 模型发送请求,content 方法以字符串形式返回 AI 模型的响应。

注意:基于 call() 方法的调用属于同步调用,需要所有响应结果全部返回后才能返回给前端。

启动项目,在浏览器中访问:http://localhost:8082/chat?input=请解释一下灵笼动漫里的主要角色,测试效果如下图:

在这里插入图片描述

3.2.2 以编程方式创建多模型的 ChatClient

使用自动配置 ChatClient.Builder方式构建ChatClient虽然简单,但是有些场景是需要多个聊天模型一起使用的,或者不同模型处理不同场景下的问题,这个时候自动配置的方式就不适配了。因此,我们需要通过设置属性 spring.ai.chat.client.enabled=false 来禁用 ChatClient.Builder bean 的自动配置,然后以编程方式创建 ChatClient实例,这样可以为每个聊天模型创建一个实例 ChatModel:

<!-- deepseek依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>

<!-- zhipuai依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-zhipuai</artifactId>
</dependency>

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

<!-- ollama依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>

<!-- Spring AI Alibaba(通义大模型支持) -->
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-autoconfigure</artifactId>
    <version>1.0.0-M6.1</version>
</dependency>
spring:
  ai:
    # 禁用默认的 Chat Client
    chat:
      client:
        enabled: false
    deepseek:
      # API 密钥
      api-key: deepseek api-key
      # 可选:DeepSeek API 基础地址,默认是 https://api.deepseek.com
      # base-url: https://api.deepseek.com
      chat:
        options:
          # DeepSeek 使用的聊天模型,可选 deepseek-chat 或 deepseek-reasoner
          # deepseek-chat为聊天模型,deepseek-reasoner为推理模型,推理模型会生成推理过程,比较消耗token
          model: deepseek-chat
          # 模型的温度值,控制生成文本的随机性(0.0 = 最确定,1.0 = 最随机)
          temperature: 0.8

    zhipuai:
      api-key: zhipuai api-key
      base-url: https://open.bigmodel.cn/api/paas
      chat:
        options:
          model: glm-4-air

    openai:
      api-key: openai api-key
      base-url: https://api.openai.com/v1
      chat:
        options:
          model: gpt-3.5-turbo
          temperature: 0.7

    dashscope:
      api-key: dashscope api-key
      chat:
        options:
          model: qwen-max
          temperature: 0.8
          top-p: 0.9

当使用多个 AI 模型时,我们可以定义一个ChatClientConfig 类专门为每个模型定义单独的 ChatClient bean:

/**
 *
 * 多平台AI聊天服务客户端初始化配置类
 *
 * @author wasteland
 * @create 2025-07-12
 */
@Configuration
public class ChatClientConfig {
    /**
     * 创建并配置一个ChatClient实例
     * 该方法通过注入的DeepSeekChatModel对象初始化一个ChatClient
     * 主要作用是将聊天模型与客户端进行绑定,以便进行后续的聊天操作
     *
     * @param chatModel 聊天模型,包含了聊天所需的配置和参数
     * @return 返回配置好的ChatClient实例
     */
    @Bean
    public ChatClient deepSeekChatClient(DeepSeekChatModel chatModel) {
        return ChatClient.create(chatModel);
    }


    /**
     * 创建ChatClient实例的Bean定义
     * 该方法将OllamaChatModel转换为ChatClient实例,供Spring框架管理
     *
     * @param chatModel 聊天模型,包含了与聊天客户端相关的信息和配置
     * @return ChatClient实例,用于与AI聊天服务进行交互
     */
    @Bean
    public ChatClient ollamaChatClient(OllamaChatModel chatModel) {
        return ChatClient.create(chatModel);
    }

    /**
     * 创建ChatClient实例的Bean定义
     * 该方法将OpenAiChatModel转换为ChatClient实例,供Spring框架管理
     *
     * @param chatModel 聊天模型,包含了与聊天客户端相关的信息和配置
     * @return ChatClient实例,用于与AI聊天服务进行交互
     */
    @Bean
    public ChatClient openAIChatClient(OpenAiChatModel chatModel) {
        return ChatClient.create(chatModel);
    }

    /**
     * 创建ChatClient实例的Bean定义
     * 该方法将DashScopeChatModel转换为ChatClient实例,供Spring框架管理
     *
     * @param chatModel 聊天模型,包含了与聊天客户端相关的信息和配置
     * @return ChatClient实例,用于与AI聊天服务进行交互
     */
    @Bean
    public ChatClient qWenChatClient(DashScopeChatModel chatModel) {
        return ChatClient.create(chatModel);
    }

    /**
     * 创建ChatClient实例的Bean定义
     * 该方法将ZhiPuAiChatModel转换为ChatClient实例,供Spring框架管理
     *
     * @param chatModel 聊天模型,包含了与聊天客户端相关的信息和配置
     * @return ChatClient实例,用于与AI聊天服务进行交互
     */
    @Bean
    public ChatClient zhiPuAiChatClient(ZhiPuAiChatModel chatModel) {
        return ChatClient.create(chatModel);
    }

}

然后我们可以将其注入到控制器中:

@RestController
public class ChatController {

    @Resource
    private ChatClient deepSeekChatClient;

    @Resource
    private ChatClient ollamaChatClient;

	...

    @GetMapping("/deepSeekChatClient/chat")
    public String testDeepSeekChatClient(@RequestParam("input") String input) {
        return this.deepSeekChatClient.prompt()
                .user(input)
                .call()
                .content();
    }

    @GetMapping("/ollamaChatClient/chat")
    public String testOllamaChatClient(@RequestParam("input") String input) {
        return this.ollamaChatClient.prompt()
                .user(input)
                .call()
                .content();
    }
	...
}

3.2.3 走进源码

既然前文说使用自动配置的 ChatClient.Builder,那么本节来深入到源码中分析Spring AI是如何实现这一过程的,这一实现过程核心类就是ChatClientAutoConfiguration自动配置类:

在这里插入图片描述

能从源码中分析得到,如果spring.ai.chat.client.enabled=true(这也是源码中默认的),那么Spring AI将会使用自动配置得到ChatClient.Builder的实例数据。此外,从方法入参也能看到ChatClient.Builder构建依赖ChatModel,那么加载过程中,Spring容器会优先加载ChatModel,我们再在栈帧往前寻找,不难发又一个自动配置类DeepSeekChatAutoConfiguration为什么是这个类?因为前文使用使用自动配置的 ChatClient.Builder方式构建时,导入的就是DeepSeek的依赖包):

在这里插入图片描述

通过上面源码分析,也不难能发现为啥在做前置数据准备的时候要申请API key了吧,因为源码里做了数据校验。那么又有小伙伴有疑问了,这个base URL也进行了校验啊,为啥就不需要强制配置它呢?其实源码中已经给它设置了默认值,我们可以在另一个配置类DeepSeekConnectionProperties找到相关痕迹:

在这里插入图片描述

上面是模型的链接配置,模型的聊天配置可以定位到DeepSeekChatProperties配置类:

在这里插入图片描述

可以配置的选项有这么多:

在这里插入图片描述

3.3 处理 ChatClient 响应

ChatClient API 提供了多种方法来格式化来自 AI 模型的响应。

3.3.1 返回 ChatResponse(性能监控)

AI 模型的响应是一种由ChatResponse类型定义的丰富结构。它包含响应生成相关的元数据,同时它还可以包含多个子响应(称为Generation),每个子响应都有自己的元数据。元数据包括用于创建响应的令牌(token)数量信息(在英文中,每个令牌大约为一个单词的 3/4),了解令牌信息很重要,因为 AI 模型根据每个请求使用的令牌数量收费。
下面的代码段显示了通过调用 chatResponse() 返回 ChatResponse 的示例,相比于调用 content() 方法,这里在调用 call() 方法之后调用 chatResponse()

@GetMapping("/testChatResponse/chat")
public ChatResponse testChatResponse(@RequestParam("input") String input) {
    return this.chatClient.prompt()
            .user(input)
            .call()
            .chatResponse();
}

调试输入内容如下:

在这里插入图片描述

执行结果:

在这里插入图片描述

3.3.2 返回实体类(Entity)

您经常希望返回一个预先定义好的实体类型响应,Spring AI 框架可以自动替我们完成从 String 到实体类的转换,调用entity() 方法可完成响应数据转换。

例如,给定 Java record(POJO)定义:

@Data
public class AnimeCharacter {
    private String animeName;
    private String name;          // 角色名称(如“马克”)
    private String englishName;   // 英文名(如“Mark”)
    private String identity;      // 身份(如“猎荒者指挥官”)
    private List<String> traits;  // 特点(列表)
    private List<String> keyPlots;// 关键剧情
    private String fate;          // 角色命运(如结局)
    private String symbol;       // 象征意义(如查尔斯代表“权力与宗教”)
    private List<String> secrets; // 角色掌握的秘密(如摩根城主的旧世界科技)

}

可以使用该 entity 方法轻松地将 AI 模型的输出映射到 AnimeCharacter 类型,如下所示:

@GetMapping("/testEntity/chat")
public AnimeCharacter testEntity(@RequestParam("input") String input) {
    return this.chatClient.prompt()
            .user(input)
            .call()
            .entity(AnimeCharacter.class);
}

调试输入内容如下:

在这里插入图片描述
执行结果:

在这里插入图片描述

entity 还有一种带有参数的重载方法 entity(ParameterizedTypeReference<T> type),可让您指定如泛型 List 等类型:

@GetMapping("/testEntityReference/chat")
public List<AnimeCharacter> testEntityReference(@RequestParam("input") String input) {
    return this.chatClient.prompt()
            .user(input)
            .call()
            .entity(new ParameterizedTypeReference<List<AnimeCharacter>>() {});
}

调试输入内容如下:
在这里插入图片描述

执行结果:

在这里插入图片描述

3.3.3 流式响应(实时交互)

同步调用需要等待很长时间页面才能看到结果,用户体验不好。为了解决这个问题,我们可以将调用方式改进为流式调用。在 SpringAI 中使用了 WebFlux 技术实现流式调用,其中stream 方法是一种异步的、持续的获得模型响应的方式,通过Flux实现逐字符输出,适用于聊天窗口、代码编辑器等实时场景。

// 注意看返回值,是Flux<String>,也就是流式结果,另外需要设定响应类型和编码,不然前端会乱码
@GetMapping(value = "/testStream/chat", produces = "text/html;charset=UTF-8")
public Flux<String> testStream(@RequestParam("input") String input) {
    return this.chatClient.prompt()
            .user(input)
            .stream()
            .content();
}

在浏览器页面上测试效果如下图所示:

在这里插入图片描述

前端集成:客户端通过SSE(Server-Sent Events)接收流式数据,实现“打字机”效果。

相比于上面的 Flux<String>,您还可以使用 Flux<ChatResponse> chatResponse() 方法获得 ChatResponse 响应数据流,实现过程类似,这里不做赘述。进阶技巧:结合StructuredOutputConverter自定义解析逻辑,处理非标准格式。这里只做个简单示例,StructuredOutputConverter具体内容会在后续文章中详细介绍。

@GetMapping("/testStreamConvert/chat")
public List<AnimeCharacter> testStreamConvert() {
    BeanOutputConverter<List<AnimeCharacter>> converter = new BeanOutputConverter<>(new ParameterizedTypeReference<List<AnimeCharacter>>() {
    });

    Flux<String> flux = chatClient.prompt()
            .user(u -> u.text("""
                          请介绍一下灵笼动漫里的主要角色。
                          {format}
                        """)
                    .param("format", converter.getFormat()))
            .stream()
            .content();

    String content = flux.collectList().block().stream().collect(Collectors.joining());
    return converter.convert(content);

}

3.4 call() 返回值

ChatClient.call() 方法支持几种不同类型的响应格式。

  • String content():返回响应的字符串内容

  • ChatResponse chatResponse():返回ChatResponse包含多个代以及有关响应的元数据的对象,例如,使用了多少个令牌来创建响应。

  • ChatClientResponse chatClientResponse():返回一个 ChatClientResponse 对象,其中包含 ChatResponse 对象和 ChatClient 执行上下文context,让可以访问在执行 advisors 期间使用的其他数据(例如,在 RAG 流程中检索的相关文档)。

  • entity 返回 Java 类型

    • entity(ParameterizedTypeReference type):用于返回实体类型的集合。
    • entity(Class type): 用于返回特定的实体类型。
    • entity(StructuredOutputConverter structuredOutputConverter): 用于指定一个实例 StructuredOutputConverter,将 String 转换为实体类型。

3.5 stream() 返回值

还可以调用stream方法而不是call方法,在ChatClient上天指定stream方法,响应类型有几个选项:

  • Flux<String> content():返回由AI模型生成的字符串的Flux。
  • Flux<ChatResponse> chatResponse():返回 ChatResponse 对象的 Flux,其中包含有关响应的其他元数据。
  • Flux<ChatClientResponse> chatClientResponse():返回 ChatClientResponse 对象的 Flux,其中包含 ChatResponse 对象和 ChatClient 执行上下文,让可以访问在执行 advisors 期间使用的其他数据(例如,在 RAG 流程中检索的相关文档)。

3.6 定制 ChatClient 默认值

在前面 ChatClient 的初步体验中,我们使用 ChatClient.Builder.build() 快速创建了一个 ChatClient 实例,开发者还可以通过修改 ChatClient.Builder 定制 ChatClient 实例。

注意,创建 ChatClient 时指定的配置将作为与模型交互时的默认参数,这样可以避免每次调用都重复设置。

3.6.1 设置默认 System Message

在以下示例中,我们为 ChatClient 设置了一个默认的 system message(以动漫解说风格回答关于动漫的问题),这样,当 ChatClient 与模型交互时都会自动携带这条 system message,用户只需要指定 user message 即可。

/**
 * @author wasteland
 * @create 2025-07-27
 */
@Configuration
public class ChatClientConfig {
    @Bean
    ChatClient defaultTextChatClient(DeepSeekChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("以动漫解说风格回答关于动漫的问题")
                .build();
    }
}

在 Controller 中使用这个 ChatClient

/**
 * @author wasteland
 * @create 2025-07-25
 */
@RestController
public class ChatController {

    private final ChatClient chatClient;

    @Resource
    private ChatClient deepSeekChatClient;


    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/testDefaultSysText/chat")
    public String testDefaultSysText(@RequestParam(value = "input") String input) {
        return this.deepSeekChatClient.prompt().user(input).call().content();
    }
}

测试效果:

在这里插入图片描述

在上面 builder.defaultSystem() 创建 ChatClient 的时,我们还可以选择使用模板,类似 “以{style}风格回答关于动漫的问题“,这让我们有机会在每次调用前修改请求参数。

/**
 * @author wasteland
 * @create 2025-07-27
 */
@Configuration
public class ChatClientConfig {
    @Bean
    ChatClient defaultTextChatClient(DeepSeekChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("以{style}风格回答关于动漫的问题")
                .build();
    }
}

在 Controller 中使用这个 ChatClient

@RestController
public class ChatController {

    private final ChatClient chatClient;

    @Resource
    private ChatClient deepSeekChatClient;


    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/testDefaultSysTextWithParam/chat")
    public String testDefaultSysTextWithParam(@RequestParam(value = "input") String input,
                                              @RequestParam(value = "style") String style) {
        return this.deepSeekChatClient.prompt()
                .system(sp -> sp.param("style", style))
                .user(input)
                .call()
                .content();
    }
}

在这里插入图片描述

测试效果:

在这里插入图片描述

3.6.2 其他默认设置

除了 defaultSystem 之外,您还可以在 ChatClient.Builder 级别上指定其他默认提示。

  • defaultOptions(ChatOptions chatOptions):传入 ChatOptions 类中定义的可移植选项或特定于模型实现的如 DashScopeChatOptions 选项。有关特定于模型的ChatOptions实现的更多信息,请参阅 JavaDocs。

  • defaultFunction(String name, String description, java.util.function.Function<I, O> function):name 用于在用户文本中引用该函数,description解释该函数的用途并帮助 AI 模型选择正确的函数以获得准确的响应,参数 function 是模型将在必要时执行的 Java 函数实例。

  • defaultFunctions(String... functionNames):应用程序上下文中定义的 java.util.Function 的 bean 名称。

  • defaultUser(String text)、defaultUser(Resource text)、defaultUser(Consumer<UserSpec> userSpecConsumer) 这些方法允许您定义用户消息输入,Consumer允许您使用 lambda 指定用户消息输入和任何默认参数。

  • defaultAdvisors(RequestResponseAdvisor... advisor):Advisors 允许修改用于创建 Prompt 的数据,QuestionAnswerAdvisor 实现通过在 Prompt 中附加与用户文本相关的上下文信息来实现 Retrieval Augmented Generation 模式。

  • defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer):此方法允许您定义一个 Consumer 并使用 AdvisorSpec 配置多个 Advisor,Advisor 可以修改用于创建 Prompt 的最终数据,Consumer<AdvisorSpec> 允许您指定 lambda 来添加 Advisor 例如 QuestionAnswerAdvisor

您可以在运行时使用 ChatClient 提供的不带 default 前缀的相应方法覆盖这些默认值。

  • options(ChatOptions chatOptions)

  • function(String name, String description, java.util.function.Function<I, O> function)

  • functions(String... functionNames)

  • user(String text)、user(Resource text)、user(Consumer<UserSpec> userSpecConsumer)

  • advisors(RequestResponseAdvisor... advisor)

  • advisors(Consumer<AdvisorSpec> advisorSpecConsumer)

3.7 Advisors

在使用用户输入文本构建 Prompt 调用 AI 模型时,一个常见模式是使用上下文数据附加或扩充 Prompt,最终使用扩充后的 Prompt 与模型交互。

这些用于扩充 Prompt 的上下文数据可以是不同类型的,常见类型包括:

  • 您自己的数据:这是 AI 模型尚未训练过的数据,如特定领域知识、产品文档等,即使模型已经看到过类似的数据,附加的上下文数据也会优先生成响应。
  • 对话历史记录:聊天模型的 API 是无状态的,如果您告诉 AI 模型您的姓名,它不会在后续交互中记住它,每次请求都必须发送对话历史记录,以确保在生成响应时考虑到先前的交互。

ChatClient API 提供了一个 AdvisorSpec 接口用于配置 advisors。这个接口提供了添加参数、一次性设置多个参数以及向链中添加一个或多个 advisors 的方法。

在这里插入图片描述

在此配置中,MessageChatMemoryAdvisor 将首先执行,将对话历史添加到提示中。然后,QuestionAnswerAdvisor 将基于用户的问题和添加的对话历史执行其搜索,可能提供更相关的结果。

ChatClient.builder(chatModel)
    .build()
    .prompt()
    .advisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(),
        QuestionAnswerAdvisor.builder(vectorStore).build()
    )
    .user(userText)
    .call()
    .content();

注意:将 advisors 添加到链中的顺序至关重要,因为它决定了它们的执行顺序。每个 advisor 都以某种方式修改提示或上下文,一个 advisor 所做的更改会传递给链中的下一个。

3.7.1 检索增强生成(RAG)

向量数据库存储的是 AI 模型不知道的数据,当用户问题被发送到 AI 模型时,QuestionAnswerAdvisor 会在向量数据库中查询与用户问题相关的文档。来自向量数据库的响应被附加到用户消息 Prompt 中,为 AI 模型生成响应提供上下文。假设您已将数据加载到中 VectorStore,则可以通过向 ChatClient 提供 QuestionAnswerAdvisor 实例来执行检索增强生成 (RAG ) 。

ChatResponse response = ChatClient.builder(chatModel)
        .build().prompt()
        .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
        .user(userText)
        .call()
        .chatResponse();

在此示例中,SearchRequest.defaults() 将对 Vector 向量数据库中的所有文档执行相似性搜索。为了限制要搜索的文档类型,SearchRequest 采用了可移植到任意向量数据库中的类似 SQL 筛选表达式。

动态过滤表达式
SearchRequest 使用 FILTER_EXPRESSION Advisor 上下文参数在运行时更新过滤表达式:

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
    .build();

// Update filter expression at runtime
String content = chatClient.prompt()
    .user("Please answer my question XYZ")
    .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
    .call()
    .content();

FILTER_EXPRESSION 参数允许您根据提供的表达式动态过滤搜索结果。
关于检索增强生成的内容将在后面的文章中做更加详细的介绍。

3.7.2 聊天记忆

ChatMemory 接口表示聊天对话历史记录的存储,它提供向对话添加消息、从对话中检索消息以及清除对话历史记录的方法。

目前提供两种实现方式 InMemoryChatMemoryCassandraChatMemory,分别为聊天对话历史记录提供内存存储和 time-to-live 类型的持久存储。

创建一个包含 time-to-live 配置的 CassandraChatMemory

CassandraChatMemory.create(CassandraChatMemoryConfig.builder().withTimeToLive(Duration.ofDays(1)).build());

以下 Advisor 实现使用 ChatMemory 接口来使用对话历史记录来增强(advice)Prompt,这些 advisor 实现在如何将对话历史记录添加到 Prompt 的细节上有所不同。

  • MessageChatMemoryAdvisor:内存被检索并作为消息集合添加到提示中
  • PromptChatMemoryAdvisor:检索内存并将其添加到提示的系统文本中。
  • VectorStoreChatMemoryAdvisor :构造函数VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize)允许您指定要从中检索聊天历史记录的 VectorStore、唯一的对话 ID、要检索的聊天历史记录的大小(以令牌大小为单位)。

关于聊天记忆内容将在后面的文章中做更加详细的介绍。

3.7.3 日志记录

SimpleLoggerAdvisor 是一个用于记录 ChatClientrequestresponse 数据 Advisor,这对于调试和监控您的 AI 交互非常有用。
要启用日志记录,请在创建 ChatClient 时将 SimpleLoggerAdvisor 添加到 Advisor 链中。建议将其添加到链的末尾:

@GetMapping("/testAdvisor/chat")
public ChatResponse testAdvisor() {
    return chatClient.prompt()
            .advisors(new SimpleLoggerAdvisor())
            .user("请介绍一下灵笼动漫里的主要角色")
            .call()
            .chatResponse();
}

要查看日志,请将 Advisor 包的日志记录级别设置为 DEBUG

logging:
  level:
    org:
      springframework:
        ai:
          chat:
            client:
              advisor: DEBUG

将其添加到您的 application.propertiesapplication.yaml 文件中。您可以使用以下构造函数自定义如何使用 SimpleLoggerAdvisor 记录来自 AdvisedRequestChatResponse 的数据:

在这里插入图片描述
使用示例:

SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
    request -> "Custom request: " + request.userText,
    response -> "Custom response: " + response.getResult()
);

运行结果:

在这里插入图片描述


四、总结

通过掌握ChatClient的核心功能,开发者能快速构建稳定、高效的AI交互层,让业务系统轻松具备智能对话、内容生成、数据分析等能力。
通过本文的详细解析,相信你已掌握ChatClient的基本用法。在此,希望本文能对您有所帮助,在后续的文章中将持续更加深入的介绍更多多Spring AI相关知识。


五、参考资料


创作不易,如果有帮助到你的话请给点个赞吧!我是Wasteland,下期文章再见!

在这里插入图片描述

Logo

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

更多推荐