第三章 ChatModel 与 ChatClient 的深度对比
s01 > s02 > [ s03 ] s04 > s05 > s06 > s07 > s08 > s09 > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > s18
"底层看 ChatModel, 开发多用 ChatClient" -- 一个偏原始能力, 一个偏工程效率。
一、为什么需要两个 API?
1.1 初学者的困惑
很多初学者看到 Spring AI 同时提供了 ChatModel 和 ChatClient 都会问:
- "这两个不都是用来调用 AI 的吗?"
- "到底该用哪一个?"
- "他们有什么区别?"
这一章我们就来彻底搞清楚这个问题!
1.2 比喻理解
让我用一个生活化的比喻来解释:
ChatModel(对话模型) 就相当于手机的「原始电话功能」
- 只能打电话(调用 send/receive)
- 功能简单直接
- 需要自己处理各种细节
ChatClient(对话客户端) 相当于「智能手机」
- 在电话基础上,增加了:
- 通讯录管理(系统提示词)
- 短信应用(Prompt 模板)
- 语音助手(工具调用)
- 智能外设(向量存储、记忆功能)
- 功能更丰富,使用更便捷
二、ChatModel 详解
2.1 什么是 ChatModel?
ChatModel 是 Spring AI 的核心底层接口,它直接封装了与 AI 模型的通信。
大白话解释:把 ChatModel 想象成"AI 模型的翻译官",你给它发中文,它能听懂;AI 返回的内容,它也能翻译成 Java 对象给你。
public interface ChatModel extends Model, ChatOptionsDesktop {
// 核心方法就两个:
// 方法1:一次性返回完整结果
ChatResponse call(Prompt prompt);
// 方法2:流式返回(一个字一个字吐出来)
Flux<ChatResponse> stream(Prompt prompt);
}
2.2 ChatModel 的特点
| 特点 | 说明 |
|---|---|
| 底层接口 | 直接与 AI 服务商通信 |
| 功能单一 | 只负责发送消息、接收回复 |
| 灵活度高 | 可以精确控制请求的每个细节 |
| 使用难度 | 需要了解 Prompt、Message 等概念 |
2.3 代码示例
// 使用 ChatModel 调用 AI
@RestController
public class ChatModelController
{
@Resource
private ChatModel chatModel;
@GetMapping("/chatmodel/hello")
public String hello(String msg)
{
// ChatModel 的调用方式:直接传入字符串
String result = chatModel.call(msg);
return result;
}
}
三、ChatClient 详解
3.1 什么是 ChatClient?
ChatClient 是构建在 ChatModel 之上的高级 API,它提供了链式调用(Builder模式)的便捷写法。
大白话解释:ChatClient 就像一个"智能点餐机",不仅能帮你叫外卖,还支持:
- 定义口味偏好(系统提示词)
- 选择优惠券(模板变量)
- 凑单推荐(RAG 检索)
- 会员积分(对话记忆)
3.2 ChatClient 的链式调用
// ChatClient 的经典使用方式:链式调用
String result = chatClient.prompt() // 第一步:创建提示词
.system("你是一个Java助手") // 第二步:设置系统提示词
.user("什么是反射?") // 第三步:设置用户问题
.call() // 第四步:执行调用
.content(); // 第五步:获取结果
这种链式写法比原生 ChatModel 更加直观易读。
3.3 代码示例
// 使用 ChatClient 调用 AI
@RestController
public class ChatClientController
{
// 注意:ChatClient 不能直接注入,需要通过 Builder 构建
private final ChatClient dashScopeChatClient;
// 构造函数注入,并构建 ChatClient
public ChatClientController(ChatModel dashScopeChatModel)
{
// 通过 ChatModel 创建 ChatClient
this.dashScopeChatClient = ChatClient.builder(dashScopeChatModel).build();
}
@GetMapping("/chatclient/hello")
public String hello(String msg)
{
// 链式调用,语义清晰
String result = dashScopeChatClient.prompt()
.user(msg) // 设置用户消息
.call() // 调用 AI
.content(); // 获取文本内容
return result;
}
}
四、两者对比
4.1 功能对比表
| 特性 | ChatModel | ChatClient |
|---|---|---|
| 层级 | 底层接口 | 高级封装 |
| API 风格 | 直接调用 | 链式 Builder |
| 系统提示词 | 需手动构造 Prompt 对象 | .system() 方法直接设置 |
| 模板变量 | 不支持 | 支持 .param() 替换占位符 |
| 结构化输出 | 需额外配置 | .entity() 方法直接映射 |
| 对话记忆 | 不支持 | 支持 Advisor 扩展 |
| 工具调用 | 需手动配置 | 支持 .tools() 方法 |
| 适用场景 | 需要精确控制的底层开发 | 快速构建 AI 功能 |
4.2 如何选择?
使用 ChatModel 的场景:
- 你需要精确控制请求的每个参数
- 你是库/框架开发者,需要底层能力
- 你需要实现自定义的 AI 逻辑
使用 ChatClient 的场景:
- 日常的 AI 对话开发(推荐)
- 需要快速构建原型
- 需要用到系统提示词、模板、工具调用等高级功能
4.3 ChatModel 也能实现 ChatClient 的功能(但代码更多)
很多初学者误以为 ChatModel 功能太简单,做不到 ChatClient 的那些高级功能。其实 ChatModel 通过手写样板代码完全可以实现相同的功能,只不过代码量会多一些。
让我们对比一下两者的实现方式:
场景:让 AI 扮演 Java 助手,回答技术问题
ChatClient 的写法(简洁):
String result = chatClient.prompt()
.system("你是一个专业的Java工程师") // 系统提示词
.user("什么是反射?") // 用户问题
.call()
.content();
ChatModel 的写法(样板代码多):
// ChatModel 需要手动构造 Prompt 对象
// 第一步:创建系统消息
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(
"你是一个专业的%s", "Java工程师" // 模板变量
);
Message systemMessage = systemPromptTemplate.createMessage(
Map.of("role", "professional", "specialty", "Java工程师")
);
// 第二步:创建用户消息
UserMessage userMessage = new UserMessage("什么是反射?");
// 第三步:组合成对话消息列表
List<Message> messages = new ArrayList<>();
messages.add(systemMessage);
messages.add(userMessage);
// 第四步:创建 Prompt 对象
Prompt prompt = new Prompt(messages);
// 第五步:调用并获取结果
ChatResponse response = chatModel.call(prompt);
String result = response.getResult().getOutput().getText();
从上面的对比可以看出:
- ChatClient:3 行代码搞定(链式调用)
- ChatModel:需要 10+ 行样板代码(手动构造)
那 ChatModel 还有什么用?
虽然 ChatModel 代码多,但它给了你最大的控制权:
- 可以自定义消息类型
- 可以精确控制每个参数
- 可以在发送前对消息进行任意处理
💡 总结:ChatModel 就像 "手动挡汽车",功能全但需要自己换挡;ChatClient 像 "自动挡汽车",开起来简单但牺牲了一些灵活性。
💡 初学者建议:90% 的情况下,使用
ChatClient就够了。除非你有特殊需求,否则不需要直接使用ChatModel。
五、项目代码详解
5.1 项目结构
SAA-03ChatModelChatClient/
├── pom.xml # 依赖配置
├── src/main/java/com/atguigu/study/
│ ├── config/
│ │ └── SaaLLMConfig.java # 配置文件
│ ├── controller/
│ │ ├── ChatClientController.java # ChatClient 示例
│ │ ├── ChatClientControllerV2.java # ChatClient V2写法
│ │ └── ChatModelController.java # ChatModel 示例
│ └── Saa03ChatModelChatClientApplication.java
5.2 配置类
package com.atguigu.study.config;
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;
/**
* 配置类:注册多个 ChatModel 和 ChatClient 的 Bean
*/
@Configuration
public class SaaLLMConfig
{
/**
* 创建一个命名的 ChatModel(阿里云 DashScope)
* @Bean 注解会将方法返回值注册为 Spring 容器中的 Bean
* 默认名称是方法名,这里我们显式命名为 "dashScopeChatModel"
*/
@Bean("dashScopeChatModel")
public ChatModel dashScopeChatModel()
{
// 这里不需要手动创建,Spring AI 的自动配置会处理
// 只是为了演示多Bean的情况,保留这个方法作为标记
return null; // 实际由 Spring AI 自动配置注入
}
/**
* 创建一个命名的 ChatClient(基于 dashScopeChatModel)
* 名称为 "dashScopeChatClient"
*/
@Bean("dashScopeChatClient")
public ChatClient dashScopeChatClient(ChatModel dashScopeChatModel)
{
// 通过 Builder 模式创建 ChatClient
return ChatClient.builder(dashScopeChatModel).build();
}
}
💡 提示:实际上,这些配置大部分由 Spring AI 的自动配置(Auto Configuration)完成了,我们只需要了解原理即可。
5.3 ChatClientController V2 写法(推荐)
package com.atguigu.study.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 使用 @Autowired 注入 ChatClient
*/
@RestController
public class ChatClientControllerV2
{
// 直接注入已注册的 ChatClient Bean
@Autowired
private ChatClient chatClient;
@GetMapping("/chatclientv2/dochat")
public String doChat(@RequestParam(name = "msg", defaultValue = "2加9等于几") String msg)
{
// 简洁的链式调用
return chatClient.prompt()
.user(msg) // 设置用户消息
.call() // 调用
.content(); // 获取文本内容
}
}
六、进阶:ChatClient 的多种用法
6.1 返回 Flux 流式响应
@GetMapping("/stream")
public Flux<String> stream(String msg)
{
return chatClient.prompt()
.user(msg)
.stream() // 开启流式模式
.content();
}
6.2 方法引用写法
// 更加简洁的 Lambda 写法
@GetMapping("/chat")
public String chat(String msg)
{
return chatClient.prompt(msg).call().content();
}
// 或者更复杂点
return chatClient.prompt()
.system("你是一个专业的%s", "Java工程师") // 格式化
.user(msg)
.call()
.chatResponse() → 获取 AI 返回来的【完整响应对象】(头+体+脚)
.getResult() → 拿到【结果体】
.getOutput() → 拿到【输出内容】
.getText() → 拿到【文本字符串】
七、本章小结
7.1 核心知识点
| 概念 | 说明 |
|---|---|
| ChatModel | 底层AI调用接口,直接与模型通信 |
| ChatClient | 高级封装,提供链式调用的便捷API |
| @Bean | Spring 中用于注册组件的注解 |
| 链式调用 | 通过 Builder 模式实现的方法链 |
7.2 选择建议
┌─────────────────────────────────────────────────┐
│ 选择决策树 │
│ │
│ 需要精细控制底层请求? ──YES──→ 使用 ChatModel │
│ │ │
│ NO │
│ ↓ │
│ 直接开发AI应用功能? ──YES──→ 使用 ChatClient │
│ │
└─────────────────────────────────────────────────┘
本章重点:
- 理解 ChatModel 与 ChatClient 的定位差异
- 掌握 ChatClient 的链式调用写法
- 能够根据场景选择合适的 API
下章剧透(s04):
了解了两种 API 的使用后,下一章我们将学习流式输出的高级用法,体验"打字机"效果的实现原理。
📝 编辑者:Flittly
📅 更新时间:2026年3月
🔗 相关资源:Spring AI ChatClient 官方文档