Skip to content

ChatModel & ChatClient

Spring AI Alibaba 模型交互的核心抽象层,理解这两个组件是掌握整个框架的基础。

核心概念区分

很多初学者会混淆 ChatModelChatClient,它们是两个不同层次的抽象:

维度ChatModelChatClient
层次底层接口高层封装
职责直接调用 LLMFluent API + Advisor 链
使用场景框架扩展、底层控制日常业务开发
自动装配需要手动 build
Advisor 支持

结论:日常开发优先使用 ChatClient,需要底层控制时使用 ChatModel

ChatModel 深度解析

接口定义

java
public interface ChatModel extends Model<Prompt, ChatResponse>, StreamingChatModel {

    // 同步调用
    ChatResponse call(Prompt prompt);

    // 流式调用(默认实现,子类可覆盖)
    default Flux<ChatResponse> stream(Prompt prompt) {
        // 默认将同步结果包装为 Flux
        return Flux.just(this.call(prompt));
    }

    // 便捷方法:直接传字符串
    default String call(String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        return call(prompt).getResult().getOutput().getContent();
    }
}

DashScopeChatModel 实现原理

ChatModel.call(prompt)


DashScopeChatModel

    ├── 1. 将 Spring AI Prompt 转换为 DashScope API 请求格式
    │      Prompt → DashScopeApi.ChatCompletionRequest

    ├── 2. 调用 DashScope HTTP API
    │      POST https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation

    ├── 3. 解析响应
    │      DashScopeApi.ChatCompletion → ChatResponse

    └── 4. 填充元数据
           TokenUsage / FinishReason / ModelMetadata

ChatResponse 结构

java
ChatResponse response = chatModel.call(prompt);

// 获取文本内容
String content = response.getResult().getOutput().getContent();

// 获取 Token 用量
Usage usage = response.getMetadata().getUsage();
long promptTokens = usage.getPromptTokens();
long completionTokens = usage.getGenerationTokens();

// 获取结束原因
FinishReason reason = response.getResult().getMetadata().getFinishReason();
// STOP / LENGTH / TOOL_CALLS / CONTENT_FILTER

调用选项(ChatOptions)

java
// 方式一:全局配置(application.yml)
spring:
  ai:
    dashscope:
      chat:
        options:
          model: qwen-max
          temperature: 0.7
          top-p: 0.9
          max-tokens: 2048

// 方式二:运行时覆盖
ChatOptions options = DashScopeChatOptions.builder()
    .withModel("qwen-max")
    .withTemperature(0.3f)   // 更确定性的输出
    .withMaxTokens(4096)
    .build();

Prompt prompt = new Prompt("解释量子纠缠", options);
ChatResponse response = chatModel.call(prompt);

ChatClient 深度解析

设计模式:Builder + Fluent API

ChatClient 采用 Builder 模式构建,支持默认配置和运行时覆盖:

java
// 构建 ChatClient(通常在 @Configuration 中)
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
    return builder
        // 默认系统提示词
        .defaultSystem("你是一个专业的技术助手,回答简洁准确。")
        // 默认 Advisor(每次调用都会执行)
        .defaultAdvisors(
            new SimpleLoggerAdvisor(),
            new MessageChatMemoryAdvisor(chatMemory)
        )
        // 默认调用选项
        .defaultOptions(
            DashScopeChatOptions.builder()
                .withModel("qwen-max")
                .withTemperature(0.7f)
                .build()
        )
        .build();
}

Fluent API 完整用法

java
// 完整的 ChatClient 调用链
String result = chatClient.prompt()
    // 覆盖系统提示词
    .system("你是一个 Java 专家")
    // 用户消息(支持多种格式)
    .user(u -> u.text("解释 {concept}").param("concept", "虚拟线程"))
    // 运行时添加 Advisor
    .advisors(new QuestionAnswerAdvisor(vectorStore))
    // 注入工具
    .tools(new SearchTools(), new CalculatorTools())
    // 运行时覆盖选项
    .options(DashScopeChatOptions.builder().withTemperature(0.3f).build())
    // 执行调用
    .call()
    // 获取结果
    .content();

四种响应获取方式

java
var callSpec = chatClient.prompt().user("你好").call();

// 1. 获取纯文本
String text = callSpec.content();

// 2. 获取完整 ChatResponse(含元数据)
ChatResponse response = callSpec.chatResponse();

// 3. 结构化输出(自动 JSON 解析)
record BookInfo(String title, String author, int year) {}
BookInfo book = chatClient.prompt()
    .user("介绍《三体》这本书")
    .call()
    .entity(BookInfo.class);

// 4. 获取 List
List<String> items = chatClient.prompt()
    .user("列出 5 种设计模式")
    .call()
    .entity(new ParameterizedTypeReference<List<String>>() {});

流式响应

java
// 流式文本
Flux<String> textStream = chatClient.prompt()
    .user("写一首关于 Java 的诗")
    .stream()
    .content();

// 流式 ChatResponse(含元数据)
Flux<ChatResponse> responseStream = chatClient.prompt()
    .user("解释 Spring Boot 自动配置")
    .stream()
    .chatResponse();

// 在 WebFlux Controller 中使用
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message) {
    return chatClient.prompt()
        .user(message)
        .stream()
        .content();
}

多模型支持

Spring AI Alibaba 支持同时使用多个模型:

java
@Configuration
public class MultiModelConfig {

    // 通义千问(默认)
    @Bean
    @Primary
    public ChatClient qwenChatClient(
        @Qualifier("dashScopeChatModel") ChatModel qwenModel
    ) {
        return ChatClient.builder(qwenModel).build();
    }

    // OpenAI(备用)
    @Bean("openaiChatClient")
    public ChatClient openaiChatClient(
        @Qualifier("openAiChatModel") ChatModel openaiModel
    ) {
        return ChatClient.builder(openaiModel).build();
    }
}

// 使用时按需注入
@Service
public class SmartService {

    @Autowired
    private ChatClient qwenChatClient;

    @Autowired
    @Qualifier("openaiChatClient")
    private ChatClient openaiChatClient;
}

支持的模型列表

模型特点适用场景
qwen-max最强能力复杂推理、代码生成
qwen-plus均衡性能通用对话、文本处理
qwen-turbo速度最快实时交互、高并发
qwen-long超长上下文长文档分析
qwen-vl-max视觉理解图像分析
qwen-audio-turbo语音理解语音交互

底层原理:请求生命周期

用户代码
  │  chatClient.prompt().user("...").call().content()

ChatClient
  │  构建 AdvisedRequest

Advisor Chain(正向)
  │  SimpleLoggerAdvisor.before()
  │  MessageChatMemoryAdvisor.before()  ← 注入历史消息
  │  QuestionAnswerAdvisor.before()     ← 检索相关文档

ChatModel.call(enrichedPrompt)
  │  HTTP 请求 → DashScope API

Advisor Chain(反向)
  │  QuestionAnswerAdvisor.after()
  │  MessageChatMemoryAdvisor.after()   ← 保存新消息
  │  SimpleLoggerAdvisor.after()

返回 ChatResponse

最佳实践

推荐做法

  1. @Configuration 中统一配置 ChatClient Bean,避免重复创建
  2. 使用 defaultAdvisors 设置全局横切逻辑(日志、记忆)
  3. 运行时通过 .advisors() 添加场景特定的 Advisor(如 RAG)
  4. 生产环境配置合理的 temperature:创意任务 0.7-0.9,精确任务 0.1-0.3

注意事项

  • ChatClient 是线程安全的,可以作为单例 Bean
  • 流式响应必须完整消费,否则连接不会释放
  • Token 限制:注意 max-tokens 不超过模型上下文窗口

相关组件

本站内容由 褚成志 整理编写,仅供学习参考