ChatClient vs ChatModel 详细对比
一、基本概念对比
1.1 ChatModel(基础模型接口)
定位:Spring AI的核心接口,提供基础的AI对话功能
public interface ChatModel {
// 基础对话方法
String call(String message);
// 流式对话方法
Flux<String> stream(String message);
// 高级对话方法
ChatResponse call(Prompt prompt);
Flux<ChatResponse> stream(Prompt prompt);
}
1.2 ChatClient(高级客户端)
定位:基于ChatModel的高级封装,提供Fluent API和更多功能
public interface ChatClient {
// 创建提示构建器
PromptUserSpec prompt();
// 直接调用
String call();
Flux<String> stream();
// 设置默认选项
ChatClient.Builder defaultOptions(ChatOptions options);
}
二、API风格对比
2.1 ChatModel:简单函数式风格
@RestController
public class ChatModelController {
@Resource
private ChatModel chatModel;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
// 简单直接,一行代码
return chatModel.call(msg);
}
@GetMapping("/stream")
public Flux<String> stream(@RequestParam String msg) {
// 流式响应
return chatModel.stream(msg);
}
}
2.2 ChatClient:Fluent API风格
@RestController
public class ChatClientController {
@Resource
private ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
// 链式调用,语义清晰
return chatClient
.prompt()
.user(msg)
.call()
.content();
}
@GetMapping("/stream")
public Flux<String> stream(@RequestParam String msg) {
// 流式响应
return chatClient
.prompt()
.user(msg)
.stream()
.content();
}
}
三、功能对比表
| 功能特性 | ChatModel | ChatClient |
|---|---|---|
| 简单对话 | ✅ 支持 | ✅ 支持 |
| 流式响应 | ✅ 支持 | ✅ 支持 |
| 系统提示 | ⚠️ 需要手动构建Prompt | ✅ .system() |
| 多轮对话 | ⚠️ 需要手动构建Prompt | ✅ .messages() |
| 函数调用 | ❌ 不支持 | ✅ .functions() |
| 多模态 | ❌ 不支持 | ✅ 支持图片等 |
| Fluent API | ❌ 不支持 | ✅ 支持 |
| 自动注入 | ✅ 支持 | ❌ 需要手动创建Bean |
| 代码可读性 | ⚠️ 一般 | ✅ 优秀 |
四、详细功能对比
4.1 系统提示(System Prompt)
ChatModel方式(复杂)
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
// 需要手动构建Prompt
Prompt prompt = new Prompt(
List.of(
new SystemMessage("你是一个专业的Java程序员助手"),
new UserMessage(msg)
)
);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
ChatClient方式(简洁)
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
// 链式调用,清晰明了
return chatClient
.prompt()
.system("你是一个专业的Java程序员助手")
.user(msg)
.call()
.content();
}
4.2 多轮对话
ChatModel方式(复杂)
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
// 需要手动维护对话历史
List<Message> messages = new ArrayList<>();
messages.add(new SystemMessage("你是一个助手"));
messages.add(new UserMessage("你好"));
messages.add(new AssistantMessage("你好!有什么我可以帮助你的?"));
messages.add(new UserMessage(msg));
Prompt prompt = new Prompt(messages);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
ChatClient方式(简洁)
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
// 链式调用,易于理解
return chatClient
.prompt()
.system("你是一个助手")
.user("你好")
.advisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.user(msg)
.call()
.content();
}
4.3 函数调用
ChatModel方式(不支持)
// ChatModel不支持函数调用
// 需要手动处理
ChatClient方式(支持)
@GetMapping("/weather")
public String getWeather(@RequestParam String city) {
return chatClient
.prompt()
.user("查询" + city + "的天气")
.functions("getCurrentWeather") // 函数调用
.call()
.content();
}
4.4 多模态(图片等)
ChatModel方式(不支持)
// ChatModel不支持多模态
ChatClient方式(支持)
@GetMapping("/describe")
public String describeImage(@RequestParam String imageUrl) {
return chatClient
.prompt()
.user(userSpec -> userSpec
.text("描述这张图片")
.media(MimeTypeUtils.IMAGE_JPEG, imageUrl)
)
.call()
.content();
}
五、依赖注入对比
5.1 ChatModel:支持自动注入
@RestController
public class ChatModelController {
// ✅ 直接注入,无需额外配置
@Resource
private ChatModel chatModel;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatModel.call(msg);
}
}
5.2 ChatClient:需要手动创建Bean
配置类
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel).build();
}
}
控制器
@RestController
public class ChatClientController {
// ✅ 通过配置Bean注入
@Resource
private ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient.prompt().user(msg).call().content();
}
}
或者构造器中创建
@RestController
public class ChatClientController {
private final ChatClient chatClient;
// ✅ 在构造器中创建
public ChatClientController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel).build();
}
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient.prompt().user(msg).call().content();
}
}
六、高级功能对比
6.1 设置默认选项
ChatModel方式
// ChatModel没有内置的默认选项设置
// 每次调用都需要传递选项
ChatOptions options = ChatOptions.builder()
.model("qwen-plus")
.temperature(0.7)
.build();
String response = chatModel.call(new Prompt(msg, options));
ChatClient方式
// 配置类中设置默认选项
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultOptions(ChatOptions.builder()
.model("qwen-plus")
.temperature(0.7)
.build())
.build();
}
// 使用时无需重复设置
String response = chatClient
.prompt()
.user(msg)
.call()
.content();
6.2 Advisors(顾问模式)
ChatModel方式
// ChatModel不支持Advisors
ChatClient方式
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient
.prompt()
.user(msg)
.advisors(
new MessageChatMemoryAdvisor(new InMemoryChatMemory()), // 记忆
new QuestionAnswerAdvisor(new VectorStore()) // RAG
)
.call()
.content();
}
七、代码复杂度对比
7.1 简单对话场景
ChatModel(更简单)
// 1行代码
String result = chatModel.call(msg);
ChatClient(稍复杂)
// 3行代码
String result = chatClient
.prompt()
.user(msg)
.call()
.content();
7.2 复杂对话场景
ChatModel(复杂)
// 需要手动构建Prompt
List<Message> messages = new ArrayList<>();
messages.add(new SystemMessage("你是一个助手"));
messages.add(new UserMessage(msg));
ChatOptions options = ChatOptions.builder()
.model("qwen-plus")
.temperature(0.7)
.build();
Prompt prompt = new Prompt(messages, options);
String result = chatModel.call(prompt).getResult().getOutput().getContent();
ChatClient(清晰)
// 链式调用,语义清晰
String result = chatClient
.prompt()
.system("你是一个助手")
.user(msg)
.options(ChatOptions.builder()
.model("qwen-plus")
.temperature(0.7)
.build())
.call()
.content();
八、性能对比
8.1 性能差异
| 方面 | ChatModel | ChatClient |
|---|---|---|
| 调用开销 | 低(直接调用) | 略高(多一层封装) |
| 内存占用 | 低 | 略高 |
| 性能差异 | 基准 | <1%的差异(可忽略) |
8.2 性能测试示例
@SpringBootTest
public class PerformanceTest {
@Resource
private ChatModel chatModel;
@Resource
private ChatClient chatClient;
@Test
public void testChatModelPerformance() {
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
chatModel.call("你好");
}
long end = System.currentTimeMillis();
System.out.println("ChatModel: " + (end - start) + "ms");
}
@Test
public void testChatClientPerformance() {
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
chatClient.prompt().user("你好").call().content();
}
long end = System.currentTimeMillis();
System.out.println("ChatClient: " + (end - start) + "ms");
}
}
结论:性能差异极小(<1%),可以忽略不计。
九、使用场景建议
9.1 使用ChatModel的场景
✅ 适合使用ChatModel的情况:
- 简单对话:只需要基本的问答功能
- 快速原型:开发初期,快速验证想法
- 性能敏感:对性能要求极高的场景(虽然差异很小)
- 学习入门:刚开始学习Spring AI
- 代码简洁:希望代码尽可能简单
示例代码
@RestController
public class SimpleChatController {
@Resource
private ChatModel chatModel;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatModel.call(msg);
}
}
9.2 使用ChatClient的场景
✅ 适合使用ChatClient的情况:
- 复杂对话:需要系统提示、多轮对话等
- 函数调用:需要调用外部函数
- 多模态:需要处理图片、音频等
- RAG应用:需要结合知识库检索
- 记忆功能:需要记住对话历史
- 可读性:希望代码更易读、易维护
- 生产环境:正式项目推荐使用
示例代码
@RestController
public class AdvancedChatController {
@Resource
private ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient
.prompt()
.system("你是一个专业的Java程序员助手")
.user(msg)
.advisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.call()
.content();
}
}
十、迁移指南
10.1 从ChatModel迁移到ChatClient
迁移前(ChatModel)
@RestController
public class OldController {
@Resource
private ChatModel chatModel;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatModel.call(msg);
}
}
迁移后(ChatClient)
// 1. 配置类中创建Bean
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel).build();
}
}
// 2. 控制器中使用
@RestController
public class NewController {
@Resource
private ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient.prompt().user(msg).call().content();
}
}
十一、总结对比表
| 对比维度 | ChatModel | ChatClient |
|---|---|---|
| 定位 | 基础接口 | 高级封装 |
| API风格 | 函数式 | Fluent API |
| 简单对话 | ✅ 简洁 | ✅ 稍复杂 |
| 复杂对话 | ⚠️ 复杂 | ✅ 清晰 |
| 系统提示 | ⚠️ 需手动构建 | ✅ 支持 |
| 多轮对话 | ⚠️ 需手动构建 | ✅ 支持 |
| 函数调用 | ❌ 不支持 | ✅ 支持 |
| 多模态 | ❌ 不支持 | ✅ 支持 |
| Advisors | ❌ 不支持 | ✅ 支持 |
| 自动注入 | ✅ 支持 | ❌ 需配置 |
| 代码可读性 | ⚠️ 一般 | ✅ 优秀 |
| 学习曲线 | ✅ 简单 | ⚠️ 中等 |
| 性能 | ✅ 略快 | ⚠️ 略慢 |
| 推荐场景 | 简单应用、快速原型 | 生产环境、复杂应用 |
十二、最终建议
选择原则
- 新手入门:先学ChatModel,理解基本概念
- 简单项目:使用ChatModel,代码更简洁
- 复杂项目:使用ChatClient,功能更强大
- 生产环境:推荐ChatClient,可维护性更好
- 团队协作:推荐ChatClient,代码更易读
实际项目建议
// 生产环境推荐配置
@Configuration
public class ChatConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultOptions(ChatOptions.builder()
.model("qwen-plus")
.temperature(0.7)
.maxTokens(2000)
.build())
.build();
}
// ChatModel仍然可以保留,用于简单场景
// Spring AI会自动创建
}
总结:ChatModel是基础,ChatClient是进阶。根据项目复杂度和需求选择合适的API,两者可以共存使用。