Spring AI入门教程二:Chat Model API详解
在上一篇教程中,我们了解了 Spring AI 的整体架构、核心概念,并快速体验了 ChatClient API 的便捷性。今天,我们将深入底层------Chat Model API。它是 Spring AI 与各种大语言模型(LLM)交互的核心抽象层,为你提供更细粒度的控制:从构建多轮对话、配置模型参数,到解析结构化输出、处理流式响应。掌握 Chat Model API,你将能够灵活驾驭不同的 AI 模型,构建生产级的智能应用。
一、Chat Model API 概览
Spring AI 的 Chat Model API 定义了两个核心接口:ChatModel(同步)和 StreamingChatModel(异步流式)。所有针对具体 AI 模型的实现(如 DeepSeek、智谱AI、OpenAI 等)都遵循这两个接口,因此你可以在不修改业务代码的情况下轻松切换模型提供商。
java
public interface ChatModel extends Model<Prompt, ChatResponse> {
default String call(String message) {...}
ChatResponse call(Prompt prompt);
}
public interface StreamingChatModel extends StreamingModel<Prompt, ChatResponse> {
default Flux<String> stream(String message) {...}
Flux<ChatResponse> stream(Prompt prompt);
}
call(String message):最简形式,直接传入问题字符串,返回回答字符串。适用于快速测试或简单场景。call(Prompt prompt):核心方法,接收封装好的Prompt对象,返回完整的ChatResponse(包含元数据、多个生成结果等)。- 流式方法 :返回响应式
Flux,适合长文本生成场景,提升用户体验。
二、ChatModel 的自动配置与注入
在编写代码之前,有必要先了解 Spring AI 是如何自动配置 ChatModel 的,这样才能理解为什么直接 @Autowired 一个 ZhiPuAiChatModel 就能使用,以及如何根据自己的需求调整配置。
2.1 自动配置原理
Spring AI 为每个支持的模型提供了独立的 *AutoConfiguration 类。以智谱AI为例,ZhiPuAiChatAutoConfiguration 承担了核心初始化工作:
java
@AutoConfiguration(after = {...})
@ConditionalOnClass(ZhiPuAiApi.class)
@ConditionalOnProperty(name = SpringAIModelProperties.CHAT_MODEL, havingValue = SpringAIModels.ZHIPUAI, matchIfMissing = true)
@EnableConfigurationProperties({ ZhiPuAiConnectionProperties.class, ZhiPuAiChatProperties.class })
public class ZhiPuAiChatAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ZhiPuAiChatModel zhiPuAiChatModel(...) {
// 1. 构建 ZhiPuAiApi(封装 HTTP 细节)
// 2. 创建 ZhiPuAiChatModel 实例
// 3. 返回
}
}
@ConditionalOnClass:只有引入了spring-ai-starter-model-zhipuai依赖,配置才会生效。@ConditionalOnProperty:默认启用,可通过spring.ai.chat.model控制。@EnableConfigurationProperties:将配置文件中以spring.ai.zhipuai前缀的属性绑定到ZhiPuAiConnectionProperties和ZhiPuAiChatProperties。@ConditionalOnMissingBean:如果没有手动定义ZhiPuAiChatModelBean,则自动创建。
在 application.yaml 中,你只需配置:
yaml
spring:
ai:
zhipuai:
api-key: ${ZHIPU_API_KEY}
base-url: https://open.bigmodel.cn/api/paas/v4
chat:
options:
model: glm-4-flash
temperature: 0.7
2.2 注入 ChatModel
自动配置完成后,Spring 容器中就有了 ZhiPuAiChatModel 的 Bean。你可以直接注入:
java
@Service
public class AiService {
// 按类型注入
@Autowired
private ZhiPuAiChatModel chatModel;
// 或者注入接口(项目中只有一个实现时)
@Autowired
private ChatModel chatModel;
}
注意 :如果你同时配置了多个模型(如 DeepSeek 和智谱AI),需要使用
@Qualifier指定具体 Bean 名称。
三、核心组件详解
要熟练使用 Chat Model API,必须理解以下几个核心类:
3.1 Prompt 与 Message
Prompt 是请求的封装,内部包含一个 List<Message> 和可选的 ChatOptions。Message 接口代表一段对话内容,根据角色分为三种实现:
| 实现类 | 角色 | 说明 |
|---|---|---|
SystemMessage |
系统 | 设定 AI 助手的行为、身份、知识范围 |
UserMessage |
用户 | 用户的提问或指令 |
AssistantMessage |
助手 | AI 模型的回复(通常用于多轮对话的上下文) |
java
SystemMessage system = new SystemMessage("你是一位资深Java架构师");
UserMessage user = new UserMessage("请介绍Spring Boot和Spring Cloud的区别");
Prompt prompt = new Prompt(List.of(system, user));
3.2 ChatOptions -- 模型配置
控制模型生成行为的关键参数,例如 temperature、maxTokens、topP 等。Spring AI 支持 启动时默认配置 + 运行时动态覆盖。
- 启动配置 :在
application.yaml中设置。 - 运行时覆盖 :在
Prompt中传入新的ChatOptions实例,优先级更高。
java
ZhiPuAiChatOptions runtimeOptions = ZhiPuAiChatOptions.builder()
.temperature(0.9)
.maxTokens(500)
.build();
Prompt prompt = new Prompt(userMessage, runtimeOptions);
3.3 ChatResponse 与 Generation
ChatResponse 是模型的返回结果,包含:
List<Generation>:模型可能返回多个候选结果(通常只有一个)。ChatResponseMetadata:响应元数据,如 token 使用量、模型版本等。
每个 Generation 对象中包裹着 AssistantMessage(实际回复文本)和 ChatGenerationMetadata。
java
ChatResponse response = chatModel.call(prompt);
Generation generation = response.getResults().get(0);
String answer = generation.getOutput().getText();
Map<String, Object> usage = response.getMetadata().getUsage();
四、基础用法代码示例
以下示例均基于智谱AI 的 ZhiPuAiChatModel,你只需替换为其他模型(如 DeepSeek)即可复用。
4.1 最简单的调用 -- call(String)
java
@RestController
public class SimpleChatController {
@Resource
private ZhiPuAiChatModel chatModel;
@GetMapping("/simple")
public String simpleChat(@RequestParam String question) {
return chatModel.call(question);
}
}
4.2 使用 Prompt 和 UserMessage
java
@GetMapping("/prompt")
public ChatResponse promptCall(@RequestParam String question) {
UserMessage userMessage = new UserMessage(question);
Prompt prompt = new Prompt(userMessage);
return chatModel.call(prompt);
}
4.3 多轮对话:携带系统消息和历史
java
@GetMapping("/multi-turn")
public ChatResponse multiTurn() {
SystemMessage system = new SystemMessage("你是一位专业的Java面试官,请用中文回答。");
UserMessage user = new UserMessage("什么是JVM?");
Prompt prompt = new Prompt(List.of(system, user));
return chatModel.call(prompt);
}
4.4 为消息添加元数据(可用于链路追踪)
java
UserMessage userMsg = new UserMessage("今天天气怎么样?");
userMsg.getMetadata().put("userId", "12345");
userMsg.getMetadata().put("requestId", UUID.randomUUID().toString());
Prompt prompt = new Prompt(userMsg);
ChatResponse response = chatModel.call(prompt);
4.5 解析 ChatResponse 的完整信息
java
@GetMapping("/parse")
public Map<String, Object> parseResponse(@RequestParam String question) {
ChatResponse resp = chatModel.call(new Prompt(new UserMessage(question)));
Generation gen = resp.getResults().get(0);
return Map.of(
"answer", gen.getOutput().getText(),
"responseMetadata", resp.getMetadata(),
"generationMetadata", gen.getMetadata()
);
}
五、高级功能实战
5.1 结构化输出 -- 将 AI 回复转为 Java 对象
利用 BeanOutputConverter,你可以在提示词中嵌入 JSON 格式要求,并自动将模型输出反序列化为 POJO。
定义实体:
java
public record ActorFilms(String actor, List<String> movies) {}
调用示例:
java
@GetMapping("/actor")
public ActorFilms getActorFilms() {
BeanOutputConverter<ActorFilms> converter = new BeanOutputConverter<>(ActorFilms.class);
String promptText = """
生成演员周星驰的5部代表作。
%s
""".formatted(converter.getFormat()); // 自动生成格式说明
Prompt prompt = new Prompt(new UserMessage(promptText));
String content = chatModel.call(prompt).getResults().get(0).getOutput().getText();
return converter.convert(content);
}
返回列表 时使用 ParameterizedTypeReference:
java
BeanOutputConverter<List<ActorFilms>> converter = new BeanOutputConverter<>(
new ParameterizedTypeReference<List<ActorFilms>>() {}
);
5.2 动态模型切换
在同一 ChatModel 实例上,通过运行时 ChatOptions 指定不同模型或参数:
java
@GetMapping("/switch")
public ChatResponse switchModel(@RequestParam String modelName) {
UserMessage userMsg = new UserMessage("介绍一下你自己");
ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()
.model(modelName) // 例如 "glm-4.6v" 或 "glm-4-flash"
.temperature(0.8)
.build();
Prompt prompt = new Prompt(userMsg, options);
return chatModel.call(prompt);
}
5.3 复杂的模型参数配置
java
ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()
.model("glm-4.6v")
.temperature(0.95) // 高创造性
.maxTokens(800)
.topP(0.9) // nucleus sampling
.stopSequences(List.of("\n\n")) // 遇到两个换行停止
.build();
六、流式响应(StreamingChatModel)
对于长文本生成(如文章、代码),流式输出能大幅提升用户体验。StreamingChatModel 返回响应式 Flux,与 Spring WebFlux 完美集成。
6.1 基础流式字符串
java
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String question) {
return chatModel.stream(question);
}
6.2 流式返回 ChatResponse(获取元数据)
java
@GetMapping(value = "/stream-response", produces = MediaType.APPLICATION_NDJSON_VALUE)
public Flux<ChatResponse> streamResponse(@RequestParam String question) {
return chatModel.stream(new Prompt(new UserMessage(question)));
}
6.3 流式聚合与错误处理
java
@GetMapping("/stream-collect")
public String collectStream(@RequestParam String question) {
StringBuilder full = new StringBuilder();
chatModel.stream(question)
.doOnNext(chunk -> log.info("收到: {}", chunk))
.doOnError(e -> log.error("流式异常", e))
.onErrorResume(e -> Flux.just("发生错误,请重试"))
.blockLast(); // 阻塞直到完成
return full.toString();
}
注意 :流式方法依赖
reactor.core,需确保项目中引入了spring-boot-starter-webflux(或响应式环境)。
七、最佳实践与注意事项
-
API Key 安全:永远不要将密钥硬编码在代码或配置文件中。使用环境变量或配置中心(如 Spring Cloud Config)。
yamlspring.ai.zhipuai.api-key: ${ZHIPU_API_KEY} -
合理设置
temperature:- 0.0~0.3:适合代码生成、事实问答(确定性高)。
- 0.5~0.7:通用对话,平衡创造性与准确性。
- 0.8~1.0:故事创作、头脑风暴(更随机)。
-
多轮对话的记忆管理 :你需要自行维护消息列表,将每次的
UserMessage和AssistantMessage存储并传入下一次Prompt。 -
结构化输出的提示技巧 :
converter.getFormat()会生成类似"你的输出必须是符合以下 JSON schema 的 JSON 对象..."的指令。如果模型不遵守,可以在提示词中增加"只输出 JSON,不要有其他解释"。 -
流式响应异常处理 :使用
doOnError和onErrorResume避免连接断开时客户端无响应。 -
性能考虑 :同步调用会阻塞线程,在 WebFlux 环境中建议优先使用流式或异步方式。对于高并发场景,考虑线程池配置或使用响应式
WebClient调用模型 API。