Spring AI Chat模型入门——理解 ChatModel、ChatClient、Prompt 和 ChatResponse

一、为什么需要 Spring AI

直接在 Java 项目中调用大模型,通常要处理 HTTP 请求、身份认证、请求参数、响应解析、流式传输以及不同模型厂商之间的协议差异。当应用从一个模型切换到另一个模型时,这些差异还会进入业务代码。

Spring AI 的作用,是借鉴 Spring 生态一贯的抽象设计,为 AI 应用提供相对统一的编程模型。开发者可以更多地面向 `ChatModel`、`ChatClient`、`Prompt` 等 Spring AI 抽象编程,而不是让业务代码直接依赖某一家模型厂商的原始 SDK。

Spring AI 并不是一个大模型,也不会替我们训练模型。它更像连接 Java 应用与大模型服务之间的工程化适配层。

二、本文学习目标

学完本文,你应该能够回答以下问题:

  • Spring AI 的聊天模型 API 解决了什么问题?

  • `ChatModel` 和 `ChatClient` 有什么区别?

  • `Message`、`Prompt`、`Generation` 和 `ChatResponse` 分别表示什么?

  • 为什么调用一次聊天接口,并不等于实现了多轮对话?

  • 同步调用和流式调用在接口层面有什么区别?

三、核心对象之间的关系

3.1 Message:带有角色的消息

例如:

java 复制代码
```java
SystemMessage systemMessage =
        new SystemMessage("你是一名专业的 Java 教师");

UserMessage userMessage =
        new UserMessage("请解释什么是依赖注入");

注意:System Message 是对模型的指令和引导,但不能代替权限校验、数据脱敏、输入检查和输出治理。

3.2 Prompt:一次模型请求的输入载体

`Prompt` 用于组织一次模型调用所需的信息。它可以包含一条或多条消息,也可以携带本次请求的模型参数。

java 复制代码
```java
Prompt prompt = new Prompt(
        List.of(systemMessage, userMessage)
);

可以把 `Prompt` 理解为一次模型请求的完整上下文,而不是简单的"提示词字符串"。

3.3 Generation:模型生成的一条候选结果

模型可能返回一个或多个候选答案,Spring AI 使用 `Generation` 表示其中一条生成结果。生成的文本通常位于输出消息中。

java 复制代码
Generation generation = response.getResult();

String text = generation != null
        ? generation.getOutput().getText()
        : "";

不同 Spring AI 版本中,读取文本的方法可能是 `getText()` 或旧版的 `getContent()`。同一篇文章必须使用与项目依赖一致的 API,不能混用不同版本的写法。

3.4 ChatResponse:完整的模型响应

ChatResponse` 不只包含最终文本,还可能包含:

  • 一个或多个生成结果;

  • 模型响应元数据;

  • Token 使用量;

  • 结束原因;

  • 模型供应商提供的其他信息。

如果业务只需要字符串,可以使用便捷方法;如果需要统计 Token、记录模型信息或者分析结束原因,就应保留完整的 `ChatResponse`。

四、ChatModel:底层聊天模型抽象

ChatModel 是 Spring AI 聊天模型体系中的核心接口。它统一了同步调用和流式调用的入口,让上层代码不必直接面向某个厂商的模型客户端。

本文使用版本中的接口结构如下:

java 复制代码
public interface ChatModel
        extends Model<Prompt, ChatResponse>, StreamingChatModel {

    default String call(String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        Generation generation = call(prompt).getResult();
        return (generation != null)
                ? generation.getOutput().getText()
                : "";
    }

    default String call(Message... messages) {
        Prompt prompt = new Prompt(Arrays.asList(messages));
        Generation generation = call(prompt).getResult();
        return (generation != null)
                ? generation.getOutput().getText()
                : "";
    }

    @Override
    ChatResponse call(Prompt prompt);

    default ChatOptions getDefaultOptions() {
        return ChatOptions.builder().build();
    }

    default Flux<ChatResponse> stream(Prompt prompt) {
        throw new UnsupportedOperationException(
                "streaming is not supported"
        );
    }
}
4.1 call(String message) 做了什么?

该方法适合最简单的单轮调用:

String answer = chatModel.call("请介绍一下 Spring AI");

从源码可以看出,它在内部完成了以下转换:

bash 复制代码
String
  → UserMessage
  → Prompt
  → call(Prompt)
  → ChatResponse
  → Generation
  → 文本

因此,`call(String)` 是便捷入口,真正的核心同步方法仍然是 `call(Prompt)`。

4.2 call(Message... messages) 有什么用

这个重载可以一次传入多条不同角色的消息:

java 复制代码
String answer = chatModel.call(
        new SystemMessage("你是一名专业的 Java 教师"),
        new UserMessage("请解释 Spring Bean 的生命周期")
);

它比单字符串调用更加灵活,但最终仍只返回文本。如果需要完整元数据,应使用 call(Prompt)。

4.3 call(Prompt prompt) 为什么最重要
java 复制代码
ChatResponse response = chatModel.call(prompt);

具体模型实现必须实现这个方法。使用它可以:

  • 发送多条消息;

  • 设置本次调用的模型参数;

  • 读取完整的 `ChatResponse`;

  • 获取生成结果之外的元数据。

4.4 声明流式能力,不等于所有模型都支持流式输出

`ChatModel` 同时继承 `StreamingChatModel`,但接口中的默认 `stream()` 实现会直接抛出异常:

java 复制代码
throw new UnsupportedOperationException(
        "streaming is not supported"
);

这说明 Spring AI 在接口层面统一了流式调用入口,但具体实现仍需满足两个条件:

  1. 对应的 `ChatModel` 实现重写了流式方法;

  2. 底层模型服务本身支持流式响应。

五、ChatClient:面向业务的高层客户端

`ChatClient` 构建在 `ChatModel` 之上,提供类似 `WebClient` 或 `RestClient` 的链式调用体验。

java 复制代码
String answer = chatClient.prompt()
        .user("请介绍一下 Spring AI")
        .call()
        .content();

相比直接操作 `ChatModel`,`ChatClient` 更适合在业务代码中使用,因为它可以更自然地组织:

  • System Message 和 User Message;

  • 提示词模板参数;

  • 同步和流式调用;

  • 结构化输出;

  • Advisor 等增强能力。

二者的层级关系是:

六、ChatClient 与 ChatModel 如何选择

七、一次调用为什么不等于多轮聊天

下面的代码每次只发送当前用户输入:

java 复制代码
chatClient.prompt()
        .user(message)
        .call()
        .content();

当第二次 HTTP 请求到来时,模型并不知道第一次请求发生过什么。因此,这只是单轮调用,而不是真正具有上下文的多轮聊天。

多轮对话需要显式保存和重新发送历史消息,或者使用 Spring AI 的 Chat Memory、Advisor 等机制。后续会单独用一篇文章讲解。

八、常见问题

8.1 Spring AI 会自动训练模型吗?

不会。Spring AI 主要解决应用集成和工程化问题,模型能力来自 DeepSeek、OpenAI、Ollama 等底层模型服务。

8.2 换模型以后业务代码完全不用改吗?

基础调用通常可以保持相对稳定,但不同模型支持的参数、多模态能力、工具调用能力和返回元数据并不完全相同。统一抽象降低了切换成本,并不意味着所有模型功能完全一致。

九、本文总结

本文需要记住五点:

  1. Spring AI 是 Java 应用与大模型服务之间的工程化抽象层。

  2. `Message` 表示带角色的消息,`Prompt` 表示一次模型请求的完整输入。

  3. `ChatModel` 是底层模型抽象,统一同步和流式调用入口。

  4. `ChatClient` 构建在 `ChatModel` 之上,更适合大多数业务开发。

  5. 单次调用默认没有跨请求记忆,多轮聊天需要额外维护会话上下文。

下一篇将搭建一个完整的 Spring Boot 项目,并通过 `ChatClient` 调用 DeepSeek。

相关推荐
江厌0113 分钟前
金融大模型私有化:信创合规下,算力该按哪个口径配
人工智能·深度学习·大模型·私有化部署·gpu·信创·ai服务器
Csvn16 分钟前
第 13 章 反思 Reflection
人工智能·aigc·agent
霸道流氓气质21 分钟前
Spring AI 结构化输出:JSON Mode 与 BeanOutputConverter
人工智能·spring·json
G***技23 分钟前
IB3-771嵌入式主板6 TOPS真实算力怎么用:从PyTorch到RKNN的量化落地
人工智能·嵌入式硬件
cxr82824 分钟前
涌现与坍塌在AI自然语义分析与生成中的层级化因果约束
人工智能·智能体·认知框架
nanawinona25 分钟前
先跑通小流程,再让 AI 和 Python 承接复杂量化
人工智能·python
beiju25 分钟前
别拿生产账号给 Agent 实习:从 Anthropic 事故看 Agent Staging
人工智能
用户52746756142126 分钟前
模型退役不是换个 ID:Agent 迁移最容易丢的是岗位能力
人工智能
奈斯先生Vector29 分钟前
本地图片识别怎么接入多模态 AI?用 Python API 理解 GPT-4o Vision 的真实工作流
开发语言·人工智能·windows·python·网络协议·http·aigc