一文吃透 Spring AI Alibaba ChatClient:结构化输出、角色消息、对话记忆实战
一、前言
随着大模型应用落地越来越广泛,传统硬编码对接各大 LLM 接口的方式,存在适配复杂、底层逻辑冗余、多模型切换成本高等问题。而 Spring AI Alibaba 基于 Spring 生态打造,提供了统一、流畅的 ChatClient 流式 API,屏蔽了不同大模型的底层通信细节,让 Java 开发者可以像写普通 Spring 业务代码一样快速开发 AI 应用,聚焦业务逻辑而非接口适配。
本文基于 Spring AI Alibaba 核心组件 ChatClient,从基础概念、适用场景出发,结合结构化实体返回、动态消息角色、对话记忆(内存 / Redis 持久化) 三大高频实战场景,附上完整可运行代码,帮助大家快速上手企业级 AI 聊天应用开发。
二、什么是 ChatClient?
1. 核心概念
ChatClient 是 Spring AI 体系中面向大模型对话的核心流式客户端,设计风格对标 Spring 生态的 WebClient,采用链式调用写法。
它统一封装了各类大模型的请求构造、参数传递、结果解析、流式响应等底层能力,抹平不同 LLM 厂商的接口差异。开发者无需关心 HTTP 请求头、请求体拼装、响应解析等底层工作,只需要通过链式 API 组织对话内容,极大提升 LLM 应用的开发效率。
2. 主要适用场景
- 基础对话问答:通用聊天机器人、智能客服、知识问答等场景;
- 结构化数据输出:要求大模型返回固定格式数据(对象、数组、枚举),替代手动解析 JSON;
- 角色定制对话:动态切换 AI 身份、语气、人设,如模仿不同风格回复、专属职业助手;
- 多轮连续对话:需要上下文记忆的聊天场景(聊天机器人、智能陪伴、交互式问答);
- 流式输出:SSE 流式返回回答内容,实现类似 ChatGPT 打字机效果。
三、实战一:大模型结果直接返回实体类(结构化输出)
在实际业务中,我们经常需要大模型按照固定格式返回数据,而非纯文本。Spring AI 的 entity() 方法可以直接将大模型返回内容映射为 Java 实体(Record/POJO),自动完成序列化,彻底告别手动 JSON 解析。
1. 定义接收实体
使用 Java Record 定义结构化返回实体(简洁高效,适合数据载体):
// 接收演员+对应影视作品的结构化实体
public record ActorMovies(String actor, List<String> movies) {
}
2. 接口开发
通过 chatClient.prompt().user().call().entity(实体类.class) 直接绑定返回类型:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/movies")
public class MovieAiController {
private final ChatClient chatClient;
// 构造器注入ChatClient(Spring AI自动装配)
public MovieAiController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@RequestMapping
public ActorMovies movies(@RequestParam(value = "message") String message){
// 链式调用:传入用户提问,直接映射为ActorMovies实体
return this.chatClient.prompt()
.user(message)
.call()
.entity(ActorMovies.class);
}
}
使用说明:调用接口后,大模型会自动按照实体结构返回数据,框架内部完成格式校验与映射,适用于报表、信息提取、数据整理等强结构化场景。
四、实战二:设置消息角色 & 动态参数
大模型的系统角色(System Message) 是控制 AI 行为、语气、身份的核心。ChatClient 支持全局默认角色 + 接口动态角色参数,灵活实现人设切换。
完整控制器代码
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ai")
public class AiController {
private final ChatClient chatClient;
// 全局配置默认系统角色,预留动态参数 {voice}
public AiController(ChatClient.Builder chatClient){
this.chatClient = chatClient
.defaultSystem("你是一个聊天机器人,回答问题请用{voice}的语气回答我")
.build();
}
@RequestMapping
public String chat(
@RequestParam("message") String message,
@RequestParam("voice") String voice
){
return this.chatClient.prompt()
// 动态替换系统角色中的 {voice} 参数
.system(sp -> sp.param("voice", voice))
// 用户提问
.user(message)
// 同步调用,返回纯文本内容
.call()
.content();
}
}
核心要点
defaultSystem():全局预设系统提示词,定义 AI 基础身份,支持占位符{参数名};.system(sp -> sp.param()):接口层动态传递参数,替换占位符,实现运行时切换语气 / 人设;- 支持角色区分:系统角色(AI 人设)、用户角色(用户提问),符合 LLM 标准对话协议。
测试示例:传入 message="讲个笑话"、voice="可爱俏皮",AI 就会以可爱风格回复内容。
五、实战三:实现大模型多轮对话记忆(内存 + Redis 两种方案)
默认情况下,ChatClient 每次请求都是单次独立对话,无法记住上文内容。想要实现多轮连续对话,需要借助 Spring AI 提供的 ChatMemory 对话记忆组件,搭配 MessageChatMemoryAdvisor 实现上下文存储。
主流分为两种方案:
- 内存存储(InMemory):数据存服务内存,重启丢失,适合测试、单机临时对话;
- Redis 存储:数据持久化到 Redis,服务重启不丢失,支持分布式部署,生产环境首选。
前置依赖
使用 Redis 记忆需要引入 Spring AI Alibaba Redis 记忆 依赖,本文基于阿里云 Spring AI 生态组件。
方案 1:基于内存的对话记忆(单机临时会话)
核心组件:
MessageWindowChatMemory:窗口式对话记忆,限制最大消息条数,防止上下文过长;InMemoryChatMemoryRepository:内存仓库;MessageChatMemoryAdvisor:对话记忆增强器,自动拼接历史上下文。
完整代码:
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("memory")
public class MemoryController {
private ChatClient chatClient;
public MemoryController(ChatModel chatModel) {
// 1. 构建内存对话记忆:最大保存100条消息
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(100)
.build();
// 2. 构建记忆增强器
MessageChatMemoryAdvisor messageChatMemoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory)
.build();
// 3. 初始化ChatClient,绑定默认系统角色和记忆增强器
this.chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是一个旅游规划导师,请根据用户的需求提供旅游规划建议。")
.defaultAdvisors(messageChatMemoryAdvisor)
.build();
}
// 流式接口(SSE)返回对话内容
@RequestMapping ("/in-memory")
public Flux<String> memory(
@RequestParam("prompt") String prompt,
@RequestParam("chatId") String chatId,
HttpServletResponse response
) {
response.setCharacterEncoding("UTF-8");
// 传入会话ID,区分不同用户的对话上下文
return chatClient.prompt(prompt)
.advisors(a -> a
.param(ChatMemory.CONVERSATION_ID, chatId) // 会话唯一标识
.param("TOP_K", 50)
)
.stream() // 开启流式输出
.content();
}
}
特点:
- 依靠
chatId区分不同会话,不同用户 / 对话互不干扰; - 数据保存在 JVM 内存,服务重启、集群部署后记忆失效;
- 适合本地测试、演示场景。
方案 2:基于 Redis 的持久化对话记忆(生产推荐)
使用 RedisChatMemoryRepository 将对话历史持久化到 Redis,支持分布式、服务重启数据不丢失,是线上项目标准方案。
加入依赖
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.2.0</version>
</dependency>
完整代码:
import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/redis")
public class MemoryRedisController {
private final ChatClient chatClient;
// 注入Redis记忆仓库 + ChatClient构建器
public MemoryRedisController(ChatClient.Builder builder,
RedisChatMemoryRepository redisChatMemoryRepository ) {
// 1. 构建Redis持久化对话记忆
ChatMemory redisChatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(redisChatMemoryRepository)
.maxMessages(100)
.build();
// 2. 构建记忆增强器
MessageChatMemoryAdvisor redisAdvisor = MessageChatMemoryAdvisor.builder(redisChatMemory).build();
// 3. 初始化ChatClient
this.chatClient = builder.defaultSystem("你是一个旅游规划导师,请根据用户的需求提供旅游规划建议。")
.defaultAdvisors(redisAdvisor)
.build();
}
@RequestMapping("/chat")
public Flux<String> chat(
@RequestParam("prompt") String prompt,
@RequestParam("chatId") String chatId,
HttpServletResponse response
) {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/event-stream;charset=UTF-8");
// 绑定会话ID,读取Redis中历史对话
return chatClient.prompt(prompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId).param("TOP_K", 50))
.stream()
.content();
}
}
RedisChatMemoryRepository需要手动装配,Spring AI Alibaba 扩展组件,并非 Spring AI 原生组件,官方没有提供全局自动配置类。
package org.example.ai_demo.config;
import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host:localhost}")
private String host;
@Value("${spring.data.redis.port:6379}")
private int port;
@Bean
public RedisChatMemoryRepository redisChatMemoryRepository(){
return RedisChatMemoryRepository.builder().host(host).port(port).build();
}
}
特点:
- 对话历史存入 Redis,支持分布式集群、服务重启数据不丢失;
- 依旧通过
chatId隔离不同用户会话; - 搭配
stream()实现 SSE 流式输出,前端可实现打字机效果; - 企业级生产环境首选方案。
两种记忆方案对比
表格
| 存储方案 | 底层仓库 | 优缺点 | 适用场景 |
|---|---|---|---|
| 内存记忆 | InMemoryChatMemoryRepository | 简单、无中间件依赖;重启丢失、不支持集群 | 本地测试、单机演示 |
| Redis 记忆 | RedisChatMemoryRepository | 持久化、支持分布式;依赖 Redis 中间件 | 线上生产、集群项目 |
六、总结
- ChatClient 定位:Spring AI Alibaba 核心对话客户端,流式链式 API,屏蔽 LLM 底层细节,让开发者专注业务;
- 结构化输出:借助
entity()方法,一键将大模型返回映射为 Java 实体,告别手动 JSON 解析; - 动态角色:通过系统提示词 + 动态参数,灵活切换 AI 语气、人设,满足多样化对话需求;
- 对话记忆:依靠
ChatMemory + Advisor实现多轮对话,测试用内存、生产用 Redis 是最佳实践;
Spring AI Alibaba 深度融合 Spring Boot 生态,学习成本低、上手快,以上四大能力基本覆盖了聊天机器人、智能助手、交互式问答等主流 LLM 应用场景。大家可以基于本文代码快速搭建基础 AI 对话服务,再结合 RAG、函数调用等能力进一步拓展业务功能。

所有评论(0)