Spring AI 文本聊天模型完全指南:ChatModel 与 ChatClient

本文定位 :这是一篇基于 Spring AI 官方文档的二次创作。官方文档分散在多个页面且偏英文参考手册风格,本文将两篇核心文档------ChatClient APIOpenAI Chat 融合在一起,用通俗易懂的中文逐节解读,并结合真实项目代码帮助你理解每一个知识点。

希望对大家有帮助!

官方文档原文链接


目录

  • [1. 官方文档说了什么?------全局概览](#1. 官方文档说了什么?——全局概览)
  • [2. 背景知识:两个核心角色 ChatModel 与 ChatClient](#2. 背景知识:两个核心角色 ChatModel 与 ChatClient)
  • [3. 前置条件(Prerequisites)](#3. 前置条件(Prerequisites))
  • [4. 自动配置(Auto-configuration)](#4. 自动配置(Auto-configuration))
  • [5. 配置属性详解(Configuration Properties)](#5. 配置属性详解(Configuration Properties))
    • [5.1 连接属性(Connection Properties)](#5.1 连接属性(Connection Properties))
    • [5.2 重试属性(Retry Properties)](#5.2 重试属性(Retry Properties))
    • [5.3 聊天模型属性(Chat Properties)](#5.3 聊天模型属性(Chat Properties))
  • [6. ChatModel 底层用法------直接调用模型](#6. ChatModel 底层用法——直接调用模型)
    • [6.1 最简调用:一句话聊天](#6.1 最简调用:一句话聊天)
    • [6.2 使用 Prompt 对象:更精细的控制](#6.2 使用 Prompt 对象:更精细的控制)
    • [6.3 角色设定:System Message 与 User Message](#6.3 角色设定:System Message 与 User Message)
    • [6.4 流式响应:stream() 方法](#6.4 流式响应:stream() 方法)
    • [6.5 运行时选项(Runtime Options)](#6.5 运行时选项(Runtime Options))
  • [7. ChatClient 高层用法------流式 API(官方推荐)](#7. ChatClient 高层用法——流式 API(官方推荐))
    • [7.1 创建 ChatClient](#7.1 创建 ChatClient)
    • [7.2 Fluent API 基础用法](#7.2 Fluent API 基础用法)
    • [7.3 多种响应类型](#7.3 多种响应类型)
    • [7.4 流式响应(Streaming)](#7.4 流式响应(Streaming))
    • [7.5 提示词模板(Prompt Templates)](#7.5 提示词模板(Prompt Templates))
    • [7.6 默认配置(Using Defaults)](#7.6 默认配置(Using Defaults))
    • [7.7 Advisor 机制](#7.7 Advisor 机制)
    • [7.8 聊天记忆(Chat Memory)](#7.8 聊天记忆(Chat Memory))
  • [8. ChatModel vs ChatClient:如何选择?](#8. ChatModel vs ChatClient:如何选择?)
  • [9. 官方示例控制器解读](#9. 官方示例控制器解读)
  • [10. 实战进阶:结合项目代码讲解](#10. 实战进阶:结合项目代码讲解)
  • [11. 常见问题与排错指南](#11. 常见问题与排错指南)
  • [12. 总结](#12. 总结)
  • [13. 参考资料](#13. 参考资料)

1. 官方文档说了什么?------全局概览

Spring AI 关于文本聊天的官方文档分散在两个页面中,它们的关系和结构如下:

复制代码
Spring AI 文本聊天
│
├── ChatClient API(chatclient.html)        ← 高层 API,流式编程风格
│   ├── Creating a ChatClient                → 如何创建
│   ├── ChatClient Fluent API                → 流式 API 用法
│   ├── ChatClient Responses                 → 响应处理(同步/流式/实体映射)
│   ├── Prompt Templates                     → 提示词模板
│   ├── Using Defaults                       → 默认配置
│   ├── Advisors                             → 拦截器/增强器
│   └── Chat Memory                          → 聊天记忆
│
└── OpenAI Chat(openai-chat.html)          ← 底层模型配置
    ├── Prerequisites                        → 前置条件
    ├── Auto-configuration                   → 自动配置与依赖
    ├── Chat Properties                      → 配置属性
    ├── Runtime Options                      → 运行时选项
    ├── Sample Controller                    → 官方示例
    ├── Function Calling                     → 函数调用
    └── Multimodal                           → 多模态

一句话理解两者关系ChatModel(如 OpenAiChatModel)是底层"引擎",ChatClient 是上层"方向盘"。你可以直接操作引擎,也可以通过方向盘更优雅地驾驶。


2. 背景知识:两个核心角色 ChatModel 与 ChatClient

2.1 ChatModel 是什么?

官方原文 (来自 openai-chat.html):
"Spring AI supports the various AI language models from OpenAI, the company behind ChatGPT, which has been instrumental in sparking interest in AI-driven text generation."

ChatModel 是 Spring AI 定义的底层聊天模型接口,它直接封装了与 AI 模型的交互。其核心方法如下:

java 复制代码
public interface ChatModel {
    // 核心方法
    ChatResponse call(Prompt prompt);             // 同步调用,传入 Prompt,返回完整响应
    Flux<ChatResponse> stream(Prompt prompt);     // 流式调用,传入 Prompt,返回响应流

    // 便捷方法(接口默认方法,内部自动封装 Prompt)
    String call(String message);                  // 传入字符串,直接返回字符串
    Flux<String> stream(String message);          // 传入字符串,直接返回文本流
}

注意call(String)stream(String) 是接口提供的便捷默认方法 ,内部会自动将字符串封装成 Prompt 对象再调用核心方法。所以你看到 chatModel.call("你好") 这么简单的写法,背后其实也走了完整的 Prompt 流程。

不同 AI 厂商各自提供实现:

复制代码
ChatModel(接口)
├── OpenAiChatModel       → OpenAI(GPT-4o、GPT-3.5 等)
├── OllamaChatModel       → Ollama(本地模型)
├── DashScopeChatModel    → 阿里云通义千问
├── QianFanChatModel      → 百度千帆
└── ...更多实现

2.2 ChatClient 是什么?

官方原文 (来自 chatclient.html):
"The ChatClient offers a fluent API for communicating with an AI Model. It supports both a synchronous and streaming programming model."

翻译:ChatClient 提供了一个流式 API(Fluent API)来与 AI 模型通信。它同时支持同步和流式编程模型。

ChatClient 是构建在 ChatModel 之上的高层封装,它提供了更优雅的链式调用方式:

java 复制代码
// ChatModel 的调用方式(底层)
String result = chatModel.call("你好");

// ChatClient 的调用方式(高层,Fluent API)
String result = chatClient.prompt()
        .user("你好")
        .call()
        .content();

2.3 核心类一览

类名 层级 通俗解释
ChatModel 底层接口 "聊天引擎"的规范
OpenAiChatModel 底层实现 OpenAI 的"聊天引擎"
ChatClient 高层封装 在引擎之上的"方向盘",更好用
ChatClient.Builder 构建器 用来创建和配置 ChatClient
Prompt 请求对象 封装消息列表和选项
UserMessage 用户消息 用户说的话
SystemMessage 系统消息 给 AI 的"人设"指令
ChatResponse 响应对象 AI 返回的完整结果
OpenAiChatOptions 运行时选项 控制模型、温度等参数

2.4 调用流程对比

复制代码
【ChatModel 直接调用】                    【ChatClient 链式调用】

构建 Prompt                               chatClient.prompt()
    ↓                                         .system("人设")
chatModel.call(prompt)                        .user("用户输入")
    ↓                                         .call()
拿到 ChatResponse                             .content()
    ↓                                         ↓
response.getResult()                      直接拿到 String 结果
    .getOutput().getText()

3. 前置条件(Prerequisites)

官方原文
"You will need to create an API with OpenAI to access ChatGPT models. Create an account at OpenAI signup page and generate the token on the API Keys page."

3.1 获取 API Key

  1. 打开 OpenAI 官网 注册账号
  2. 进入 API Keys 页面
  3. 点击 "Create new secret key" 生成密钥
  4. 妥善保存密钥

3.2 配置 API Key

官方原文
"The Spring AI project defines a configuration property named spring.ai.openai.api-key that you should set to the value of the API Key obtained from openai.com."

"For enhanced security when handling sensitive information like API keys, you can use Spring Expression Language (SpEL) to reference a custom environment variable."

推荐通过环境变量配置(和 ImageModel 一样):

yaml 复制代码
# application.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
bash 复制代码
# 设置环境变量
# Linux / macOS
export OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx

# Windows PowerShell
$env:OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxx"

4. 自动配置(Auto-configuration)

官方原文
"Spring AI provides Spring Boot auto-configuration for the OpenAI Chat Client. To enable it add the following dependency to your project's Maven pom.xml."

添加依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

和 ImageModel 用同一个依赖spring-ai-starter-model-openai 是 OpenAI 的"全家桶" Starter,同时激活 ChatModel、ImageModel、SpeechModel 的自动配置。

加了这个依赖后,Spring Boot 会自动创建以下 Bean:

  • OpenAiChatModel --- 可以直接注入使用
  • ChatClient.Builder --- 用于构建 ChatClient

启用/禁用聊天模型

官方原文
"Enabling and disabling of the chat auto-configurations are now configured via top level properties with the prefix spring.ai.model.chat."

yaml 复制代码
# 启用(默认就是开启的)
spring.ai.model.chat=openai

# 禁用
spring.ai.model.chat=none

5. 配置属性详解(Configuration Properties)

5.1 连接属性(Connection Properties)

官方原文
"The prefix spring.ai.openai is used as the property prefix that lets you connect to OpenAI."

属性 说明 默认值 什么时候需要改?
spring.ai.openai.base-url API 服务器地址 https://api.openai.com 使用代理或第三方兼容服务(如 DeepSeek)时
spring.ai.openai.api-key API 密钥 必填
spring.ai.openai.organization-id 组织 ID 属于多个组织时
spring.ai.openai.project-id 项目 ID 区分不同项目用量时

实用提示 :很多国产模型(如 DeepSeek、千帆等)都兼容 OpenAI 协议,只需修改 base-urlapi-key 即可接入:

yaml 复制代码
spring:
  ai:
    openai:
      base-url: https://api.deepseek.com    # DeepSeek 的 API 地址
      api-key: ${DEEPSEEK_API_KEY}

5.2 重试属性(Retry Properties)

官方原文
"The prefix spring.ai.retry is used as the property prefix that lets you configure the retry mechanism for the OpenAI chat model."

和 ImageModel 完全相同的重试机制(指数退避策略):

属性 说明 默认值
spring.ai.retry.max-attempts 最大重试次数 10
spring.ai.retry.backoff.initial-interval 首次重试等待 2 秒
spring.ai.retry.backoff.multiplier 等待倍增因子 5
spring.ai.retry.backoff.max-interval 最大等待时间 3 分钟
spring.ai.retry.on-client-errors 4xx 错误是否重试 false

大多数情况下默认值就够用了。

5.3 聊天模型属性(Chat Properties)

官方原文
"The prefix spring.ai.openai.chat is the property prefix that lets you configure the chat model implementation for OpenAI."

这些是核心配置,直接影响 AI 的回答质量和风格:

属性 说明 默认值 详细解释
spring.ai.openai.chat.options.model 模型名称 gpt-4o-mini 可选 gpt-4ogpt-4o-minigpt-4-turbogpt-3.5-turbo
spring.ai.openai.chat.options.temperature 温度 0.8 0~2,越高越有创意,越低越稳定
spring.ai.openai.chat.options.maxTokens 最大输出 Token 数 - 限制回复长度,1 个 Token ≈ 3/4 个英文单词
spring.ai.openai.chat.options.topP 核采样 - 0~1,和 temperature 类似,不建议同时修改两者
spring.ai.openai.chat.options.frequencyPenalty 频率惩罚 0.0 -2~2,正值减少重复内容
spring.ai.openai.chat.options.presencePenalty 存在惩罚 - -2~2,正值鼓励谈论新话题
spring.ai.openai.chat.options.n 生成几个回复 1 一般保持 1 以节省费用
spring.ai.openai.chat.options.stop 停止序列 - 遇到这些文本时停止生成
spring.ai.openai.chat.options.seed 随机种子 - 相同 seed + 相同输入 → 相同输出(Beta)

官方文档还提到聊天模型可以单独覆盖连接属性:

  • spring.ai.openai.chat.base-url:单独为聊天模型设置 API 地址
  • spring.ai.openai.chat.api-key:单独为聊天模型设置 API Key
  • spring.ai.openai.chat.completions-path:API 路径,默认 /v1/chat/completions
temperature 通俗解释

这是新手最常见的疑问,官方文档只说了"controls creativity":

temperature 值 效果 适用场景
0.0 ~ 0.3 稳定、确定性高,几乎每次回答一样 代码生成、数据提取、FAQ 问答
0.4 ~ 0.7 平衡,有一定变化但不会太离谱 日常对话、内容摘要
0.8 ~ 1.2 有创意,回答多样化 创意写作、头脑风暴
1.2 ~ 2.0 非常随机,可能出现不连贯内容 实验场景
配置文件完整示例
yaml 复制代码
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4o             # 使用 GPT-4o 模型
          temperature: 0.7          # 平衡的创意度
          maxTokens: 2000           # 最大回复 2000 Token

6. ChatModel 底层用法------直接调用模型

OpenAiChatModel 是最直接的使用方式,适合简单场景和快速上手。

6.1 最简调用:一句话聊天

来自项目中 DeepSeekController.javaQianfanController.java 的写法:

java 复制代码
@Autowired
private OpenAiChatModel chatModel;

@RequestMapping("/chat")
public String chat(String message){
    return chatModel.call(message);
}

就这么简单! chatModel.call(message) 传入一个字符串,返回一个字符串------AI 的回复。

官方原文 (Runtime Options 章节)也展示了类似的简单调用。call(String) 是 ChatModel 接口提供的便捷方法,内部会自动把字符串封装成 Prompt 对象。

6.2 使用 Prompt 对象:更精细的控制

当你需要获取更多信息(如 Token 用量)时,可以使用 Prompt + ChatResponse

java 复制代码
@RequestMapping("/chatByPrompt")
public String chatByPrompt(String message){
    Prompt prompt = new Prompt(message);
    ChatResponse response = chatModel.call(prompt);
    return response.getResult().getOutput().getText();
}

调用链解读

复制代码
chatModel.call(prompt)
    ↓
ChatResponse                    ← 完整响应对象
    .getResult()                ← 获取第一个生成结果(Generation)
    .getOutput()                ← 获取输出(AssistantMessage)
    .getText()                  ← 获取文本内容

官方原文 (ChatClient Responses 章节):
"The response from the AI model is a rich structure defined by the type ChatResponse. It includes metadata about how the response was generated and can also contain multiple responses, known as Generations, each with its own metadata. The metadata includes the number of tokens used to create the response."

翻译:ChatResponse 是一个丰富的结构,包含生成元数据,可能包含多个 Generation,每个都有自己的元数据(如 Token 用量)。

6.3 角色设定:System Message 与 User Message

官方原文
"The AI model processes two main types of messages: user messages, which are direct inputs from the user, and system messages, which are generated by the system to guide the conversation."

翻译:AI 模型处理两种主要消息类型:用户消息(用户的直接输入)和系统消息(系统生成的用于引导对话的指令)。

这是 AI 对话中非常重要的概念------通过 System Message 给 AI 设定"人设"

java 复制代码
@RequestMapping("/role")
public String role(String message){
    // 系统消息:设定 AI 的角色和行为
    SystemMessage systemMessage = new SystemMessage(
            "我叫小特, 是比特教育研发的一款智能AI助手, 擅长Java 和 C++ 语言");
    // 用户消息:用户的实际问题
    UserMessage userMessage = new UserMessage(message);
    // 将两种消息组合成 Prompt
    Prompt prompt = new Prompt(systemMessage, userMessage);
    ChatResponse response = chatModel.call(prompt);
    return response.getResult().getOutput().getText();
}

消息类型对照

消息类型 类名 作用 比喻
System Message SystemMessage 给 AI 设定角色、规则 导演给演员的"角色说明书"
User Message UserMessage 用户的输入/提问 观众的提问
Assistant Message AssistantMessage AI 的回复 演员的回答

6.4 流式响应:stream() 方法

官方原文 (ChatClient Responses 章节):
"The stream() method lets you get an asynchronous response."

普通的 call()同步的 ------等 AI 全部生成完才返回。stream()流式的------AI 每生成一小段就返回一小段,实现"打字机效果":

java 复制代码
@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String message){
    Prompt prompt = new Prompt(message);
    Flux<ChatResponse> response = chatModel.stream(prompt);
    return response.map(x -> x.getResult().getOutput().getText());
}

关键点

  1. produces = "text/html;charset=utf-8":设置响应类型,让浏览器能正确显示中文流式文本
  2. Flux<ChatResponse>:Spring WebFlux 的响应式类型,表示"一连串的响应片段"
  3. .map(x -> x.getResult().getOutput().getText()):从每个片段中提取文本

同步 vs 流式对比

call()(同步) stream()(流式)
返回类型 ChatResponse Flux<ChatResponse>
等待方式 等全部生成完才返回 生成一段返回一段
用户体验 等待时间长,一次性出现 像打字机一样逐字出现
适用场景 后端处理、短回复 前端展示、长回复

6.5 运行时选项(Runtime Options)

官方原文
"At run-time, you can override the default options by adding new, request-specific options to the Prompt call."

官方示例

java 复制代码
ChatResponse response = chatModel.call(
    new Prompt(
        "Generate the names of 5 famous pirates.",
        OpenAiChatOptions.builder()
            .model("gpt-4o")
            .temperature(0.4)
            .build()
    ));

和 ImageModel 一样的两层配置机制------配置文件设默认值,代码中运行时覆盖

java 复制代码
// 运行时覆盖模型和温度
ChatResponse response = chatModel.call(
    new Prompt("讲个笑话",
        OpenAiChatOptions.builder()
            .model("gpt-4o")         // 这次用 gpt-4o
            .temperature(1.2)        // 这次要更有创意
            .build()
    ));

7. ChatClient 高层用法------流式 API(官方推荐)

官方原文
"The ChatClient offers a fluent API for communicating with an AI Model."

ChatClient 是 Spring AI 官方推荐的主要使用方式,它在 ChatModel 之上提供了更多开箱即用的功能。

7.1 创建 ChatClient

官方原文
"The ChatClient is created using a ChatClient.Builder object. You can obtain an autoconfigured ChatClient.Builder instance for any ChatModel Spring Boot autoconfiguration or create one programmatically."

方式一:使用自动配置的 Builder(最简单)

官方原文
"In the most simple use case, Spring AI provides Spring Boot autoconfiguration, creating a prototype ChatClient.Builder bean for you to inject into your class."

java 复制代码
@RestController
class MyController {

    private final ChatClient chatClient;

    public MyController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/ai")
    String generation(String userInput) {
        return this.chatClient.prompt()
                .user(userInput)
                .call()
                .content();
    }
}

官方原文
"In this simple example, the user input sets the contents of the user message. The call() method sends a request to the AI model, and the content() method returns the AI model's response as a String."

方式二:在 @Configuration 中手动构建(适合需要定制的场景)

来自项目中 CommonConfiguration.java 的写法:

java 复制代码
@Configuration
public class CommonConfiguration {

    @Bean
    public ChatClient deepseekChatClient(OpenAiChatModel chatModel, ChatMemory chatMemory){
        return ChatClient.builder(chatModel)
                .defaultSystem("你叫小特, 是比特教育研发的一款智能AI助手, 擅长Java 和C++")
                .defaultAdvisors(
                    new SimpleLoggerAdvisor(),
                    new MessageChatMemoryAdvisor(chatMemory))
                .build();
    }
}

这种方式可以在构建时设置默认系统提示词Advisor聊天记忆等。

方式三:使用静态方法快速创建

java 复制代码
ChatClient chatClient = ChatClient.create(chatModel);

7.2 Fluent API 基础用法

官方原文
"The ChatClient fluent API allows you to create a prompt in three distinct ways using an overloaded prompt method."

ChatClient 提供了三种 prompt() 重载方法来启动链式调用:

java 复制代码
// 方式一:无参,后续通过链式 API 构建
chatClient.prompt()
        .user("你好")
        .call()
        .content();

// 方式二:直接传入用户文本(快捷方式)
chatClient.prompt("你好")
        .call()
        .content();

// 方式三:传入已有的 Prompt 对象
Prompt prompt = new Prompt("你好");
chatClient.prompt(prompt)
        .call()
        .content();

来自项目中 ChatClientController.java 的最简写法:

java 复制代码
@RequestMapping("/call")
public String call(String message){
    return chatClient.prompt(message).call().content();
}

一行代码搞定聊天! 这就是 Fluent API 的魅力。

7.3 多种响应类型

官方原文
"The ChatClient API offers several ways to format the response from the AI Model."

call() 之后,你可以选择不同的方式获取结果:

java 复制代码
// 1. 直接获取文本(最常用)
String text = chatClient.prompt()
        .user("讲个笑话")
        .call()
        .content();

// 2. 获取完整的 ChatResponse(包含元数据,如 Token 用量)
ChatResponse chatResponse = chatClient.prompt()
        .user("讲个笑话")
        .call()
        .chatResponse();

// 3. 直接映射为 Java 实体类(结构化输出)
record ActorFilms(String actor, List<String> movies) {}

ActorFilms result = chatClient.prompt()
        .user("推荐一个演员和他的5部电影")
        .call()
        .entity(ActorFilms.class);

官方原文 (关于 entity):
"You often want to return an entity class that is mapped from the returned String. The entity() method provides this functionality."

这是一个非常强大的功能------AI 的文本输出自动转换为 Java 对象!

call() 返回值总结

方法 返回类型 说明
.content() String 直接获取文本
.chatResponse() ChatResponse 完整响应(含元数据)
.entity(Class<T>) T 自动映射为 Java 对象
.entity(ParameterizedTypeReference<T>) T 映射为泛型类型(如 List<T>

7.4 流式响应(Streaming)

官方原文
"The stream() method lets you get an asynchronous response."

java 复制代码
Flux<String> output = chatClient.prompt()
    .user("Tell me a joke")
    .stream()
    .content();

来自项目中的写法:

java 复制代码
// ChatClientController.java
@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String message){
    return chatClient.prompt(message).stream().content();
}

// ChatController.java(带聊天记忆的流式响应)
@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String prompt, String chatId){
    return this.chatClient.prompt()
            .user(prompt)
            .advisors(spec -> spec.param(
                    AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatId))
            .stream()
            .content();
}

7.5 提示词模板(Prompt Templates)

官方原文
"The ChatClient fluent API lets you provide user and system text as templates with variables that are replaced at runtime."

java 复制代码
String answer = ChatClient.create(chatModel).prompt()
    .user(u -> u
        .text("Tell me the names of 5 movies whose soundtrack was composed by {composer}")
        .param("composer", "John Williams"))
    .call()
    .content();

提示词模板用 {变量名} 作为占位符,运行时通过 .param() 替换:

java 复制代码
String answer = chatClient.prompt()
        .user(u -> u
            .text("请用{language}语言写一个{function}函数")
            .param("language", "Java")
            .param("function", "快速排序"))
        .call()
        .content();

官方原文 (关于模板引擎):
"Internally, the ChatClient uses the PromptTemplate class to handle the user and system text and replace the variables with the values provided at runtime relying on a given TemplateRenderer implementation."

7.6 默认配置(Using Defaults)

官方原文
"Creating a ChatClient with a default system text in an @Configuration class simplifies runtime code. By setting defaults, you only need to specify the user text when calling ChatClient."

这是 ChatClient 相比 ChatModel 的一大优势------可以预设默认行为

java 复制代码
@Configuration
class Config {
    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder
                .defaultSystem("你是一个友好的聊天机器人,用海盗的口吻回答问题")
                .build();
    }
}

官方原文 还给出了带参数的默认系统文本示例:

java 复制代码
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
    return builder
        .defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
        .build();
}

运行时通过 .system(sp -> sp.param("voice", voice)) 传入参数。

来自项目中 CommonConfiguration.java 的实际写法:

java 复制代码
@Bean
public ChatClient deepseekChatClient(OpenAiChatModel chatModel, ChatMemory chatMemory){
    return ChatClient.builder(chatModel)
            .defaultSystem("你叫小特, 是比特教育研发的一款智能AI助手, 擅长Java 和C++, " +
                    "主要工作是解决学生在学习过程中遇到的一些问题")
            .defaultAdvisors(
                    new SimpleLoggerAdvisor(),
                    new MessageChatMemoryAdvisor(chatMemory))
            .build();
}

这里同时设置了:

  • 默认系统提示词:设定 AI 的角色
  • 默认 Advisor:日志记录 + 聊天记忆

7.7 Advisor 机制

官方原文
"The Advisors API provides a flexible and powerful way to intercept, modify, and enhance AI-driven interactions in your Spring applications."

翻译:Advisor API 提供了一种灵活且强大的方式来拦截、修改和增强 Spring 应用中的 AI 交互。

Advisor 是 ChatClient 的"插件"系统,你可以理解为 AI 对话的拦截器/中间件。常见的 Advisor:

Advisor 作用
SimpleLoggerAdvisor 记录请求和响应日志,方便调试
MessageChatMemoryAdvisor 自动管理聊天记忆,让 AI "记住"上下文
QuestionAnswerAdvisor RAG(检索增强生成),用自己的数据增强回答

官方原文 (关于日志 Advisor):
"The SimpleLoggerAdvisor is an advisor that logs the request and response data of the ChatClient. This can be useful for debugging and monitoring your AI interactions."

使用示例:

java 复制代码
// 在构建时设置默认 Advisor
ChatClient client = ChatClient.builder(chatModel)
        .defaultAdvisors(
            new SimpleLoggerAdvisor(),                    // 日志
            new MessageChatMemoryAdvisor(chatMemory))     // 聊天记忆
        .build();

// 或者在调用时动态添加
chatClient.prompt()
        .user("你好")
        .advisors(new SimpleLoggerAdvisor())
        .call()
        .content();

启用日志 Advisor 后,需要在配置文件中设置日志级别:

yaml 复制代码
logging:
  level:
    org.springframework.ai.chat.client.advisor: DEBUG

7.8 聊天记忆(Chat Memory)

官方原文
"The interface ChatMemory represents a storage for chat conversation memory. It provides methods to add messages to a conversation, retrieve messages from a conversation, and clear the conversation history."

翻译:ChatMemory 接口代表聊天对话记忆的存储。它提供了添加消息、检索消息和清除对话历史的方法。

为什么需要聊天记忆? 因为 AI 模型的 API 本身是无状态的------你告诉它你叫什么名字,下次请求它就忘了。

官方原文
"The chat model's API is stateless. If you tell the AI model your name, it won't remember it in subsequent interactions. Conversational history must be sent with each request."

Spring AI 通过 ChatMemory + MessageChatMemoryAdvisor 自动管理聊天历史:

java 复制代码
// 1. 创建聊天记忆(内存实现)
@Bean
public ChatMemory chatMemory(){
    return new InMemoryChatMemory();
}

// 2. 构建带记忆的 ChatClient
@Bean
public ChatClient chatClient(OpenAiChatModel chatModel, ChatMemory chatMemory){
    return ChatClient.builder(chatModel)
            .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
            .build();
}

// 3. 调用时传入会话 ID,自动管理上下文
chatClient.prompt()
        .user(prompt)
        .advisors(spec -> spec.param(
                AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatId))
        .stream()
        .content();

官方原文 (关于 MessageWindowChatMemory):
"MessageWindowChatMemory is a chat memory implementation that maintains a window of messages up to a specified maximum size (default: 20 messages). When the number of messages exceeds this limit, older messages are evicted, but system messages are preserved."

存储实现选择:

实现 说明
InMemoryChatMemoryRepository 内存存储,重启丢失
JdbcChatMemoryRepository 数据库存储(JDBC)
CassandraChatMemoryRepository Cassandra 存储
Neo4jChatMemoryRepository Neo4j 图数据库存储

8. ChatModel vs ChatClient:如何选择?

对比维度 ChatModel(底层) ChatClient(高层)
API 风格 传统的方法调用 Fluent API 链式调用
代码量 需要手动构建 Prompt、解析 Response 一行链式调用搞定
系统消息 手动创建 SystemMessage 并组装 .defaultSystem() 预设
聊天记忆 需要自己管理历史消息 通过 Advisor 自动管理
结构化输出 手动解析 AI 返回文本 .entity(Class) 自动映射
日志/拦截 需要自己实现 Advisor 机制开箱即用
适用场景 简单场景、快速测试 生产项目、复杂交互

结论

  • 快速测试/简单调用 → 用 ChatModel,一行 chatModel.call("你好") 搞定
  • 正式项目/复杂需求 → 用 ChatClient,功能更强大、代码更优雅

9. 官方示例控制器解读

官方原文(来自 openai-chat.html Sample Controller)提供了一个示例:

java 复制代码
@RestController
public class ChatController {

    private final OpenAiChatModel chatModel;

    @Autowired
    public ChatController(OpenAiChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @GetMapping("/ai/generate")
    public Map<String,String> generate(@RequestParam(value = "message",
            defaultValue = "Tell me a joke") String message) {
        return Map.of("generation", this.chatModel.call(message));
    }

    @GetMapping("/ai/generateStream")
    public Flux<ChatResponse> generateStream(@RequestParam(value = "message",
            defaultValue = "Tell me a joke") String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        return this.chatModel.stream(prompt);
    }
}

解读要点

  1. 注入 OpenAiChatModel:通过构造器注入,由 Spring Boot 自动配置提供
  2. 同步调用 call(message):直接传字符串,返回字符串,包装成 JSON 返回
  3. 流式调用 stream(prompt) :使用 UserMessage 构建 Prompt,返回 Flux<ChatResponse> 实现流式输出

10. 实战进阶:结合项目代码讲解

10.1 最简单的 ChatModel 调用

来自 OllamaController.java,展示了 ChatModel 的两种基础用法:

java 复制代码
@Autowired
private OllamaChatModel chatModel;

// 同步调用
@RequestMapping("/chat")
public String chat(String message){
    return chatModel.call(message);
}

// 流式调用
@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String message){
    return chatModel.stream(message);
}

注意这里用的是 OllamaChatModel(本地模型),但 API 和 OpenAiChatModel 完全一样------这就是面向接口编程的好处。

10.2 ChatClient 的极简调用

来自 ChatClientController.java

java 复制代码
private final ChatClient chatClient;

public ChatClientController(ChatClient chatClient) {
    this.chatClient = chatClient;
}

@RequestMapping("/call")
public String call(String message){
    return chatClient.prompt(message).call().content();
}

@RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String message){
    return chatClient.prompt(message).stream().content();
}

对比 ChatModel 版本,ChatClient 的代码更简洁------一行链式调用即可。

10.3 带角色设定和聊天记忆的完整示例

来自 CommonConfiguration.java + ChatController.java(spring-chat-bot 模块):

配置类

java 复制代码
@Configuration
public class CommonConfiguration {
    @Bean
    public ChatMemory chatMemory(){
        return new InMemoryChatMemory();
    }

    @Bean
    public ChatClient deepseekChatClient(OpenAiChatModel chatModel, ChatMemory chatMemory){
        return ChatClient.builder(chatModel)
                .defaultSystem("你叫小特, 是比特教育研发的一款智能AI助手, " +
                        "擅长Java 和C++, 主要工作是解决学生在学习过程中遇到的一些问题")
                .defaultAdvisors(
                        new SimpleLoggerAdvisor(),
                        new MessageChatMemoryAdvisor(chatMemory))
                .build();
    }
}

控制器

java 复制代码
@RestController
@RequestMapping("/chat")
public class ChatController {
    private final ChatClient chatClient;

    public ChatController(ChatClient deepseekChatClient) {
        this.chatClient = deepseekChatClient;
    }

    @RequestMapping(value = "/stream", produces = "text/html;charset=utf-8")
    public Flux<String> stream(String prompt, String chatId){
        return this.chatClient.prompt()
                .user(prompt)
                .advisors(spec -> spec.param(
                        AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatId))
                .stream()
                .content();
    }
}

这段代码做了什么?

  1. defaultSystem(...) --- 设定 AI 的角色:"小特"智能助手
  2. new SimpleLoggerAdvisor() --- 自动记录每次对话日志
  3. new MessageChatMemoryAdvisor(chatMemory) --- 自动管理聊天记忆
  4. .advisors(spec -> spec.param(..., chatId)) --- 通过 chatId 区分不同用户的对话
  5. .stream().content() --- 流式返回 AI 的回复

这是一个生产级别的聊天机器人的核心代码,包含了角色设定、聊天记忆、流式响应等关键功能。

10.4 带工具调用(Tool Calling)的 ChatClient

来自 ChatController.java(spring-alibaba-demo 模块):

java 复制代码
public ChatController(DashScopeChatModel chatModel, ToolCallback weatherTool) {
    this.chatClient = ChatClient.builder(chatModel)
            .defaultTools(new DateTimeTools())       // 声明式工具
            .defaultToolCallbacks(weatherTool)       // 编程式工具
            .build();
}

@RequestMapping("/call")
public String call(String message){
    return chatClient.prompt()
            .user(message)
            .toolContext(Map.of("chatId", "chatId-366465788689797"))
            .call()
            .content();
}

官方原文 (Function Calling 章节):
"You can register custom Java functions with the OpenAiChatModel and have the OpenAI model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions."

工具调用让 AI 可以调用你定义的 Java 函数------比如查天气、查时间、查数据库等。


11. 常见问题与排错指南

Q1:ChatModel 和 ChatClient 可以同时用吗?

可以。它们不冲突。ChatClient 底层还是调用 ChatModel。简单场景用 ChatModel,复杂场景用 ChatClient。

Q2:流式响应在浏览器中显示乱码?

原因 :未设置正确的响应类型。
解决 :在 @RequestMapping 中添加 produces = "text/html;charset=utf-8"

Q3:AI 不记得之前说的话?

原因 :Chat API 是无状态的(官方文档明确说明)。
解决 :使用 ChatMemory + MessageChatMemoryAdvisor,参见第 7.8 节。

Q4:如何切换到 DeepSeek / 通义千问等国产模型?

只需修改 base-urlapi-key(兼容 OpenAI 协议的模型),或者使用对应的 Starter(如 spring-ai-alibaba)。

Q5:temperature 和 topP 应该怎么调?

官方建议不要同时修改两者。一般只调 temperature 即可,参见第 5.3 节的温度说明表。

Q6:如何查看每次请求花了多少 Token?

使用 ChatResponse 获取元数据:

java 复制代码
ChatResponse response = chatClient.prompt()
        .user("你好")
        .call()
        .chatResponse();
// response 中包含 Token 用量等元数据

12. 总结

本文沿着官方文档的脉络,将 ChatClient API 和 OpenAI Chat 两篇文档融合解读:

官方文档章节 本文对应 核心要点
Prerequisites 第 3 章 获取 API Key,通过环境变量安全配置
Auto-configuration 第 4 章 一个 Starter 依赖自动注入 ChatModel 和 ChatClient.Builder
Chat Properties 第 5 章 连接、重试、聊天模型三类属性,重点理解 temperature
Runtime Options 第 6.5 节 配置文件设默认 + 代码运行时覆盖
ChatClient Fluent API 第 7 章 链式调用、多种响应类型、流式响应
Using Defaults 第 7.6 节 在 @Configuration 中预设系统消息和 Advisor
Advisors 第 7.7 节 日志、聊天记忆、RAG 等"插件"机制
Chat Memory 第 7.8 节 让 AI "记住"上下文的关键能力
Sample Controller 第 9 章 官方示例逐行解读

核心认知

  • ChatModel 是底层引擎,简单直接,适合快速测试
  • ChatClient 是高层方向盘,功能强大,适合正式项目
  • 两者共享同一套配置属性和 Spring Boot 自动配置
  • Spring AI 的统一设计模式:构建 Options → 构建 Prompt → 调用 call()/stream() → 处理 Response

13. 参考资料

Spring AI 官方文档

OpenAI 官方文档

其他

相关推荐
CBeann12 小时前
企业级规则引擎落地实战:动态脚本引擎 QLExpress ,真香!
java·ai·大模型·规则引擎·qlexpress·大厂实战项目
懈尘12 小时前
从 Java 1.7 到 Java 21:逐版本深入解析新特性与平台演进
java·开发语言
亓才孓12 小时前
[Maven]Maven基础
java·maven
hello 早上好12 小时前
05_Java 类加载过程
java·开发语言
是店小二呀12 小时前
CANN 异构计算的极限扩展:从算子融合到多卡通信的统一优化策略
人工智能·深度学习·transformer
冻感糕人~12 小时前
收藏备用|小白&程序员必看!AI Agent入门详解(附工业落地实操关联)
大数据·人工智能·架构·大模型·agent·ai大模型·大模型学习
echoVic12 小时前
多模型支持的架构设计:如何集成 10+ AI 模型
java·javascript
橙露12 小时前
Java并发编程进阶:线程池原理、参数配置与死锁避免实战
java·开发语言