目录
- 一、ChatClient
-
- [1.1 实现简单对话](#1.1 实现简单对话)
- [1.2 ⻆⾊预设](#1.2 ⻆⾊预设)
- [1.3 结构化输出](#1.3 结构化输出)
- [1.4 实现流式输出](#1.4 实现流式输出)
- [1.5 打印日志](#1.5 打印日志)
- 二、流式编程
-
- [2.1 SSE协议](#2.1 SSE协议)
- [2.2 Spring中的SSE实现](#2.2 Spring中的SSE实现)
- 三、ChatModel
-
- [3.1 实现简单对话](#3.1 实现简单对话)
- [3.2 ⻆⾊预设](#3.2 ⻆⾊预设)
- [3.3 实现流式输出](#3.3 实现流式输出)
- [3.4 ChatModel与ChatClient对比](#3.4 ChatModel与ChatClient对比)

Spring AI 的聊天模型,通过标准化的接⼝设计,使开发⼈员可以将AI模型的聊天功能集成到应⽤程序中.它利⽤预先训练的语⾔模型,例如GPT(GenerativePre-trainedTransformer),以⾃然语⾔⽣成类似⼈类的响应.
API 的⼯作原理通常是向AI模型发送提⽰或部分对话,然后AI模型根据其训练数据和对⾃然语⾔模式的理解⽣成响应.然后,把响应将返回给应⽤程序,应⽤程序可以将其呈现给⽤⼾或将其⽤于进⼀步处理.
在SpringAI框架中,ChatModel和ChatClient是构建对话式AI应⽤的两⼤核⼼接⼝.
一、ChatClient
ChatClient 是 Spring AI 框架中封装复杂交互流程的⾼阶API接⼝,旨在简化开发者与⼤语⾔模型(如GPT、通义千问等)的集成过程.
ChatClient 提供了与AI模型通信的FluentAPI(流程接口,链式调用,可读性高),它⽀持同步和反应式(Reactive)编程模型,将与LLM及其他组件交互的复杂性进⾏封装,给⽤⼾提供开箱即⽤的服务
1.1 实现简单对话
根据⽤⼾输⼊的消息,进⾏响应.
参考链接:https://docs.spring.io/spring-ai/reference/api/chatclient.html
- 创建ChatClient,并注⼊
使⽤ ChatClient.Builder 来创建ChatClient对象
java
private final ChatClient client;
public ChatClientController(ChatClient.Builder clientBuilder) {
this.client = clientBuilder.build();
}
- 使⽤ChatClient
java
@RequestMapping("/call")
public String call(String message) {
return client.prompt()
//⽤⼾输⼊的信息
.user(message)
//请求模型
.call()
//获取模型返回的信息
.content();
}
- user 方法 把⽤⼾输⼊的内容设置为⽤⼾消息的内容
- call ⽅法向AI模型发送请求
- content ⽅法以字符串的形式返回AI模型的响应
java
package com.spring.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/chat")
public class ChatClientController {
private final ChatClient client;
public ChatClientController(ChatClient.Builder clientBuilder) {
this.client = clientBuilder.build();
}
@RequestMapping("/call")
public String call(String message) {
return client.prompt()
//⽤⼾输⼊的信息
.user(message)
//请求模型
.call()
//获取模型返回的信息
.content();
}
}
测试:http://127.0.0.1:8080/chat/call?message=2026春节

1.2 ⻆⾊预设
我们应⽤程序接⼊DeepSeek之后,可以设置⾃⼰智能助⼿的名字。
配置角色:
在SpringAI的 ChatClient.Builder 中, 使用 defaultSystem() ⽅法⽤于设置AI模型的默认系统消息(SystemMessage),它会作为对话的基础⻆⾊设定或初始指令。通过ChatClient.Builder链式调⽤设置后,该⽅法定义的⽂本会作为系统消息注⼊到每次对话的上下⽂中,⽤于引导AI的回复⻛格或⾝份设定.
我们通过 defaultSystem()方法来配置系统⻆⾊。并将这个方法提取出来交给spring管理。
java
package com.spring.ai.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient ChatClientController(ChatClient.Builder clientBuilder) {
return clientBuilder
.defaultSystem("你是⼀个专业的聊天机器人,你叫鸽鸽,你会回答用户的问题")
.build();
}
}
测试:http://127.0.0.1:8080/chat/call?message=你是谁

1.3 结构化输出
想从LLM接收结构化输出,SpringAI⽀持将 ChatModel/ChatClient ⽅法的返回类型从 String 更改为其他类型.
通过 entity() ⽅法将模型输出转为⾃定义实体,需确保输出格式符合JSON规范。
借助JDK16提供的新关键词record来定义⼀个实体类:
java
record Recipe(String dish, List<String> ingredients) {}
@RequestMapping("/entity")
public String entity(String message) {
Recipe recipe = client.prompt()
//⽤⼾输⼊的信息
.user(message)
//请求模型
.call()
//获取模型返回的信息
.entity(Recipe.class);
return recipe.toString();
}
测试:http://127.0.0.1:8080/chat/entity?message=你是谁

1.4 实现流式输出
⽤⼾和⼤模型进⾏交互时,由于⼤模型⼀次输出内容较多,等待全部内容⽣成完毕会导致⽤⼾等待时间过⻓,这对⽤⼾的体验⾮常不友好.可以采⽤流式输出的⽅式(例如ChatGPT和DeepSeek逐字显⽰回答).
⼤模型流式输出(StreamingOutput)是通过逐步⽣成内容⽽⾮⼀次性返回完整结果的技术.SpringAI 使⽤
ChatClient 的 stream() ⽅法⽣成 Flux 流,适⽤于需要更轻量级客⼾端控制的场景.
java
@RequestMapping(value = "/stream",produces = "text/html;charset=UTF-8")
public Flux<String> stream(String message) {
return client.prompt()
//⽤⼾输⼊的信息
.user(message)
//请求模型,流式输出
.stream()
//获取模型返回的信息
.content();
}
测试:http://127.0.0.1:8080/chat/stream?message=你是谁

1.5 打印日志
Advisors介绍:
Spring AI中的Advisors是介于⽤⼾请求与AI模型之间的中间件组件,它的核⼼功能就是对请求进⾏拦截过滤和增强,帮助我们在API调⽤前后解决各种问题,例如调⽤前参数如何构建,调⽤后结果如何处理。Spring AI 中的Advisors是基于AOP思想实现的,在具体实现上进⾏了领域适配。其设计核⼼借鉴了Spring AOP的拦截机制,各个Advisor以链式结构运⾏,序列中的每个Advisor都有机会对传⼊的请求和传出的响应进⾏处理。这种链式处理机制确保了每个Advisor可以在请求和响应流中添加⾃⼰的逻辑,从⽽实现更灵活和可定制的功能.

SimpleLoggerAdvisor:Spring AI 内置了⼀些Advisor,SimpleLoggerAdvisor 作为其中之⼀, 主要功能是记录⽇志.使⽤只需把它添加到Advisor链中,即可⾃动记录所有经过该Advisor的聊天请求和响应,并且可以对其进⾏配置,⽐如⽇志级别和⽇志格式。
两种使用方式:
- 添加在配置类中:

- 为具体对话添加:

配置日志级别:
java
logging:
level:
org.springframework.ai.chat.client.advisor: debug
测试:http://127.0.0.1:8080/chat/stream?message=你是谁


二、流式编程
2.1 SSE协议
HTTP协议本⾝设计为⽆状态的请求-响应模式,严格来说,是⽆法做到服务器主动推送消息到客⼾端,但通过Server-SentEvents(服务器发送事件,简称SSE)技术可实现流式传输,允许服务器主动向浏览器推送数据流。也就是说,服务器向客⼾端声明,接下来要发送的是流消息(streaming),这时客⼾端不会关闭连接,会⼀直等待服务器发送过来新的数据流.
SSE(Server-Sent Events)是⼀种基于HTTP的轻量级实时通信协议,浏览器通过内置的 EventSource API接收并处理这些实时事件。
核⼼特点
- 基于HTTP协议
复⽤标准HTTP/HTTPS协议,⽆需额外端⼝或协议,兼容性好且易于部署 - 单向通信机制
SSE仅⽀持服务器向客⼾端的单向数据推送,客⼾端通过普通HTTP请求建⽴连接后,服务器可持续发送数据流,但客⼾端⽆法通过同⼀连接向服务器发送数据. - ⾃动重连机制
⽀持断线重连,连接中断时,浏览器会⾃动尝试重新连接(⽀持retry字段指定重连间隔) - ⾃定义消息类型
客⼾端发起请求后,服务器保持连接开放,响应头设置 Content-Type: text/event-stream ,标识为事件流格式,持续推送事件流
数据格式:服务端向浏览器发送SSE数据,需要设置必要的HTTP头信息。
java
Content-Type: text/event-stream;charset=utf-8
Connection: keep-alive
每⼀次发送的消息,由若⼲个message组成,每个message之间由 \n\n 分隔,每个message内部由若⼲⾏组成,每⼀⾏都是如下格式:[field]: value\n
Field 可以取值为:
- data[必需]:数据内容
- event[⾮必需]:表⽰⾃定义的事件类型,默认是message事件
- id[⾮必需]:数据标识符,相当于每⼀条数据的编号
- retry[⾮必需]:指定浏览器重新发起连接的时间间隔
- 除此之外,还可以有冒号 : 开头的⾏,表⽰注释.
服务端实现:
java
package com.spring.ai.controller;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
public class SSEController {
@RequestMapping("/data")
public void data(HttpServletResponse response) throws InterruptedException, IOException {
response.setContentType("text/event-stream;charset=UTF-8");
PrintWriter writer = response.getWriter();
String s = "";
for (int i = 0; i < 20; i++) {
s = "data: " +new Date() + "\n\n";
writer.write(s);
writer.flush();
Thread.sleep(1000);
}
}
}
客户端API:
java
<div id="sse"></div>
<script>
let eventSource = new EventSource("/sse/data");
eventSource.onmessage = function(event){
document.getElementById("sse").innerHTML = event.data;
}
</script>
会循环发送请求给后端:

2.2 Spring中的SSE实现
Spring 4.2 开始就已经⽀持SSE,从Spring5开始我们可以使⽤WebFlux更优雅的实现SSE协议.Flux是WebFlux的核⼼API.
快速使⽤
Flux的流程分为三个步骤:
- 创建Flux
创建⼀个Flux数据流,并有数据源. - 处理数据
使⽤操作符对数据进⾏处理 - 订阅数据
订阅Flux来消费数据,触发数据的流动
java
public class FluxDemoTest {
public static void main(String[] args) throws InterruptedException {
//创建 Flux
Flux<String> fruitFlux = Flux.just("Apple", "Banana", "Cherry")
.delayElements(Duration.ofSeconds(1));
//处理数据并订阅数据
fruitFlux.map(String::toUpperCase).subscribe(System.out::println);
//防⽌程序处理完成之前,进程结束
Thread.sleep(4000);
}
}
| 操作 | 作⽤ | ⽰例代码⽚段 |
|---|---|---|
| map() | 元素⼀对⼀转换 | .map(String::toUpperCase) |
| filter() | 条件过滤 | .filter(s -> s.length() > 5) |
| take() | 限制元素数量 | .take(2) //只取前2个元素 |
| merge() | 合并多个Flux(不保证顺序) | Flux.merge(Flux.just("A"), Flux.just("B")) |
| concat() | 顺序拼接多个Flux(保证顺序) | Flux.concat(Flux.just("A"), Flux.just("B")) |
| delayElements | 延迟元素发射 | .delayElements(Duration.ofSeconds(1)) |
三、ChatModel
ChatModel是SpringAI构建对话应⽤的核⼼接⼝,它抽象了应⽤与模型交互的过程,包括使⽤Prompt 作为输⼊,使⽤ ChatResponse 作为输出等.ChatModel的⼯作原理是接收Prompt或部分对话作为输⼊,将输⼊发送给后端⼤模型,模型根据其训练数据和对⾃然语⾔的理解⽣成对话响应,应⽤程序可以将响应呈现给⽤⼾或⽤于进⼀步处理。ChatClient 本质上也是基于ChatModel进⾏的封装和增强。

3.1 实现简单对话
java
package com.spring.ai.controller;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ds")
public class DeepSeekChatController {
@Autowired
private OpenAiChatModel openAiChatModel;
@GetMapping("/callByPrompt")
public String callByPrompt(String message) {
ChatResponse response = openAiChatModel.call(new Prompt(message));
return response.getResult().getOutput().getText();
}
}
测试:http://127.0.0.1:8080/ds/callByPrompt?message=祝大家新年快乐

3.2 ⻆⾊预设
在SpringAI中,ChatModel ⽀持通过Prompt预设⻆⾊,这是引导模型输出特定⻛格或专业内容的
核⼼技术⼿段.
java
@GetMapping(value = "/role")
public String role(String message) {
SystemMessage systemMsg = new SystemMessage(" 你叫小黑子,会鸡叫");
UserMessage userMsg = new UserMessage(message);
Prompt prompt = new Prompt(List.of(systemMsg, userMsg));
ChatResponse response = openAiChatModel.call(prompt);
return response.getResult().getOutput().getText();
}
测试:http://127.0.0.1:8080/ds/role?message=你是谁

3.3 实现流式输出
java
@GetMapping(value = "/callByStream", produces = "text/html;charset=utf-8")
public Flux<String> callByStream(String message) {
Flux<ChatResponse> response = openAiChatModel.stream(new Prompt(message));
return response.map(x->x.getResult().getOutput().getText());
}
测试:http://127.0.0.1:8080/ds/callByStream?message=你是谁

3.4 ChatModel与ChatClient对比
| 维度 | ChatModel | ChatClient |
|---|---|---|
| 交互⽅式 | ⼿动构建Prompt,解析响应 | 链式API,⾃动封装请求与响应 |
| 结构化输出 | ⼿动解析⽂本 | ⽀持 .entity(Class) ⾃动映射POJO |
| 扩展能⼒ | 依赖外部组件 | 内置Advisor机制,提供更⾼级的功能,如提供上下⽂记忆,RAG等功能 |
| 适合场景 | 适合需要精细控制模型参数的场景,⽐如模型实验,参数调优等定制需求 | 适合快速构建AI服务,如带记忆的客服系统 |