【Spring AI 实战】二、 5 分钟上手 ChatClient,对话模型调用就这么简单
【Spring AI 实战】二、5 分钟上手 ChatClient,对话模型调用就这么简单
作者:Spring AI 系列专题 | 更新时间:2025-04
所属阶段:第一阶段·核心基础
前置知识:建议先阅读第一篇,了解 Spring AI 的整体定位与模型抽象。
适用版本:本文保留 Spring AI 1.0.0-M4 的入门写法;如果你使用 1.0 正式版,优先采用
spring-ai-openai-spring-boot-starter等 starter 依赖。本文导航
1. 前置条件
1.1 环境要求
- JDK 17+(Spring AI 要求)
- Spring Boot 3.2+
- 可用的 OpenAI API Key(或任何支持的模型 API Key)
1.2 项目创建
推荐使用 Spring Initializr 创建项目,勾选:
✅ Spring Web
✅ Spring Boot DevTools
然后在 pom.xml 中加入:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
1.3 配置 API Key
方式一:环境变量(推荐)
export OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
方式二:application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY:sk-your-key-here}
base-url: https://api.openai.com
chat:
options:
model: gpt-4o-mini
temperature: 0.7
⚠️ 生产环境务必使用环境变量或 Vault,不要将 API Key 硬编码在配置文件中。
2. ChatClient 核心 API 详解
2.1 ChatClient 是什么
ChatClient 是 Spring AI 1.0 推出的全新对话 API,设计灵感来自 RestClient 和 JdbcTemplate,特点是:流式 API + Builder 模式 + 类型安全。
// 使用方式极其简洁
chatClient.prompt()
.user("你好")
.call()
.content();
2.2 核心组件

| 组件 | 说明 |
|------|------|
| ChatClient | 主入口,通过 prompt().user().call() 链式调用 |
| Prompt | 对话提示词,包含 user message / system message |
| UserPrompt | 用户消息,构建 .user() |
| SystemPrompt | 系统提示词,构建 .system() |
| ChatResponse | 响应对象,包含 content、metadata 等 |
2.3 工作流程
Application Code
│
▼
chatClient.prompt()
│
▼
Prompt(user message, system message, ...)
│
▼
[OpenAiChatModel / AnthropicChatModel / ...] ← 自动注入的模型实现
│
▼
HTTP POST to AI Provider API
│
▼
ChatResponse
│
▼
.content() / .entity() / .document()
3. 四种调用方式实战

3.1 同步调用(最常用)
@Service
public class AiService {
private final ChatClient chatClient;
public AiService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
/**
* 最简单的同步调用
*/
public String ask(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
/**
* 带系统提示词的同步调用
*/
public String askWithContext(String question) {
return chatClient.prompt()
.system("你是一位资深的 Java 后端工程师,用简洁专业的语言回答问题。")
.user(question)
.call()
.content();
}
}
3.2 流式调用(适合实时输出)
流式调用的核心优势:首 token 延迟低,用户体验好,适合 AI 助手类应用。
/**
* 流式调用 - 返回 Flux<分段内容>
*/
public Flux<String> askStream(String question) {
return chatClient.prompt()
.user(question)
.stream()
.content();
}
/**
* 在 Controller 中返回流式响应(SSE)
*/
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestParam String question) {
return aiService.askStream(question);
}
实际效果:AI 是一边"思考"一边输出字符,而不是等全部生成完再一次性返回。
// 后端推送到前端的 SSE 格式
/*
data: 你好
data: ,
data: 我
data: 是
data: Spring
data: AI
data: 。
data: [DONE]
*/
3.3 带结构化参数调用
/**
* 控制模型的温度(创造性)和最大 token 数
*/
public String askWithOptions(String question) {
return chatClient.prompt()
.user(question)
.options(
ChatOptionsBuilder.builder()
.withTemperature(0.3) // 降低随机性,更确定性
.withMaxTokens(500) // 限制输出长度
.withTopP(0.9) // 控制采样范围
.build()
)
.call()
.content();
}
常见参数说明:
| 参数 | 范围 | 说明 |
|------|------|------|
| temperature | 0.0~2.0 | 随机性,越低越确定性,默认 0.7 |
| maxTokens | 1~4096 | 最大生成 token 数,限制响应长度 |
| topP | 0.0~1.0 | 核采样,控制输出多样性 |
| frequencyPenalty | -2.0~2.0 | 频率惩罚,减少重复 |
| presencePenalty | -2.0~2.0 | 在场惩罚,鼓励话题扩展 |
3.4 返回结构化对象(强类型响应)
Spring AI 支持让模型直接返回 Java 对象,而不只是字符串:
// 定义响应结构
public record WeatherInfo(String city, String condition, double temperature) {}
// 服务层
public WeatherInfo getWeather(String city) {
return chatClient.prompt()
.user("请查询" + city + "今天的天气,用 JSON 格式返回,包含 city、condition、temperature 字段")
.call()
.entity(WeatherInfo.class); // 自动解析为 Java 对象
}
// 输出
// WeatherInfo[city=北京, condition=晴, temperature=23.5]
配合 @JsonProperty 可以映射更复杂的结构:
public record ApiResponse<T>(
int code,
String message,
T data
) {}
public ApiResponse<UserProfile> getUserProfile(Long userId) {
return chatClient.prompt()
.user("查询用户 ID=" + userId + " 的个人信息")
.call()
.entity(new ParameterizedTypeReference<ApiResponse<UserProfile>>() {});
}
4. 多轮对话实现
4.1 简单的内存级对话
@Service
public class ChatSessionService {
private final Map<String, List<Message>> sessions = new ConcurrentHashMap<>();
private final ChatClient chatClient;
public ChatSessionService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
/**
* 多轮对话:每次携带历史消息
*/
public String chat(String sessionId, String userMessage) {
// 获取或创建会话历史
List<Message> history = sessions.computeIfAbsent(
sessionId, k -> new ArrayList<>()
);
// 构建 Prompt(携带历史)
Prompt prompt = new Prompt(
MessageBuilder.createMessage(
MessageType.USER,
userMessage,
new MediaContent(MediaType.TEXT_PLAIN, userMessage)
)
);
// 如果有系统提示词,先加进去
if (history.isEmpty()) {
prompt.getOptions().setSystem("你是智能助手小 Spring。");
}
// 实际使用 Advisors 更优雅(见下节),这里演示手动方式
return chatClient.prompt(prompt).call().content();
}
}
4.2 使用 Advisors 实现对话记忆(推荐)
Advisors 是 Spring AI 的拦截器链,用法类似 Spring AOP,是实现对话记忆的标准方式:
@Service
public class AdvisorChatService {
private final ChatClient chatClient;
// 默认开启 20 轮对话记忆
public AdvisorChatService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是一位乐于助人的 Java 技术顾问")
.build();
}
/**
* 基于 Advisors 的多轮对话(自动管理历史)
*/
public String chatWithMemory(String sessionId, String question) {
return chatClient.prompt()
.user(question)
.advisors(
// 对话记忆 Advisors:自动保存并注入历史消息
MessageChatAdvisor.builder()
.chatMemory(
new InMemoryChatMemory() // 内存存储,生产环境换 Redis
)
.sessionId(sessionId) // 按会话 ID 隔离历史
.build()
)
.call()
.content();
}
}
MessageChatAdvisor 的工作原理:
用户: 第一个问题
↓
[MessageChatAdvisor] ← InMemoryChatMemory.getHistory(sessionId) → []
↓
调用模型,携带历史 []
↓
模型回复: 第一个回答
↓
[MessageChatAdvisor] ← InMemoryChatMemory.add(sessionId, user+assistant)
↓
用户: 第二个问题(上下文相关)
↓
[MessageChatAdvisor] → InMemoryChatMemory.getHistory(sessionId) → [Q1, A1]
↓
调用模型,携带历史 [Q1, A1, Q2]
5. 系统提示词与参数控制

5.1 全局系统提示词
// 在构建 ChatClient 时设置全局系统提示词
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("你是一位专业的 Java 后端开发专家," +
"擅长 Spring Boot、Spring Cloud、Spring AI," +
"回答问题时优先给出代码示例," +
"用【技术要点】【代码示例】【运行说明】三段式结构回答。")
.build();
}
5.2 每次调用的局部系统提示词
// 覆盖默认系统提示词
String reply = chatClient.prompt()
.system(
SystemPromptTemplate.from(
"你是{role},用{style}风格回答用户问题。用户所在地:{location}"
)
.render(Map.of(
"role", "宠物医生",
"style", "专业但通俗易懂",
"location", "北京"
))
)
.user("我的猫最近食欲不振怎么办?")
.call()
.content();
5.3 PromptTemplate 模板
// 定义一个 Prompt 模板
PromptTemplate promptTemplate = PromptTemplate.from(
"请为以下{language}代码添加中文注释,并说明每段逻辑的作用:\n```\n{code}\n```"
);
// 渲染模板
Prompt prompt = promptTemplate.render(
Map.of(
"language", "Java",
"code", "public class Demo { public static void main(String[] args) { System.out.println(\"Hello\"); } }"
)
);
String result = chatClient.prompt(prompt).call().content();
6. 异常处理
6.1 Spring AI 异常体系
// 异常继承结构
SpringAIException (Root)
├── RateLimitException // 速率限制
├── BadRequestException // 参数错误(400)
├── AuthenticationException // 认证失败(401)
├── AuthorizationException // 权限不足(403)
├── ResourceNotFoundException // 资源不存在(404)
└── AIException // AI 模型通用错误
6.2 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RateLimitException.class)
public ResponseEntity<Map<String, Object>> handleRateLimit(RateLimitException ex) {
return ResponseEntity
.status(HttpStatus.TOO_MANY_REQUESTS)
.body(Map.of(
"error", "请求过于频繁,请稍后再试",
"retryAfter", 30
));
}
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<Map<String, String>> handleAuth(AuthenticationException ex) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(Map.of("error", "API Key 无效或已过期"));
}
@ExceptionHandler(AIException.class)
public ResponseEntity<Map<String, String>> handleAI(AIException ex) {
log.error("AI 调用异常", ex);
return ResponseEntity
.status(HttpStatus.BAD_GATEWAY)
.body(Map.of("error", "AI 服务暂时不可用,请稍后重试"));
}
}
7. 完整示例:从配置到 Controller
7.1 配置类
@Configuration
public class ChatConfig {
@Value("${spring.ai.openai.api-key}")
private String apiKey;
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("你是 AI 助手,请专业、简洁地回答用户问题。")
.build();
}
}
7.2 服务类
@Service
@RequiredArgsConstructor
public class AiChatService {
private final ChatClient chatClient;
public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
public Flux<String> chatStream(String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
}
7.3 Controller
@RestController
@RequestMapping("/api/ai")
@RequiredArgsConstructor
public class AiController {
private final AiChatService aiChatService;
/**
* 同步对话
*/
@GetMapping("/chat")
public ResponseEntity<Map<String, String>> chat(@RequestParam String message) {
if (message == null || message.isBlank()) {
return ResponseEntity.badRequest()
.body(Map.of("error", "消息不能为空"));
}
String reply = aiChatService.chat(message);
return ResponseEntity.ok(Map.of(
"reply", reply,
"timestamp", String.valueOf(System.currentTimeMillis())
));
}
/**
* 流式对话(SSE)
*/
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestParam String message) {
return aiChatService.chatStream(message);
}
}
7.4 application.yml
spring:
application:
name: spring-ai-demo
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com
chat:
options:
model: gpt-4o-mini
temperature: 0.7
server:
port: 8080
7.5 启动测试
# 测试同步接口
curl "http://localhost:8080/api/ai/chat?message=Spring+AI+是什么"
# 测试流式接口
curl -N "http://localhost:8080/api/ai/chat/stream?message=用三句话介绍Java"
8. 常见问题排查
| 问题 | 原因 | 解决方案 |
|------|------|---------|
| 401 Unauthorized | API Key 错误或过期 | 检查 OPENAI_API_KEY 环境变量 |
| 429 Too Many Requests | 请求频率超限 | 添加限流或等待重试 |
| SocketTimeoutException | 网络超时 | 增大超时时间或检查网络 |
| 返回空字符串 | 模型未正确配置 | 检查 base-url 和 model |
| 流式无响应 | 前端未正确解析 SSE | 检查 Accept: text/event-stream |
| 响应乱码 | 编码问题 | 确保 UTF-8 配置正确 |
9. 小结
本文围绕 ChatClient 做了全面深入的实战讲解:
- 四种调用方式:同步调用、流式调用、带参数调用、结构化对象返回
- 多轮对话:手动管理历史 vs Advisors 自动管理(推荐)
- 系统提示词:全局默认 + 局部覆盖 + 模板渲染
- 异常处理:Spring AI 完整异常体系与全局处理
- 完整示例:从配置 → 服务类 → Controller → 测试
下一篇预告:【Spring AI 实战】三、Prompt 工程:模板化、结构化输出与 Advisors 顾问模式——我们将深入 Prompt 工程的核心技巧,包括 Few-Shot 提示、Chain of Thought 推理、Advisors 拦截器链的高级用法,以及如何引导模型输出严格符合预期的 JSON 结构。
📌 系列导航
📎 示例说明:本文以入门链路为主,若你准备做生产级接入,建议继续阅读第三篇和第四篇。
更多推荐




所有评论(0)