
系列栏目: Spring AI
Spring AI 实战系列(二):ChatClient封装,告别大模型开发样板代码
Spring AI 实战系列(三):多模型共存+双版本流式输出
Spring AI 实战系列(四):Prompt工程深度实战
在本系列的第一篇教程中,我们完成了 Spring AI与阿里云百炼大模型的基础集成:
- 基于
spring-ai-alibaba-starter-dashscope官方 Starter 完成了项目工程化搭建- 通过
ChatModel原子API实现了基础的同步对话与流式响应接口- 完成了API Key的环境变量安全注入,避免了密钥硬编码的安全风险
但在实际业务开发中我们很快会发现:**
ChatModel**虽然提供了极致的底层灵活性,但面对复杂的业务场景时,需要我们手动组装提示词、处理消息结构、解析模型输出、串联对话记忆 / 向量检索等组件,会产生大量重复的样板代码,开发效率低且不易维护。
本篇我们就来学习 Spring AI 提供的Fluent流式 API ------ ChatClient,它完美解决了上述痛点,在不丢失灵活性的前提下,将大模型交互的开发效率提升一个量级。
一、ChatClient 核心定位:从原子 API 到工程化封装
1.1 什么是 ChatClient
**ChatClient**是 Spring AI提供的高层级Fluent API,它底层基于ChatModel原子能力,将大模型交互全流程的组件协调、样板代码全部封装,为开发者提供了声明式、链式的开发体验。
如果把ChatModel比作JDBC的原生Connection,需要自己处理 SQL 拼接、结果集解析;那**ChatClient**就是 MyBatis/MyBatis-Plus,把底层繁琐的操作全部收敛,让开发者只需要关注核心业务逻辑。
1.2 ChatClient 核心优势
| 能力维度 | ChatModel 原子 API | ChatClient Fluent API |
|---|---|---|
| 代码量 | 复杂场景需要大量样板代码,手动组装 Message、处理 Prompt | 链式调用一行完成全流程,无冗余代码 |
| 提示词管理 | 每次调用手动拼接系统提示、用户提示,无统一管理能力 | 支持全局默认提示词、动态参数模板,一次配置全局生效 |
| 输出解析 | 需手动处理字符串转 JSON、实体映射、格式校验 | 内置结构化输出能力,一键映射为 Java 实体 / 集合,自动约束模型输出格式 |
| 高级能力 | 对话记忆、RAG 检索、函数调用需手动串联组件 | 内置 Advisor 切面机制,一行配置开启记忆、RAG、日志、限流等能力 |
| 编程模型 | 同步 / 流式能力分离,API 不统一 | 同步call()、流式stream()统一 API,无缝切换 |
1.3 核心能力全景
ChatClient 不仅封装了基础对话能力,还覆盖了企业级大模型应用的全场景需求:
- 基础能力:提示词定制、模型参数调优、同步 / 流式响应
- 结构化输出:自动将模型返回映射为 Java 实体类、泛型集合
- 全局默认配置:统一系统提示词、模型参数、函数定义,一次配置全局生效
- 高级扩展:通过 Advisor 机制,一键集成聊天记忆、RAG 检索、日志监控、函数调用等能力
二、实战落地:基于阿里云百炼的 ChatClient 集成
本次实战完全承接上一篇的项目工程,无需从零搭建,所有代码可直接复制运行。
2.1 环境前提
- 已完成 JDK 17+、Spring Boot 3.2.x 环境搭建
- 已配置阿里云百炼API Key环境变量
DASHSCOPE_API_KEY - 已在
pom.xml中引入spring-ai-alibaba-starter-dashscope核心依赖
2.2 第一步:ChatClient 全局 Bean 配置
Spring AI不会自动装配ChatClient实例,只会自动装配ChatClient.Builder构建器,我们需要在配置类中手动创建ChatClient的 Bean 并注入 Spring 容器。
修改我们的配置类LLMConfig.java,完整代码如下:
java
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Spring AI 大模型配置类
*/
@Configuration
public class SaaLLMConfig {
/**
* 阿里云百炼API实例,负责与百炼服务通信
* 从系统环境变量读取API Key,避免硬编码泄露
*/
@Bean
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.build();
}
/**
* 全局ChatClient实例,基于阿里云百炼ChatModel构建
*/
@Bean
public ChatClient dashScopeChatClient(ChatModel dashscopeChatModel) {
return ChatClient
// 基于已自动装配的ChatModel构建Builder
.builder(dashscopeChatModel)
// 可选:全局默认系统提示词,所有调用都会自动携带
.defaultSystem("你是一个专业的Java后端开发工程师,擅长Spring生态技术栈,回答问题简洁、专业、有可落地的代码示例")
// 可选:全局默认模型参数,统一管理,避免每次调用重复配置
.defaultOptions(DashScopeChatOptions.builder()
.withModel("qwen-turbo")
.withTemperature(0.7)
.withMaxTokens(2000)
.build())
// 构建ChatClient实例
.build();
}
}
关键说明:
- 这里的
ChatModel会由spring-ai-alibaba自动装配,无需我们手动创建 - 通过
defaultSystem设置全局系统提示词,统一模型的角色定位,所有调用都会自动携带 - 通过
defaultOptions设置全局模型参数,统一管理模型版本、温度、最大 Token 数,避免硬编码分散在业务代码中
2.3 第二步:基础接口开发,对比 ChatModel 与 ChatClient
我们创建ChatClientController,同时提供ChatClient和ChatModel的调用接口,直观对比两者的开发体验。
java
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* ChatClient 实战接口
*/
@RestController
public class ChatClientController {
// 注入原生ChatModel,用于对比
@Resource
private ChatModel chatModel;
// 注入我们配置的ChatClient实例
@Resource
private ChatClient dashScopeChatClient;
/**
* ChatClient 同步对话接口
*/
@GetMapping("/chatclient/dochat")
public String doChat(@RequestParam(name = "msg", defaultValue = "你是谁") String msg) {
// ChatClient 链式调用,一行完成请求组装、调用、结果提取
String result = dashScopeChatClient
.prompt() // 创建提示词构建器
.user(msg) // 设置用户输入
.call() // 同步调用大模型
.content(); // 提取响应的文本内容
System.out.println("ChatClient响应:" + result);
return result;
}
/**
* ChatModel 同步对话接口(对比用)
*/
@GetMapping("/chatmodelv2/dochat")
public String doChat2(@RequestParam(name = "msg", defaultValue = "你是谁") String msg) {
// ChatModel 原生调用,简单场景下一行完成,但复杂场景需要大量样板代码
String result = chatModel.call(msg);
System.out.println("ChatModel响应:" + result);
return result;
}
}
接口测试 :启动项目后,访问http://localhost:8003/chatclient/dochat?msg=用Spring Boot写一个Hello World接口,可以看到ChatClient会自动携带我们配置的全局系统提示词,以 Java 工程师的专业视角返回结果,代码简洁且行为统一。
三、实践注意
-
全局配置统一管理将系统提示词、模型参数、重试策略等全部在 ChatClient Bean 中统一配置,避免硬编码分散在业务代码中,便于后续维护与迭代。
-
多模型实例隔离 若项目同时对接多个大模型(如阿里云百炼 + 本地 Ollama),需为每个模型创建独立的 ChatClient Bean,通过
@Qualifier注解区分注入,避免 Bean 冲突。 -
日志与监控集成 通过
SimpleLoggerAdvisor开启请求与响应的日志打印,同时通过 Advisor 切面统一统计 Token 消耗、请求耗时,对接 Prometheus 等监控系统。 -
异常统一处理封装 ChatClient 调用的通用异常处理,针对大模型调用超时、限流、API 密钥错误、输出解析失败等场景做统一的降级与容错处理。
-
提示词模板外部化 复杂的系统提示词不要硬编码在 Java 代码中,放到
application.yml配置文件或独立的资源文件中,通过@Value或Resource注入,便于产品与运营同学修改优化。
四、避坑指南
-
坑点 1:ChatClient 无法直接 @Resource 注入 Spring AI 不会自动装配 ChatClient 的 Bean,只会自动装配
ChatClient.Builder,必须在配置类中通过 Builder 手动构建 ChatClient Bean,否则会出现注入失败的异常。 -
坑点 3:多模型场景下的 Bean 注入歧义 若项目中存在多个 ChatModel 实例,构建 ChatClient 时必须通过
@Qualifier明确指定注入的ChatModel,否则 Spring会抛出NoUniqueBeanDefinitionException异常。 -
坑点 4:环境变量 API Key 读取失败
System.getenv()读取的是系统环境变量,IDE 本地运行时,需要在启动配置的 Environment variables 中添加DASHSCOPE_API_KEY,否则会出现 API Key 为空的错误。
下篇预告
本篇我们掌握了ChatClient的核心用法,实现了比ChatModel更优雅、更易维护的大模型交互代码。
下篇我们将深入Spring AI实战核心能力:一套代码实现多个大模型无缝共存与动态切换,同时带来 ChatModel与ChatClient双版本流式输出完整实现,解决长文本生成的用户体验痛点。
如果本文对你有帮助,欢迎点赞、收藏、评论,跟着系列教程一步步完成Spring AI应用。