spring ai入门教程二

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 前缀的属性绑定到 ZhiPuAiConnectionPropertiesZhiPuAiChatProperties
  • @ConditionalOnMissingBean :如果没有手动定义 ZhiPuAiChatModel Bean,则自动创建。

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 PromptMessage

Prompt 是请求的封装,内部包含一个 List<Message> 和可选的 ChatOptionsMessage 接口代表一段对话内容,根据角色分为三种实现:

实现类 角色 说明
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 -- 模型配置

控制模型生成行为的关键参数,例如 temperaturemaxTokenstopP 等。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 ChatResponseGeneration

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();

四、基础用法代码示例

以下示例均基于智谱AIZhiPuAiChatModel,你只需替换为其他模型(如 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 使用 PromptUserMessage
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(或响应式环境)。


七、最佳实践与注意事项

  1. API Key 安全:永远不要将密钥硬编码在代码或配置文件中。使用环境变量或配置中心(如 Spring Cloud Config)。

    yaml 复制代码
    spring.ai.zhipuai.api-key: ${ZHIPU_API_KEY}
  2. 合理设置 temperature

    • 0.0~0.3:适合代码生成、事实问答(确定性高)。
    • 0.5~0.7:通用对话,平衡创造性与准确性。
    • 0.8~1.0:故事创作、头脑风暴(更随机)。
  3. 多轮对话的记忆管理 :你需要自行维护消息列表,将每次的 UserMessageAssistantMessage 存储并传入下一次 Prompt

  4. 结构化输出的提示技巧converter.getFormat() 会生成类似"你的输出必须是符合以下 JSON schema 的 JSON 对象..."的指令。如果模型不遵守,可以在提示词中增加"只输出 JSON,不要有其他解释"。

  5. 流式响应异常处理 :使用 doOnErroronErrorResume 避免连接断开时客户端无响应。

  6. 性能考虑 :同步调用会阻塞线程,在 WebFlux 环境中建议优先使用流式或异步方式。对于高并发场景,考虑线程池配置或使用响应式 WebClient 调用模型 API。

相关推荐
动恰客流统计1 小时前
客流统计如何结合AI分析?从传统计数到智能决策的技术升级路径
数据库·人工智能·边缘计算
世界很奇妙塔1 小时前
基因编辑产业化:从科研探索到临床应用,重构生命健康产业底层逻辑
大数据·人工智能·机器学习
SeeYa-J1 小时前
Spring IOC(Inversion of Control)
java·spring·rpc
试剂界的爱马仕1 小时前
Anti-mouse PD-1 mAb (Clone RMP1-14) 与 Axitinib 小鼠实验使用方案整理汇总
大数据·人工智能·深度学习·学习
dreamread1 小时前
2026带详细解读的八字排盘App怎么选:看解释层级、AI边界和复盘价值
人工智能·软件工具·传统文化
每天三杯咖啡2 小时前
【无标题】
人工智能·技术选型·deepseek
nbtang20262 小时前
AI Agent 入门(三):Tool Use 入门 —— Function Calling 原理与实战
人工智能·ai·agent
不会c+2 小时前
02-SpringBoot配置文件
java·spring boot·后端
anOnion2 小时前
Agentic 前端开发之 实时显示 AI Agent 终端输出
前端·javascript·人工智能