ChatModel & ChatClient
Spring AI Alibaba 模型交互的核心抽象层,理解这两个组件是掌握整个框架的基础。
核心概念区分
很多初学者会混淆 ChatModel 和 ChatClient,它们是两个不同层次的抽象:
| 维度 | ChatModel | ChatClient |
|---|---|---|
| 层次 | 底层接口 | 高层封装 |
| 职责 | 直接调用 LLM | Fluent 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 / ModelMetadataChatResponse 结构
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最佳实践
推荐做法
- 在
@Configuration中统一配置ChatClientBean,避免重复创建 - 使用
defaultAdvisors设置全局横切逻辑(日志、记忆) - 运行时通过
.advisors()添加场景特定的 Advisor(如 RAG) - 生产环境配置合理的
temperature:创意任务 0.7-0.9,精确任务 0.1-0.3
注意事项
ChatClient是线程安全的,可以作为单例 Bean- 流式响应必须完整消费,否则连接不会释放
- Token 限制:注意
max-tokens不超过模型上下文窗口