Spring Boot 3 + Spring AI 实战:十分钟集成 OpenAI API 构建智能应用
-
- [🚀 前言:为什么选择 Spring AI?](#🚀 前言:为什么选择 Spring AI?)
- [🧠 一、Spring AI 核心架构与原理](#🧠 一、Spring AI 核心架构与原理)
-
- [1.1 整体架构原理图](#1.1 整体架构原理图)
- [1.2 AI 交互流程图](#1.2 AI 交互流程图)
- [⚙️ 二、环境准备与项目初始化](#⚙️ 二、环境准备与项目初始化)
-
- [2.1 技术栈要求](#2.1 技术栈要求)
- [2.2 Maven 依赖配置](#2.2 Maven 依赖配置)
- [2.3 配置文件设置](#2.3 配置文件设置)
- [💬 三、快速入门:你的第一个 AI 对话机器人](#💬 三、快速入门:你的第一个 AI 对话机器人)
-
- [3.1 创建 Service 层](#3.1 创建 Service 层)
- [3.2 创建 Controller 层](#3.2 创建 Controller 层)
- [🚀 四、进阶实战:提示词工程与流式响应](#🚀 四、进阶实战:提示词工程与流式响应)
-
- [4.1 提示词模板](#4.1 提示词模板)
- [4.2 流式响应](#4.2 流式响应)
- [🔍 五、核心应用:RAG(检索增强生成)实战](#🔍 五、核心应用:RAG(检索增强生成)实战)
-
- [5.2 Spring AI 实现 RAG](#5.2 Spring AI 实现 RAG)
- [🛠️ 六、Function Calling:赋予 AI 调用 Java 方法的能力](#🛠️ 六、Function Calling:赋予 AI 调用 Java 方法的能力)
-
- [6.1 Function Calling 流程图](#6.1 Function Calling 流程图)
- [6.2 实战:查询天气机器人](#6.2 实战:查询天气机器人)
- [🛡️ 七、生产环境最佳实践](#🛡️ 七、生产环境最佳实践)
-
- [7.1 安全性](#7.1 安全性)
- [7.2 Token 计费控制](#7.2 Token 计费控制)
- [7.3 异常处理](#7.3 异常处理)
- [7.4 性能优化](#7.4 性能优化)
- [🎯 八、总结](#🎯 八、总结)
摘要:随着生成式 AI 的爆发,Java 开发者不再需要羡慕 Python 的丰富生态。Spring AI 作为 Spring 家族的新成员,为 Java 生态带来了类似 LangChain 的标准化抽象。本文将基于 Spring Boot 3,深入剖析 Spring AI 的核心架构,通过实战案例带你十分钟集成 OpenAI API,构建具备流式输出、提示词模板甚至 RAG(检索增强生成)能力的智能应用。
🚀 前言:为什么选择 Spring AI?
在过去的一年里,如果你想用 Java 开发 AI 应用,可能需要直接调用 OpenAI 的 HTTP 接口,或者移植 Python 的 LangChain 库。这不仅繁琐,而且缺乏 Java 生态的"味道"。
Spring AI 的诞生改变了这一切。它并非 LangChain 的简单移植,而是遵循 Spring 设计哲学(可移植性、模块化、面向接口)构建的 AI 应用开发框架。它提供了以下核心价值:
- API 抽象:一套代码支持 OpenAI、Azure OpenAI、HuggingFace 等多种模型提供商。
- 同步与流式:原生支持流式响应,不再需要手动处理 Server-Sent Events (SSE)。
- Spring Boot 集成:自动配置、外部化配置,开箱即用。
🧠 一、Spring AI 核心架构与原理
在写代码之前,理解 Spring AI 的架构设计至关重要,这有助于我们理解它的扩展性。
1.1 整体架构原理图
Spring AI 将 AI 模型交互抽象为多个层次,从底层的 HTTP 通信到上层的 POJO 转换。
基础设施与外部服务
模型提供商适配层
Spring AI 抽象层
应用层
Spring Boot Application
Chat Client / Image Client
Prompt Templates
Output Parser
Embedding Model
OpenAI Adapter
Azure Adapter
HuggingFace Adapter
OpenAI API
Vector Database
e.g. Redis/PGVector
核心组件解析:
- Client 层 :最顶层的入口,如
ChatClient,开发者通过它与 AI 交互。 - Prompt 层:负责将用户输入、系统指令和变量组装成 AI 能理解的格式。
- Model 层:具体的模型实现,负责将 Prompt 发送给 API 并解析响应。
1.2 AI 交互流程图
当我们调用 chatClient.call() 时,Spring AI 内部发生了什么?
Converter OpenAI HTTP API Converter Jackson/JSON Prompt Template ChatClient Spring Boot App Converter OpenAI HTTP API Converter Jackson/JSON Prompt Template ChatClient Spring Boot App .prompt().user("...").call() 组装 Prompt (System/User) String / ChatRequest 序列化为 JSON JSON Body HTTP POST (/v1/chat/completions) JSON Response 反序列化为 ChatResponse ChatResponse 对象 返回 Entity / String
⚙️ 二、环境准备与项目初始化
2.1 技术栈要求
硬性要求:
- JDK 17+:Spring Boot 3 强制要求。
- Spring Boot 3.2.x+:确保兼容性。
- OpenAI API Key:你需要一个密钥。
2.2 Maven 依赖配置
Spring AI 的版本更新较快,建议使用 BOM(Bill of Materials)管理版本。
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M2</version> <!-- 请检查最新版本 -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring AI OpenAI Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
注意:由于 Spring AI 尚未放入 Maven 中央仓库的 release 版本,你需要确保添加了 Spring Milestone 或 Snapshot 仓库。
xml
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2.3 配置文件设置
在 application.yml 中配置 API Key 和基础选项:
yaml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY} # 建议使用环境变量
chat:
options:
model: gpt-4-turbo # 或 gpt-3.5-turbo
temperature: 0.7 # 控制随机性,0.0-2.0
💬 三、快速入门:你的第一个 AI 对话机器人
我们将构建一个简单的 REST API,接收用户问题并返回 AI 回答。
3.1 创建 Service 层
java
@Service
public class ChatService {
private final ChatClient chatClient;
public ChatService(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
public String chat(String message) {
// 简单调用
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
代码解析:
ChatClient.Builder:Spring Boot 自动配置注入的构建器。.prompt():开始构建对话请求。.user():添加用户消息。.call():同步阻塞调用(等待完整响应)。.content():提取返回文本内容。
3.2 创建 Controller 层
java
@RestController
@RequestMapping("/api/ai")
public class ChatController {
private final ChatService chatService;
public ChatController(ChatService chatService) {
this.chatService = chatService;
}
@GetMapping("/chat")
public Map<String, String> chat(@RequestParam String message) {
String response = chatService.chat(message);
return Map.of("answer", response);
}
}
运行项目,访问 http://localhost:8080/api/ai/chat?message=Hello%20Spring%20AI,你将看到 AI 的回复。
🚀 四、进阶实战:提示词工程与流式响应
简单的字符串拼接无法胜任复杂业务。我们需要提示词模板 来管理上下文,并使用流式传输来提升用户体验。
4.1 提示词模板
假设我们要做一个"写诗助手",需要固定的角色设定。
创建 prompts/poet-template.st 文件(放在 src/main/resources 下):
text
你是一位古代诗人,擅长唐诗宋词。
请根据以下主题:{topic},创作一首七言绝句。
要求:
1. 韵律工整
2. 意境优美
3. 字数严格
Service 代码升级:
java
@Service
public class PoetService {
private final ChatClient chatClient;
// Spring AI 自动加载 resources 目录下的 .st 文件
public PoetService(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder
.defaultSystem("你是一位专业的文学创作助手") // 默认系统提示
.build();
}
public String writePoem(String topic) {
return chatClient.prompt()
.user(userSpec -> userSpec
.text("请根据主题:{topic} 写一首诗") // 内联模板
.param("topic", topic)
)
.call()
.content();
}
}
4.2 流式响应
流式响应让用户感觉像是在实时打字,而不是等待几秒钟后一次性蹦出一段文字。这在 Web 界面中尤为重要。
流式接口原理图:
SSE Request
Flux
Async HTTP
Chunks
Chunks
SSE Events
Client
Spring Controller
ChatClient
OpenAI API
代码实现:
Spring AI 的 stream() 方法返回的是 Reactor 的 Flux<String>,这可以完美适配 Spring WebFlux 或者 Spring MVC 的流式响应。
java
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.stream() // 关键:开启流式
.content(); // 返回 Flux<String>
}
前端测试体验:
使用 curl 测试可以看到数据一行行吐出:
bash
curl -N http://localhost:8080/api/ai/stream?message=讲个笑话
🔍 五、核心应用:RAG(检索增强生成)实战
大模型有两个致命弱点:知识幻觉 (瞎编乱造)和知识截止日期 (不知道今天发生的事)。
RAG (Retrieval-Augmented Generation) 是解决这些问题的银弹:先去外部知识库检索相关信息,然后扔给 LLM 让它基于这些信息回答。
5.2 Spring AI 实现 RAG
我们需要 EmbeddingModel(将文本转向量)和 VectorStore(存向量)。为了简化演示,我们使用基于内存的向量存储(生产环境建议用 PgVector 或 Milvus)。
添加依赖:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-transformers-spring-boot-starter</artifactId>
</dependency>
<!-- 注意:Transformers 比较重,适合本地演示。生产环境建议用 OpenAI 的 Embedding API -->
RAG Service 代码:
java
@Service
public class RagService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
public RagService(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) {
this.chatClient = chatClientBuilder.build();
this.vectorStore = vectorStore;
// 初始化时加载一些文档
loadKnowledgeBase();
}
private void loadKnowledgeBase() {
List<Document> documents = List.of(
new Document("Spring Boot 3 最低要求 JDK 17。"),
new Document("Spring AI 支持函数调用,可以连接外部 API。"),
new Document("GraalVM 可以将 Java 应用编译为本地镜像,实现毫秒级启动。")
);
vectorStore.add(documents);
}
public String ask(String question) {
// 1. 检索相关文档
List<Document> similarDocs = vectorStore.similaritySearch(SearchRequest.query(question).withTopK(2));
// 2. 构建包含上下文的 Prompt
String context = similarDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n"));
String systemPrompt = """
你是一个助手。请仅使用以下提供的上下文信息来回答用户的问题。
如果上下文中不包含答案,请说"我不知道"。
上下文:
{context}
""";
// 3. 发送给 LLM
return chatClient.prompt()
.system(systemPrompt)
.user(question)
.call()
.content();
}
}
关键点解析:
- VectorStore :Spring AI 抽象了向量数据库接口。我们调用
similaritySearch即可找到语义最相近的文本片段。 - Prompt 注入:我们将检索到的文档塞进 System Prompt,这是 RAG 成功的关键。
🛠️ 六、Function Calling:赋予 AI 调用 Java 方法的能力
这是 AI 应用最强大的功能之一。AI 不再只是"聊天机器人",它可以理解意图并调用后端业务逻辑,比如"查询天气"、"下单"。
6.1 Function Calling 流程图
Java Function Spring AI LLM 用户 Java Function Spring AI LLM 用户 帮我查询北京现在的天气 识别意图 ->> 需要调用 getWeather(city) 返回 Function Call 指令 执行 getWeather("Beijing") 返回 "晴, 25度" 将结果作为上下文再次请求 北京现在天气晴朗,气温25度
6.2 实战:查询天气机器人
步骤 1:定义 Java Function
java
public record WeatherRequest(String city) {}
public record WeatherResponse(String city, double temperature, String condition) {}
@Component
public class WeatherService {
@Bean
public Function<WeatherRequest, WeatherResponse> weatherFunction() {
return request -> {
// 模拟调用第三方天气 API
if ("Beijing".equalsIgnoreCase(request.city())) {
return new WeatherResponse("Beijing", 25.5, "Sunny");
}
return new WeatherResponse(request.city(), 0, "Unknown");
};
}
}
步骤 2:启用 Function Calling
java
String response = chatClient.prompt()
.user("北京现在天气怎么样?")
.functions("weatherFunction") // 指定 Bean 名称
.call()
.content();
原理: Spring AI 会自动扫描容器中注册的 Function Bean,并在调用 OpenAI 时将函数的 JSON Schema 发送给模型。如果模型判断需要调用该函数,它会暂停生成,返回一个工具调用指令,Spring AI 拦截该指令,执行 Java 方法,将结果再次喂给模型,模型最后组织语言回复用户。
🛡️ 七、生产环境最佳实践
将 Demo 级应用投入生产,需要注意以下几点:
7.1 安全性
绝对不要将 API Key 提交到 Git。
-
方案 A :使用环境变量(推荐)。
bashexport OPENAI_API_KEY=sk-xxxx... -
方案 B:使用 Spring Cloud Config 或 Vault。
7.2 Token 计费控制
LLM 按输入和输出的 Token 计费。为了防止用户滥用导致账单爆炸:
-
限制 Max Tokens :
javachatClient.prompt() .options(OpenAiChatOptions.builder().withMaxTokens(500).build()) ... -
输入长度截断:在发送给 AI 前,截断过长的 Prompt。
7.3 异常处理
网络波动或 OpenAI 服务不可用是常态。
java
try {
return chatClient.prompt().user(msg).call().content();
} catch (OpenAiApiException e) {
if (e.getStatusCode().is5xxServerError()) {
// 服务端错误,重试
return retryLogic(msg);
}
// 其他错误
return "抱歉,AI 服务暂时不可用。";
}
7.4 性能优化
- 连接池配置:Spring AI 底层使用 WebClient 或 RestTemplate,确保配置了适当的连接池大小。
- 缓存:对于常见问题(如 FAQ),可以使用 Redis 缓存 AI 的回答,直接返回,避免重复计费。
🎯 八、总结
通过 Spring Boot 3 和 Spring AI,Java 开发者现在拥有了构建企业级 AI 应用的强力武器。本文涵盖了从基础架构、简单对话、流式交互到复杂的 RAG 和 Function Calling 的完整链路。
技术演进路径:
手工 HTTP 调用 ➡️ Spring AI 标准化 Client ➡️ RAG + Function Calling 的智能 Agent
Spring AI 的优势在于它完美融合了 Spring 生态的非侵入式设计理念,让 Java 开发者能专注于业务逻辑,而不是繁琐的 HTTP 编解码。未来的 AI 应用开发,将不仅仅是算法模型的比拼,更是工程化落地能力的较量。
现在,打开你的 IDE,开始构建你的第一个 Spring AI 应用吧!