Spring AI 1.x 系列【5】对话客户端(ChatClient )
ChatClient 是构建在 ChatModel 之上的高层接口,是大语言模型(LLM)交互的统一入口,支持 Tool、RAG、Memory、Advisor 等高级功能。
文章目录
1. 概述
ChatClient 是构建在 ChatModel 之上的高层接口,是大语言模型(LLM)交互的统一入口,支持 Tool、RAG、Memory、Advisor 等高级功能。
核心源码如下:
public interface ChatClient {
// 创建 ChatClient 实例
static ChatClient create(ChatModel chatModel)
static ChatClient create(ChatModel chatModel, ObservationRegistry observationRegistry)
// 构建器模式
static Builder builder(ChatModel chatModel)
// 提示词入口
ChatClientRequestSpec prompt()
ChatClientRequestSpec prompt(String content)
ChatClientRequestSpec prompt(Prompt prompt)
// 创建变异构建器
Builder mutate()
}
DefaultChatClient 是 ChatClient 接口的标准默认实现:
public class DefaultChatClient implements ChatClient {
private static final ChatClientObservationConvention DEFAULT_CHAT_CLIENT_OBSERVATION_CONVENTION = new DefaultChatClientObservationConvention();
private static final TemplateRenderer DEFAULT_TEMPLATE_RENDERER = StTemplateRenderer.builder().build();
private static final ChatClientMessageAggregator CHAT_CLIENT_MESSAGE_AGGREGATOR = new ChatClientMessageAggregator();
private final DefaultChatClientRequestSpec defaultChatClientRequest;
public DefaultChatClient(DefaultChatClientRequestSpec defaultChatClientRequest) {
Assert.notNull(defaultChatClientRequest, "defaultChatClientRequest cannot be null");
this.defaultChatClientRequest = defaultChatClientRequest;
}
public ChatClient.ChatClientRequestSpec prompt() {
return new DefaultChatClientRequestSpec(this.defaultChatClientRequest);
}
// ......
}
2. 基础用法
2.1 创建对话客户端
支持使用 ChatClient.Builder 、ChatClient.create() 方法手动创建:
// 1. 创建 ChatModel
OpenAiChatModel chatModel = new OpenAiChatModel(
OpenAiChatOptions.builder()
.apiKey("your-api-key")
.model("gpt-4")
.temperature(0.7)
.build()
);
// 2. 创建 ChatClient
ChatClient chatClient = ChatClient.create(chatModel);
// 3. 或使用建造者
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是一个翻译助手")
.build();
Spring AI 也提供了 Spring Boot 自动配置的 ChatClient.Builder 实例,可以直接注入到业务类中调用其 build() 方法创建:
@RestController
class MyController {
private final ChatClient chatClient;
public MyController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
}
在 ChatClientAutoConfiguration 自动配置类中,可以找到该 Bean :
提示:但是这里有个问题,所依赖的 ChatModel 如果存在多个类型实例,就会报错 required a single bean, but 3 were found ,所以仅仅适用于只有一个 ChatModel 实例的场景。
2.2 默认配置项
ChatClient.Builder 提供了一系列链式方法来配置 ChatClient 的各种默认行为和参数:
defaultAdvisors:默认增强器,可用于请求拦截、参数校验、日志记录等增强逻辑。defaultOptions:默认对话配置,比如请求超时时间、使用的AI模型、请求重试策略等。defaultUser:默认的用户输入内容,支持直接传字符串、从文件 / 资源读取,或通过配置规范自定义。defaultSystem:默认的系统提示词。defaultTemplateRenderer:配置的模板渲染器。defaultTools**:用于配置聊天客户端可调用的工具、工具回调、工具上下文等。
示例代码:
ChatClient chatClient = ChatClient.builder()
// 配置系统提示
.defaultSystem("你是一个Java编程助手,回答简洁明了")
// 配置默认用户提示
.defaultUser("解释建造者模式的使用场景")
// 配置聊天选项(如超时、模型)
.defaultOptions(ChatOptions.builder().timeout(30000).model("gpt-3.5-turbo").build())
// 配置工具
.defaultToolNames("code_checker")
// 构建最终实例
.build();
2.3 Fluent API
ChatClient 采用 Fluent API 设计,提供了一种链式调用、表达力强的编程方式。
核心调用链结构如下:
| 阶段 | 方法 | 作用 |
|---|---|---|
| 入口阶段 | ChatClient |
获取 ChatClient 实例 |
| 构建请求阶段 | .prompt() |
开始调用链,构建提示词 |
| 配置阶段 | .options() |
配置模型参数 |
| 执行阶段 | .call()/ .stream() |
执行同步或异步流式调用 |
| 结果处理阶段 | .content() |
获取文本内容或完整结果对象 |
示例代码:
// 完整调用链示例
String result = chatClient // ← 入口
.prompt("解释Spring Bean的作用域") // ← 开始构建请求 ← 添加消息
.options(ChatOptions.builder() // ← 配置模型参数
.temperature(0.3)
.maxTokens(500)
.build())
.call() // ← 执行同步调用
.content(); // ← 获取文本结果
// 流式调用示例
Flux<String> stream = chatClient
.prompt()
.user("写一首关于春天的诗")
.stream() // ← 执行流式调用
.content(); // ← 获取流式结果
3. 多模型协作
在实际开发场景,在同一个 Spring AI 应用中,一般都会对接多种模型,主要因为:
- 专业化分工:不同模型做擅长的事,比如代码生成用
Anthropic,创意文案用GPT-3.5。 - 用户偏好选择:让用户自己选想用的模型,提升体验。
A/B测试:同时调用不同模型、不同配置,对比响应效果(比如回答质量、速度),选出最优方案。- 服务容灾备份:某一个模型服务不可用时,自动切换到备用模型,避免应用直接挂掉。
在上面有提到,自动配置的 ChatClient.Builder 在多个 ChatModel 时会有问题,必须先禁用这个自动配置(否则会出现 Bean 冲突):
spring:
ai:
chat:
client:
enabled: false
此外,还需要删除 ChatClient.Builder 相关调用代码。
3.1 单一模型,多个不同配置
底层用的是同一个类型的模型,但给不同的 ChatClient 配置不同的默认参数(比如系统提示、温度等)。
首先配置上 DeepSeek:
spring:
application:
name: spring-ai-01
ai:
chat:
client:
enabled: false
deepseek:
# api-key: ${AI_DASHSCOPE_API_KEY}
api-key: sk-070aa594deee454389d5ec912398bacb
base-url: https://api.deepseek.com
配置两个不同的系统提示词的 ChatClient :
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient deepSeekChatClient001(DeepSeekChatModel deepSeekChatModel) {
return ChatClient.builder(deepSeekChatModel)
.defaultSystem("你是一个严谨的事实问答助手,只回答客观事实,不编造内容")
.build();
}
@Bean
public ChatClient deepSeekChatClient002(DeepSeekChatModel deepSeekChatModel) {
return ChatClient.builder(deepSeekChatModel)
.defaultSystem("你是一个富有创意的文案助手,擅长生成有趣、新颖的内容")
.build();
}
}
编写测试类:
@SpringBootTest
class DeepSeekTest01Tests {
@Autowired
ChatClient deepSeekChatClient001;
@Autowired
ChatClient deepSeekChatClient002;
@Test
public void deepSeekTest05() {
String text1 = deepSeekChatClient001.prompt("你是谁?").call().content();
System.out.println(text1);
String text2 = deepSeekChatClient002.prompt("你是谁?").call().content();
System.out.println(text2);
}
}
可以看到,同一个问题生成了不同的结果返回:
提示:可以使用 @Qualifier 注解区分,使用时按需注入。
3.2 不同模型
配置 OpenAI、DeepSeek:
spring:
application:
name: spring-ai-01
ai:
chat:
client:
enabled: false
deepseek:
# api-key: ${AI_DASHSCOPE_API_KEY}
api-key: sk-070aa594deee454389d5ec912398bacb
base-url: https://api.deepseek.com
openai:
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
api-key: sk-2001b9ae760b4be1944d52c5457f078a
为不同类型的模型分别创建 ChatClient :
public class ChatClientConfig {
// 1. 创建 OpenAI 对应的 ChatClient Bean,名称为 openAiChatClient
@Bean
public ChatClient openAiChatClient(OpenAiChatModel openAichatModel) {
return ChatClient.builder(openAichatModel)
.build();
}
// 2. 创建 DeepSeek 对应的 ChatClient Bean,名称为 deepSeekChatClient
public ChatClient deepSeekChatClient(DeepSeekChatModel deepSeekChatModel) {
return ChatClient.builder(deepSeekChatModel)
.build();
}
}
3.3 多个兼容 OpenAI 的模型
很多平台(如 Groq、Azure OpenAI)都兼容 OpenAI 的 API 格式,因此可以复用 OpenAiApi/OpenAiChatModel 类,通过 mutate() 方法创建不同配置的实例。适用于需要使用多个兼容 OpenAI 的 API 场景。
官方示例:
@Service
public class MultiModelService {
private static final Logger logger = LoggerFactory.getLogger(MultiModelService.class);
@Autowired
private OpenAiChatModel baseChatModel;
@Autowired
private OpenAiApi baseOpenAiApi;
public void multiClientFlow() {
try {
// Derive a new OpenAiApi for Groq (Llama3)
OpenAiApi groqApi = baseOpenAiApi.mutate()
.baseUrl("https://api.groq.com/openai")
.apiKey(System.getenv("GROQ_API_KEY"))
.build();
// Derive a new OpenAiApi for OpenAI GPT-4
OpenAiApi gpt4Api = baseOpenAiApi.mutate()
.baseUrl("https://api.openai.com")
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
// Derive a new OpenAiChatModel for Groq
OpenAiChatModel groqModel = baseChatModel.mutate()
.openAiApi(groqApi)
.defaultOptions(OpenAiChatOptions.builder().model("llama3-70b-8192").temperature(0.5).build())
.build();
// Derive a new OpenAiChatModel for GPT-4
OpenAiChatModel gpt4Model = baseChatModel.mutate()
.openAiApi(gpt4Api)
.defaultOptions(OpenAiChatOptions.builder().model("gpt-4").temperature(0.7).build())
.build();
// Simple prompt for both models
String prompt = "What is the capital of France?";
String groqResponse = ChatClient.builder(groqModel).build().prompt(prompt).call().content();
String gpt4Response = ChatClient.builder(gpt4Model).build().prompt(prompt).call().content();
logger.info("Groq (Llama3) response: {}", groqResponse);
logger.info("OpenAI GPT-4 response: {}", gpt4Response);
}
catch (Exception e) {
logger.error("Error in multi-client flow", e);
}
}
}
更多推荐

所有评论(0)