简介
本文旨在记录学习和实践 Spring AI Alibaba 提供的 ChatClient
组件的过程。ChatClient
是 Spring AI 中用于与大语言模型(LLM)进行交互的高级 API,它通过流畅(Fluent)的编程接口,极大地简化了构建聊天应用程序的复杂度。相比直接使用底层的 ChatModel
,ChatClient
封装了提示词构建、响应处理、结构化输出、流式响应以及与 RAG、聊天记忆等高级功能的集成。
通过本文的学习,我们将掌握:
ChatClient
的核心概念和优势。- 如何创建和配置
ChatClient
实例。 - 使用
ChatClient
处理不同类型的 AI 响应(文本、完整响应对象、结构化实体、流式响应)。 - 结合 Spring Boot 快速搭建一个可交互的聊天后端服务。
- 了解 Server-Sent Events (SSE) 在流式响应中的应用。
我们将从官方文档入手,结合代码实践,逐步深入理解 ChatClient
的使用方法。
ChatClient相关原理

核心知识点:ChatClient
in Spring AI Alibaba
ChatClient
是 Spring AI Alibaba 提供的一个更高级别的 API,用于与 AI 模型进行交互。它旨在简化开发流程,特别是当应用程序需要组合多个组件(如提示词模板、聊天记忆、模型本身、输出解析器、RAG 组件等)时。
1. ChatClient
简介
- 目的: 提供一个 Fluent API (流畅 API) 与 AI 模型通信,支持同步和反应式 (Reactive) 编程模型。
- 优势:
- 隐藏复杂性: 将与 LLM (Large Language Model) 及其他组件(提示词模板、聊天记忆、RAG 等)交互的复杂性封装起来。
- 减少样板代码: 相比直接使用
ChatModel
、Message
等原子 API,ChatClient
减少了需要编写的重复性代码。 - 类似服务层: 在应用程序中扮演类似服务层 (Service Layer) 的角色,直接为应用提供 AI 服务。
- 快速组装交互流程: 使用 Fluent API 可以快速地组装一个完整的 AI 交互流程。
- 基础功能:
- 定制和组装模型的输入 (
Prompt
)。 - 格式化解析模型的输出 (结构化输出
Structured Output
)。 - 调整模型交互参数 (
ChatOptions
)。
- 定制和组装模型的输入 (
- 高级功能:
- 聊天记忆 (
Chat Memory
)。 - 工具/函数调用 (
Function Calling
)。 - 检索增强生成 (
RAG
)。
- 聊天记忆 (
2. 创建 ChatClient
创建 ChatClient
实例需要使用 ChatClient.Builder
对象。有两种主要方式获取 ChatClient
:
- 方式一:使用自动配置的
ChatClient.Builder
(推荐)- Spring Boot 会根据你的依赖和配置自动创建一个默认的
ChatClient.Builder
Bean。 - 你只需要在你的类中注入这个
ChatClient.Builder
,然后调用build()
方法即可获得ChatClient
实例。 - 示例代码 (带详细注释):
- Spring Boot 会根据你的依赖和配置自动创建一个默认的
java
import org.springframework.ai.chat.client.ChatClient; // 导入 ChatClient 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 注解,用于处理 HTTP GET 请求
import org.springframework.web.bind.annotation.RequestParam; // 导入 RequestParam 注解,用于获取请求参数
import org.springframework.web.bind.annotation.RestController; // 导入 RestController 注解,标识这是一个 RESTful 控制器
@RestController // 声明这是一个 Spring MVC 的 REST 控制器,其方法默认返回 JSON 或其他指定格式的数据
public class ChatController {
// 声明一个 final 的 ChatClient 成员变量,用于与 AI 模型交互
private final ChatClient chatClient;
// 控制器的构造函数
// Spring Boot 会自动查找并注入一个 ChatClient.Builder 类型的 Bean
public ChatController(ChatClient.Builder builder) {
// 使用注入的 builder 构建 ChatClient 实例,并赋值给成员变量
// builder 会使用自动配置好的底层 ChatModel (例如通义千问模型) 和其他默认设置
this.chatClient = builder.build();
}
@GetMapping("/chat") // 将 HTTP GET 请求映射到 /chat 路径
// @RequestParam("input") String input 表示从请求参数中获取名为 "input" 的值,并赋给 input 变量
public String chat(@RequestParam("input") String input) {
// 使用 chatClient 的 Fluent API 开始构建一个请求
return this.chatClient.prompt() // 1. 调用 prompt() 方法开始构建一个 Prompt (提示)
.user(input) // 2. 调用 user() 方法设置用户角色的消息内容,内容为传入的 input 字符串
.call() // 3. 调用 call() 方法,执行与 AI 模型的同步调用 (发送请求并等待响应)
.content(); // 4. 调用 content() 方法,从 AI 模型的响应 (ChatResponse) 中提取文本内容并返回
}
}
- 方式二:以编程方式创建
ChatClient
-
禁用自动配置: 如果你想完全控制
ChatClient
的创建过程,或者需要使用多个不同的ChatModel
实例,可以先禁用ChatClient.Builder
的自动配置。在application.properties
或application.yml
中设置:propertiesspring.ai.chat.client.enabled=false
-
手动创建: 然后,你可以手动创建
ChatClient.Builder
,并传入你想要使用的ChatModel
实例。 -
示例代码 (带详细注释):
-
java
import org.springframework.ai.chat.client.ChatClient; // 导入 ChatClient 类
import org.springframework.ai.chat.model.ChatModel; // 导入 ChatModel 接口
// ... 其他 import
public class MyService {
private final ChatClient customChatClient;
// 假设 myChatModel 是你通过其他方式配置或注入的特定 ChatModel 实例
public MyService(ChatModel myChatModel) {
// 方法一:使用 ChatClient.builder() 静态方法,并传入 ChatModel 实例
ChatClient.Builder builder = ChatClient.builder(myChatModel);
// 这里可以继续使用 builder 配置其他选项,例如 .defaultOptions(), .defaultSystem(), etc.
this.customChatClient = builder.build(); // 构建 ChatClient 实例
// 方法二:使用 ChatClient.create() 静态方法,这是一个更简洁的方式
// 它会使用传入的 ChatModel 和默认的 Builder 设置来创建 ChatClient
// this.customChatClient = ChatClient.create(myChatModel);
}
public String askSomething(String question) {
// 使用手动创建的 customChatClient 进行交互
return customChatClient.prompt()
.user(question)
.call()
.content();
}
}
3. 处理 ChatClient
响应
ChatClient
API 提供了多种方式来处理和格式化来自 AI 模型的响应:
-
返回
ChatResponse
:call()
或stream()
方法默认返回ChatResponse
对象。ChatResponse
是一个丰富的结构,包含:- AI 生成的实际结果 (
Generation
)。 - 与响应生成相关的元数据 (例如模型名称、token 使用情况等)。
- 可能包含多个子响应结果 (如果模型支持,例如返回多个候选答案)。
- AI 生成的实际结果 (
- 你可以从
ChatResponse
中获取详细信息。 - 示例: (在上面的
/chat
接口示例中,.call()
返回的就是ChatResponse
,之后.content()
是从中提取内容)
-
返回实体类 (Entity) - 结构化输出:
ChatClient
支持将 AI 模型的文本输出自动映射为你定义的 Java POJO (Plain Old Java Object)。这对于需要固定格式输出的场景非常有用。- 使用
.entity(YourClass.class)
方法来指定期望的输出类型。 - 示例代码 (带详细注释):
java
import org.springframework.ai.chat.client.ChatClient; // 导入 ChatClient 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 注解
import org.springframework.web.bind.annotation.RequestParam; // 导入 RequestParam 注解
import org.springframework.web.bind.annotation.RestController; // 导入 RestController 注解
@RestController
public class StructuredOutputController {
private final ChatClient chatClient;
// 定义一个简单的 POJO 类,用于接收结构化输出
static class ActorFilms {
public String actor; // 演员姓名
public List<String> movies; // 电影列表
}
public StructuredOutputController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/actor-films") // 将 HTTP GET 请求映射到 /actor-films 路径
public ActorFilms getActorFilms(@RequestParam("actor") String actorName) {
// 使用 chatClient 的 Fluent API
return this.chatClient.prompt()
// 设置用户消息,要求模型列出指定演员的电影,并明确要求 JSON 格式
.user("Generate a list of films for the actor " + actorName + ". Respond in JSON format with keys 'actor' and 'movies'.")
.call() // 执行与 AI 模型的同步调用
// 调用 entity() 方法,并传入 ActorFilms.class
// ChatClient 会尝试将 AI 返回的文本内容 (预期是 JSON 字符串)
// 解析并映射到 ActorFilms 类的实例中
.entity(ActorFilms.class);
}
}
- 流式响应 (Streaming):
- 对于需要实时显示或处理部分结果的场景 (例如聊天机器人),可以使用流式响应。
- 调用
.stream()
方法代替.call()
。 .stream()
方法返回一个Flux<ChatResponse>
(如果使用了 Reactive 库) 或支持类似的流式处理机制。你可以订阅这个流来接收模型逐步生成的内容块。- 示例代码 (概念性,具体实现依赖 Reactive 库如 Project Reactor):
java
import org.springframework.ai.chat.client.ChatClient; // 导入 ChatClient 类
import org.springframework.http.MediaType; // 导入 MediaType 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 注解
import org.springframework.web.bind.annotation.RequestParam; // 导入 RequestParam 注解
import org.springframework.web.bind.annotation.RestController; // 导入 RestController 注解
import reactor.core.publisher.Flux; // 导入 Flux 类 (来自 Project Reactor)
import org.springframework.ai.chat.model.ChatResponse; // 导入 ChatResponse 类
@RestController
public class StreamingChatController {
private final ChatClient chatClient;
public StreamingChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
// produces = MediaType.TEXT_EVENT_STREAM_VALUE 指定响应类型为 Server-Sent Events (SSE)
@GetMapping(value = "/stream-chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam("input") String input) {
// 使用 chatClient 的 Fluent API
return this.chatClient.prompt()
.user(input) // 设置用户消息
.stream() // 1. 调用 stream() 方法,启动与 AI 模型的流式交互,返回 Flux<ChatResponse>
.content(); // 2. 调用 content() 方法 (针对 Flux),将 ChatResponse 流映射为 String 内容流
// 每次模型生成一部分内容,就会在这个 Flux 中发布一个 String 片段
}
// 如果需要更详细的流信息,可以直接处理 Flux<ChatResponse>
@GetMapping(value = "/stream-chat-response", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ChatResponse> streamChatResponse(@RequestParam("input") String input) {
return this.chatClient.prompt()
.user(input)
.stream() // 直接返回 ChatResponse 的流
.chatResponse(); // 获取原始的 ChatResponse 流
}
}
4. 定制 ChatClient
默认值
可以在创建 ChatClient
时设置一些默认行为,这些默认值会应用于该 ChatClient
实例发出的所有请求,除非在单次请求中被覆盖。
- 设置默认系统消息 (System Message):
- 系统消息通常用于给 AI 模型设定角色、提供指令或上下文背景。
- 在
ChatClient.Builder
上调用.defaultSystem(...)
方法。 - 示例:
java
ChatClient chatClient = ChatClient.builder(chatModel)
// 设置默认的系统消息,告诉 AI 它是一个乐于助人的 AI 助手
.defaultSystem("You are a helpful AI assistant.")
.build();
// 后续使用这个 chatClient 发送请求时,会自动带上这个系统消息
chatClient.prompt().user("What is the capital of France?").call().content();
- 其他默认设置:
- 默认用户消息:
.defaultUser(...)
- 默认模型选项:
.defaultOptions(...)
,可以设置温度 (temperature)、最大 token 数 (maxTokens) 等模型参数。 - 默认函数:
.defaultFunctions(...)
(用于 Function Calling) - 默认头信息:
.defaultHeaders(...)
(可能用于特定模型的 API 调用)
- 默认用户消息:
5. Advisors (增强器/顾问)
- Advisors 是一种强大的机制,用于向
ChatClient
的请求/响应流程中添加额外的功能,类似于中间件 (Middleware) 或 AOP (Aspect-Oriented Programming) 中的切面。 - 它们可以在请求发送前对其进行修改,或者在收到响应后对其进行处理。
- 通过
ChatClient.Builder
的.defaultAdvisors(...)
方法添加。 - 常见的 Advisors 应用场景:
- 检索增强生成 (RAG): 在发送用户问题给 LLM 之前,先从知识库 (Vector Store) 中检索相关文档,并将文档内容添加到提示词中。
QuestionAnswerAdvisor
是一个常见的 RAG Advisor。 - 聊天记忆 (Chat Memory): 在发送请求前,从
ChatMemory
组件中加载历史对话记录,并将其添加到提示词中;在收到响应后,将当前的问答对保存到ChatMemory
中。ChatMemoryAdvisor
用于此目的。 - 日志记录: 记录请求和响应的详细信息。
- 检索增强生成 (RAG): 在发送用户问题给 LLM 之前,先从知识库 (Vector Store) 中检索相关文档,并将文档内容添加到提示词中。
- 示例 (概念性):
java
// 假设 vectorStore 和 chatMemory 是已经配置好的 Bean
VectorStore vectorStore = ...;
ChatMemory chatMemory = ...;
ChatClient chatClient = ChatClient.builder(chatModel)
// 添加一个 RAG Advisor,使用指定的 VectorStore 进行检索
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
// 添加一个聊天记忆 Advisor
.defaultAdvisors(new ChatMemoryAdvisor(chatMemory))
.build();
// 现在使用这个 chatClient 时,会自动进行 RAG 检索和聊天记忆管理
chatClient.prompt().user("Tell me about Spring AI based on my previous questions.").call().content();
总结:
ChatClient
是 Spring AI Alibaba 中进行 AI 模型交互的核心组件之一。它通过 Fluent API 极大地简化了与 AI 模型的通信,隐藏了底层实现的复杂性,并提供了强大的功能,如结构化输出、流式响应和通过 Advisors 实现的 RAG、聊天记忆等高级特性。掌握 ChatClient
的使用对于在 Spring 应用中高效地集成 AI 功能至关重要。
实践记录
配置项目

xml
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>
为了实践ChatClient的使用,我们创建了一个简单的Spring Boot项目,在pom.xml文件中引入spring-ai-alibaba-starter
依赖。

yaml
server:
port: 10001
spring:
application:
name: spring-ai-alibaba-dashscope-chat-example
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
在application.yml文件中,配置spring.ai.dashscope.api-key
,用于设置Dashscope的API密钥,使其从环境变量中读取。
调用content方法

这段代码展示了如何使用 Spring AI Alibaba 快速构建一个简单的聊天接口。 通过 Spring Boot 的 @RestController
注解,我们创建了一个 RESTful 控制器 ChatClientController
,它依赖于 Spring AI 提供的 ChatClient
组件。 构造函数中,我们利用 ChatClient.Builder
构建了一个 ChatClient
实例,Spring AI 负责自动注入这个 Builder。 核心逻辑在 chat()
方法中,这个方法通过 @GetMapping("/chat")
注解映射到 HTTP GET 请求。 当用户访问 /chat?input=你的消息
时,chat()
方法会被调用,它会接收 URL 中的 input
参数作为用户输入,并使用 ChatClient
向配置好的 AI 模型发送请求,最终将 AI 模型的响应文本内容返回给客户端。 这个简单的接口可以轻松集成到前端应用中,实现基本的对话功能,是快速体验和构建 AI 应用的理想起点。注释后的代码如下所示。
java
package com.miku.springaialibabalearn.controller;
import org.springframework.ai.chat.client.ChatClient; // 导入 Spring AI 提供的 ChatClient 接口
import org.springframework.web.bind.annotation.GetMapping; // 导入 Spring MVC 的 GetMapping 注解,用于处理 GET 请求
import org.springframework.web.bind.annotation.RestController; // 导入 Spring MVC 的 RestController 注解,表明这是一个 RESTful 控制器
/**
* RestController 注解表明这是一个 RESTful 控制器,用于处理 HTTP 请求并返回数据。
* 此类负责处理与 ChatClient 交互的 HTTP 请求。
*/
@RestController
public class ChatClientController {
private final ChatClient chatClient; // 声明一个私有 final 的 ChatClient 类型的成员变量,用于进行聊天交互
/**
* 构造器注入 ChatClient.Builder,并通过 builder 构建 ChatClient 实例。
* Spring 会自动解析 ChatClient.Builder 并注入到这里。
*
* @param builder ChatClient 的构建器,由 Spring AI 自动提供。
*/
public ChatClientController( ChatClient.Builder builder ) {
// 使用 ChatClient.Builder 构建 ChatClient 实例并赋值给成员变量 chatClient
this.chatClient = builder.build();
}
/**
* GetMapping 注解将 HTTP GET 请求 "/chat" 映射到 chat() 方法。
* 当接收到 "/chat" 的 GET 请求时,此方法将被调用。
*
* @param input 请求参数 input,从 URL 的 query parameter 中获取,例如 /chat?input=你好。
* Spring MVC 会自动将 URL 中的 input 参数值绑定到此方法参数。
* @return String 返回 AI 聊天模型的响应内容,以纯文本形式返回。
*/
@GetMapping("/chat")
// 将 HTTP GET 请求映射到 /chat 路径
public String chat( @RequestParam("input") String input ) {
// @RequestParam("input") String input 表示从请求参数中获取名为 "input" 的值,并赋给 input 变量
// 使用 chatClient 的 Fluent API 开始构建一个请求
return this.chatClient.prompt()
// 1. 调用 prompt() 方法开始构建一个 Prompt (提示)
.user(input)
// 2. 调用 user() 方法设置用户角色的消息内容,内容为传入的 input 字符串
.call()
// 3. 调用 call() 方法,执行与 AI 模型的同步调用 (发送请求并等待响应)
.content();
// 4. 调用 content() 方法,从 AI 模型的响应 (ChatResponse) 中提取文本内容并返回
}
}

启动Application.java
。

使用Postman发送GET类型的HTTP请求:http://localhost:10001/chat?input=你好,介绍依赖注入的原理
。可以看到成功获得了dashscope模型的响应。
调用chatResponse方法

java
/**AI 模型的响应是一种由ChatResponse类型定义的丰富结构。它包含响应生成相关的元数据,同时它还可以包含多个子响应(称为Generation),每个子响应都有自己的元数据。元数据包括用于创建响应的令牌(token)数量信息(在英文中,每个令牌大约为一个单词的 3/4),了解令牌信息很重要,因为 AI 模型根据每个请求使用的令牌数量收费。 */
@GetMapping( "/response" )
public ChatResponse response(String input ) {
return this.chatClient.prompt()
.user(input)
// 设置了用户消息的内容
.call()
// call 方法向 AI 模型发送请求
.chatResponse();
// 调用 chatResponse() 返回 ChatResponse
}

在浏览器使用http://localhost:10001/response?input=你好
发起http请求,返回 ChatResponse。
调用entity方法

java
// 定义一个 HTTP GET 请求处理方法,映射到 "/singer-songs" 路径
@GetMapping("/singer-songs")
// 返回类型为 SingerSongs,表示该方法会返回一个 SingerSongs 类型的对象,Spring MVC 会自动将其转换为 JSON 格式返回给客户端
public SingerSongs chatEntity(@RequestParam("name") String name) {
// 使用 @RequestParam("name") 注解将 HTTP 请求参数 "name" 绑定到方法参数 String name 上
// 使用 chatClient 的 Fluent API 构建 Prompt 并调用 AI 模型
return this.chatClient.prompt()
// 1. 开始构建 Prompt (提示) 对象,prompt() 方法是 Fluent API 的起始点
.user("Generate a list of songs of the singer " + name + ". Respond in JSON format with keys 'singer' and 'songs'. ")
// 2. 设置用户消息 (User Message),作为 Prompt 的内容
// - user() 方法指定消息的角色为用户 (User)
// - 消息内容是动态生成的字符串,包含用户输入的歌手名字 (name)
// - 指示 AI 模型生成指定歌手的歌曲列表,并明确要求以 JSON 格式返回
// - JSON 格式要求包含 "singer" 和 "songs" 两个键
.call()
// 3. 调用 call() 方法,执行与 AI 模型的同步请求并等待响应
// - call() 方法会将之前构建的 Prompt 发送给配置好的 AI 模型服务
// - 它会返回一个 ChatResponse 对象,包含了 AI 模型的响应结果
.entity(SingerSongs.class);
// 4. 调用 entity(SingerSongs.class) 方法,将 AI 模型的响应结果映射为 SingerSongs 类型的对象
// - entity() 方法假设 AI 模型返回的响应是 JSON 格式的字符串
// - 它会自动将 JSON 字符串反序列化 (解析) 成 SingerSongs 类的实例
// - SingerSongs.class 指定了目标实体类的类型,Spring AI 会根据这个类型进行映射
// 整个方法流程:
// 1. 接收 HTTP GET 请求,并从请求参数中获取歌手名字 (name)。
// 2. 构建一个 Prompt,包含指示 AI 模型生成歌手歌曲列表的指令,并要求 JSON 格式输出。
// 3. 使用 ChatClient 调用 AI 模型,发送 Prompt 并获取 ChatResponse。
// 4. 将 ChatResponse 中的 JSON 格式内容映射为 SingerSongs 对象。
// 5. 返回 SingerSongs 对象,Spring MVC 会自动将其转换为 JSON 格式返回给客户端。
}

java
package com.miku.springaialibabalearn.entity;
import java.util.List;
public class SingerSongs {
private String singer;
// 歌手名字,对应 JSON 中的 "singer" 键
private List<String> songs;
// 歌曲列表,对应 JSON 中的 "songs" 键
// 必须要有默认的无参构造器,以便 Jackson (或其他 JSON 库) 可以进行反序列化
public SingerSongs() {
}
// Getter 和 Setter 方法 (建议添加,虽然在这个例子中可能不是必须的,但良好的 Java 实践)
public String getSinger() {
return singer;
}
public void setSinger(String singer) {
this.singer = singer;
}
public List<String> getSongs() {
return songs;
}
public void setSongs(List<String> songs) {
this.songs = songs;
}
}

在浏览器使用http://localhost:10001/singer-songs?name=初音未来
发起http请求,返回JSON格式的singer
和songs
的键值对数据。
调用stream方法

java
@GetMapping( value = "/stream-chat")
// 使用 @GetMapping 注解,将 HTTP GET 请求路径 "/stream-chat" 映射到 streamChat 方法
// value = "/stream-chat" 指定了请求的 URL 路径
public Flux<String> streamChat(@RequestParam("input") String input) {
// 定义名为 streamChat 的方法,处理 "/stream-chat" 的 GET 请求
// 方法的返回类型是 Flux<String>,表示一个包含多个 String 元素的异步数据流 (Reactive Stream)
// 使用 @RequestParam("input") 注解,将 HTTP 请求参数名为 "input" 的值绑定到方法的 input 参数上,类型为 String
return this.chatClient.prompt()
// 1. 使用 chatClient 的 Fluent API 开始构建 Prompt (提示) 对象
// prompt() 方法开始构建一个新的 Prompt,它是与 AI 模型交互的输入
.user(input)
// 2. 设置用户消息 (User Message) 到 Prompt 中
// user(input) 方法指定消息的角色为 "user",消息内容为方法的参数 input (用户输入)
.stream()
// 3. 调用 stream() 方法,执行与 AI 模型的流式交互
// stream() 方法会将构建好的 Prompt 发送给配置的 AI 模型服务,并启动流式响应
// 返回值是一个 Flux<ChatResponse>,表示 ChatResponse 对象的流
.content();
// 4. 调用 content() 方法,从 Flux<ChatResponse> 中提取内容并转换为 Flux<String>
// content() 方法将 Flux<ChatResponse> 转换为 Flux<String>,只保留每个 ChatResponse 中的文本内容 (getContent())
// 最终返回 Flux<String>,每个 String 元素代表模型流式输出的一部分文本
// 整个 streamChat 方法的流程:
// 1.接收用户通过 HTTP GET 请求传递的 "input" 参数作为用户输入
// 2.构建一个 Prompt,包含用户输入的消息
// 3.使用 ChatClient 发起流式聊天请求,将 Prompt 发送给 AI 模型
// 4.从模型返回的流式 ChatResponse 中提取文本内容,并作为 Flux<String> 返回
// 5.Spring WebFlux 会将返回的 Flux<String> 作为 Server-Sent Events (SSE) 流式响应返回给客户端
}
@GetMapping( value = "/stream-chat-response")
// 使用 @GetMapping 注解,将 HTTP GET 请求路径 "/stream-chat-response" 映射到 streamChatResponse 方法
// value = "/stream-chat-response" 指定了请求的 URL 路径
public Flux<ChatResponse> streamChatResponse(@RequestParam("input") String input) {
// 定义名为 streamChatResponse 的方法,处理 "/stream-chat-response" 的 GET 请求
// 方法的返回类型是 Flux<ChatResponse>,表示一个包含多个 ChatResponse 对象的异步数据流
// 使用 @RequestParam("input") 注解,将 HTTP 请求参数名为 "input" 的值绑定到方法的 input 参数上,类型为 String
return this.chatClient.prompt()
// 1. 使用 chatClient 的 Fluent API 开始构建 Prompt (提示) 对象
// prompt() 方法开始构建一个新的 Prompt,它是与 AI 模型交互的输入
.user(input)
// 2. 设置用户消息 (User Message) 到 Prompt 中
// user(input) 方法指定消息的角色为 "user",消息内容为方法的参数 input (用户输入)
.stream()
// 3. 调用 stream() 方法,执行与 AI 模型的流式交互
// stream() 方法会将构建好的 Prompt 发送给配置的 AI 模型服务,并启动流式响应
// 返回值是一个 Flux<ChatResponse>,表示 ChatResponse 对象的流
.chatResponse();
// 4. 调用 chatResponse() 方法,直接返回 Flux<ChatResponse> 对象流
// chatResponse() 方法直接返回 stream() 方法返回的 Flux<ChatResponse>,不做任何转换
// 最终返回 Flux<ChatResponse>,每个 ChatResponse 对象包含更完整的模型响应信息 (包括文本内容和元数据等)
// 整个 streamChatResponse 方法的流程:
// 1.接收用户通过 HTTP GET 请求传递的 "input" 参数作为用户输入
// 2.构建一个 Prompt,包含用户输入的消息
// 3.使用 ChatClient 发起流式聊天请求,将 Prompt 发送给 AI 模型
// 4.直接将模型返回的流式 ChatResponse 对象作为 Flux<ChatResponse> 返回
// 5.Spring WebFlux 会将返回的 Flux<ChatResponse> 作为 Server-Sent Events (SSE) 流式响应返回给客户端
// 6.客户端可以接收到完整的 ChatResponse 对象,包含更详细的响应信息
}

在浏览器使用http://localhost:10001/stream-chat?input=介绍初音未来
发起http请求,得到字符串的流式响应。

在浏览器使用http://localhost:10001/stream-chat-response?input=介绍初音未来
发起http请求,得到ChatResponse类型的流式响应。
Server-Sent Events (SSE)

在我们的 Spring AI 应用中,为了实现 AI 回复的流式效果(即内容逐步显示而非一次性返回),我们利用了 Server-Sent Events (SSE) 技术。这通过在 Controller 方法上添加特定的 @GetMapping
注解来实现。
让我们深入理解这个关键注解:
java
@GetMapping( value = "/stream-chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
解读如下:
@GetMapping
与value
属性
@GetMapping
: 这是 Spring MVC 中用于处理 HTTP GET 请求的常用注解。value = "/stream-chat"
: 这个属性定义了该方法处理的 URL 路径。当客户端向/stream-chat
发送 GET 请求时,Spring 会调用这个方法。
-
produces = MediaType.TEXT_EVENT_STREAM_VALUE
属性这是实现流式响应的核心部分。
produces
属性: 指定了方法返回的响应内容的 MIME 类型 (Media Type)。MediaType.TEXT_EVENT_STREAM_VALUE
: 这个常量代表 MIME 类型text/event-stream
。当浏览器或客户端看到这个响应类型时,它就知道服务器将使用 Server-Sent Events (SSE) 协议来发送数据。
-
什么是 Server-Sent Events (SSE)?
SSE 是一种允许服务器向客户端单向推送更新的技术。
- 工作方式: 客户端发起一个 HTTP 请求,服务器保持该连接打开,并可以持续地向客户端发送数据流(事件),而无需客户端再次请求。
- 特点:
- 单向通信: 数据仅从服务器流向客户端。
- 基于 HTTP: 使用标准 HTTP 协议,易于实现和部署。
- 文本协议: 数据以简单的文本格式发送,易于解析。
- 自动重连: 浏览器通常会自动处理断线重连。
- 与传统 HTTP 的区别: 传统 HTTP 是请求-响应模式,服务器发送一次响应后连接即关闭。SSE 保持连接打开以持续推送。
- 与 WebSocket 的区别: WebSocket 提供双向通信,允许客户端和服务器互相发送消息,协议也不同于 HTTP。SSE 更轻量,适用于服务器单方面推送数据的场景。
- 应用场景: 实时通知、股票行情、状态更新、以及我们这里的流式 AI 对话响应。
总结
@GetMapping(value = "/stream-chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
注解的整体含义是:
创建一个处理
/stream-chat
路径 GET 请求的端点,该端点将使用 Server-Sent Events (SSE) 技术,以text/event-stream
格式向客户端持续推送数据流。
在我们的 Spring AI 聊天应用中,这意味着当客户端请求 /stream-chat
时,后端不会一次性返回完整的 AI 回复,而是将 AI 模型逐步生成的文本块作为 SSE 事件流式地发送给客户端,从而在前端实现打字机般的显示效果。
编写前端界面




用Trae辅助简单编写了一个初音未来网页,点击右上角"AI聊天",进入对话界面。


发送消息,前端向后端的streamChat方法发起请求,可以看到成功实现了流式对话。
总结与展望
通过本次学习和实践,我们深入了解了 Spring AI Alibaba 中 ChatClient
的核心用法。我们掌握了:
ChatClient
的创建与配置: 包括依赖引入、API Key 配置以及通过ChatClient.Builder
获取实例。- Fluent API 的便捷性: 使用链式调用
.prompt().user().call()/stream().content()/entity()/chatResponse()
可以清晰、简洁地构建与 AI 模型的交互逻辑。 - 多样化的响应处理: 学习了如何获取纯文本内容 (
.content()
)、完整的响应对象 (.chatResponse()
)、自动映射的 Java 实体 (.entity()
) 以及实现实时效果的流式响应 (.stream()
)。 - SSE 的应用: 理解了如何通过设置
produces = MediaType.TEXT_EVENT_STREAM_VALUE
结合Flux
来实现 Server-Sent Events,为前端提供流式数据。
ChatClient
作为 Spring AI 的高级抽象,有效地降低了在 Spring 应用中集成 LLM 的门槛。它不仅简化了基础的对话交互,还为集成 RAG(通过 QuestionAnswerAdvisor
)、聊天记忆(通过 ChatMemoryAdvisor
)等高级功能提供了便利的扩展点 (Advisors)。
后续可以探索的方向包括:
- 深入研究
ChatMemoryAdvisor
实现多轮对话记忆。 - 结合
VectorStore
和QuestionAnswerAdvisor
构建 RAG 应用,让 AI 能基于私有知识库回答问题。 - 探索 Function Calling/Tools 功能,让 AI 能够调用外部 API 或执行特定任务。
- 学习更复杂的提示词工程(Prompt Engineering)技巧。
- 完善前端聊天界面,增加用户体验。
总而言之,ChatClient
是 Spring AI 生态中一个非常实用的工具,掌握它将为我们构建更智能、更强大的 Java 应用打下坚实的基础。