【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比

第三章 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 同时提供了 ChatModelChatClient 都会问:

  • "这两个不都是用来调用 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 的场景

  1. 你需要精确控制请求的每个参数
  2. 你是库/框架开发者,需要底层能力
  3. 你需要实现自定义的 AI 逻辑

使用 ChatClient 的场景

  1. 日常的 AI 对话开发(推荐)
  2. 需要快速构建原型
  3. 需要用到系统提示词、模板、工具调用等高级功能

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   │
│                                                  │
└─────────────────────────────────────────────────┘

本章重点

  1. 理解 ChatModel 与 ChatClient 的定位差异
  2. 掌握 ChatClient 的链式调用写法
  3. 能够根据场景选择合适的 API

下章剧透(s04):

了解了两种 API 的使用后,下一章我们将学习流式输出的高级用法,体验"打字机"效果的实现原理。


📝 编辑者 :Flittly

📅 更新时间 :2026年3月

🔗 相关资源Spring AI ChatClient 官方文档

相关推荐
qq_297574672 小时前
K8s系列第十四篇:K8s 故障排查实战:常见故障定位与解决方法
java·docker·kubernetes
大厂观察员2 小时前
AI日记:BERT 和 GPT 选型难题怎么破
大数据·人工智能
2401_835792542 小时前
Java复习上
java·开发语言·python
GOWIN革文品牌咨询2 小时前
B2B品牌架构实操:集团品牌、业务品牌、产品品牌的6问判断法
大数据·人工智能·重构·智能设备·b2b品牌策划·b2b品牌设计
小昭在路上……2 小时前
编译与链接的本质:段(Section)的生成与定位
java·linux·开发语言
梦梦代码精2 小时前
开源即商用,预期产出、风险与优化建议
人工智能·gitee·前端框架·开源·github
咕噜签名-铁蛋2 小时前
GPU型实例安装nvidia-fabricmanager服务完整实操指南
大数据·数据库·人工智能·ai编程
zero15972 小时前
AI 编程黄金搭档:Superpowers Skills × OpenSpec 实战指南
人工智能·规范驱动开发·openspec·superpowers·ai高效编程
薛定猫AI2 小时前
【深度解析】Claude Auto Dream:从“短期对话”到“项目级心智模型”的记忆系统升级
人工智能·chatgpt