文章目录
- [Spring AI技术详细解析:Spring生态AI应用开发框架与生产级实践](#Spring AI技术详细解析:Spring生态AI应用开发框架与生产级实践)
-
- 前言
- [一、Spring AI 核心优势](#一、Spring AI 核心优势)
- [二、Spring AI 生产级目录结构](#二、Spring AI 生产级目录结构)
- [三、Spring AI 核心技术解析](#三、Spring AI 核心技术解析)
-
- [1. 快速入门:Spring Boot 集成 OpenAI 实现对话](#1. 快速入门:Spring Boot 集成 OpenAI 实现对话)
-
- 步骤1:添加依赖(pom.xml)
- [步骤2:配置 OpenAI 密钥(application-dev.yml)](#步骤2:配置 OpenAI 密钥(application-dev.yml))
- [步骤3:实现对话 Service(ChatService.java)](#步骤3:实现对话 Service(ChatService.java))
- 步骤4:实现对话接口(ChatController.java)
- 步骤5:请求模型(ChatReq.java)与响应模型(ChatResp.java)
- 运行与测试
- [2. 核心特性:流式响应(SSE)](#2. 核心特性:流式响应(SSE))
- [3. 核心特性:RAG 检索增强生成(企业知识库问答)](#3. 核心特性:RAG 检索增强生成(企业知识库问答))
- [4. 核心特性:工具调用(Function Calling)](#4. 核心特性:工具调用(Function Calling))
- 四、生产级实践:关键功能集成
-
- [1. 多模型切换(OpenAI → Gemini)](#1. 多模型切换(OpenAI → Gemini))
- [2. 安全配置(API密钥校验+权限控制)](#2. 安全配置(API密钥校验+权限控制))
- [3. 监控与可观测性(Spring Boot Actuator + Prometheus)](#3. 监控与可观测性(Spring Boot Actuator + Prometheus))
- [4. 部署方案(Docker + K8s)](#4. 部署方案(Docker + K8s))
- [五、框架对比:Spring AI vs LangChain Java vs 原生LLM SDK](#五、框架对比:Spring AI vs LangChain Java vs 原生LLM SDK)
- 六、总结
Spring AI技术详细解析:Spring生态AI应用开发框架与生产级实践
前言
若对您有帮助的话,请点赞收藏加关注哦,您的关注是我持续创作的动力!有问题请私信或联系邮箱:funian.gm@gmail.com
随着大语言模型(LLM)、向量数据库、AI工具链的快速发展,企业级AI应用开发面临「多模型兼容、生态集成复杂、企业级特性缺失(安全/监控/可扩展性)」等痛点。Spring AI 作为 Spring 生态官方推出的AI应用开发框架,以「标准化API、Spring生态无缝集成、企业级特性、低代码门槛」为核心设计理念,让Java开发者无需关注底层AI模型细节,即可快速构建稳定、可扩展的AI应用(如智能问答、RAG检索增强、AI工具调用、多模态应用等)。

一、Spring AI 核心优势
Spring AI 并非从零构建,而是基于 Spring 生态的核心能力(依赖注入、自动配置、AOP、事务管理等),封装了AI领域的通用能力,核心优势如下:
| 核心优势 | 具体说明 |
|---|---|
| Spring 生态无缝集成 | 原生支持 Spring Boot、Spring Cloud、Spring Security 等组件,无需额外适配 |
| 标准化 API 设计 | 统一 LLM 调用、向量库操作、工具调用接口,屏蔽不同AI服务(OpenAI/Gemini/本地化模型)的差异 |
| 多模型/多服务商支持 | 支持主流LLM(OpenAI、Gemini、Anthropic、Llama 3)、向量库(Milvus、Chroma、Elasticsearch)、AI工具(Function Calling、第三方API) |
| 企业级特性内置 | 支持配置加密、权限控制、监控告警(Actuator)、分布式追踪(Sleuth)、事务管理等企业级需求 |
| 低代码开发体验 | 基于 Spring Boot 自动配置,注解驱动开发,无需手动管理AI客户端连接、生命周期 |
| RAG 全流程支持 | 内置文档加载(PDF/Word/Markdown)、文本分割、嵌入生成、向量存储、检索增强全流程组件 |
| 多模态与流式响应 | 支持文本、图片等多模态输入,原生支持LLM流式响应(SSE),提升用户体验 |
| 可扩展性强 | 支持自定义 AI 客户端、向量库适配器、工具处理器,适配复杂业务场景 |
二、Spring AI 生产级目录结构
Spring AI 基于 Spring Boot 构建,目录结构遵循 Spring 生态规范,同时针对AI应用特点优化,以下是生产级项目的标准目录结构(适用于中大型AI应用,如企业知识库、智能客服、AI助手):
spring-ai-project/
├── .env # 环境变量(API密钥、数据库地址等敏感信息,不提交Git)
├── .env.example # 环境变量示例(提交Git,指导部署)
├── .gitignore # Git忽略文件
├── pom.xml # Maven依赖配置
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── SpringAiApplication.java # 应用入口
│ │ │ ├── config/ # 配置类(AI客户端、向量库、安全配置)
│ │ │ │ ├── AiClientConfig.java # AI客户端配置(OpenAI/Gemini)
│ │ │ │ ├── VectorStoreConfig.java # 向量库配置(Milvus/Chroma)
│ │ │ │ └── SecurityConfig.java # 安全配置(API密钥校验、权限控制)
│ │ │ ├── controller/ # 接口层(REST API,对外提供服务)
│ │ │ │ ├── ChatController.java # 对话接口(问答、流式响应)
│ │ │ │ ├── RagController.java # RAG检索增强接口(知识库问答)
│ │ │ │ └── ToolController.java # 工具调用接口(Function Calling)
│ │ │ ├── service/ # 业务逻辑层(封装AI核心逻辑)
│ │ │ │ ├── ChatService.java # 对话业务(LLM调用、Prompt优化)
│ │ │ │ ├── RagService.java # RAG业务(检索+生成)
│ │ │ │ └── ToolService.java # 工具业务(工具注册、调用逻辑)
│ │ │ ├── repository/ # 数据访问层(向量库、数据库操作)
│ │ │ │ ├── VectorRepository.java # 向量库操作封装
│ │ │ │ └── DocumentRepository.java # 文档元数据数据库操作
│ │ │ ├── model/ # 数据模型(请求/响应DTO、实体类)
│ │ │ │ ├── req/ # 请求模型
│ │ │ │ │ ├── ChatReq.java # 对话请求
│ │ │ │ │ └── RagQueryReq.java # RAG查询请求
│ │ │ │ ├── resp/ # 响应模型
│ │ │ │ │ ├── ChatResp.java # 对话响应
│ │ │ │ │ └── StreamChatResp.java # 流式对话响应
│ │ │ │ └── entity/ # 数据库实体(文档元数据、用户会话)
│ │ │ │ └── DocumentEntity.java # 文档元数据实体
│ │ │ ├── ai/ # AI核心组件(Prompt模板、工具定义、自定义适配器)
│ │ │ │ ├── prompt/ # Prompt模板(Mustache格式)
│ │ │ │ │ ├── ChatPrompt.java # 对话Prompt模板
│ │ │ │ │ └── RagPrompt.java # RAG检索Prompt模板
│ │ │ │ ├── tool/ # 工具定义(Function Calling)
│ │ │ │ │ ├── WeatherTool.java # 天气查询工具
│ │ │ │ │ └── CalculatorTool.java # 计算工具
│ │ │ │ └── adapter/ # 自定义适配器(适配特殊AI模型/向量库)
│ │ │ │ └── CustomVectorStoreAdapter.java
│ │ │ └── util/ # 工具类(文档处理、格式转换、异常处理)
│ │ │ ├── DocumentUtils.java # 文档加载、分割工具
│ │ │ ├── PromptUtils.java # Prompt格式化工具
│ │ │ └── ExceptionHandler.java # 全局异常处理
│ │ └── resources/
│ │ ├── application.yml # 通用配置文件
│ │ ├── application-dev.yml # 开发环境配置
│ │ ├── application-prod.yml # 生产环境配置
│ │ └── prompts/ # Prompt模板文件(外部化管理)
│ │ ├── chat-template.mustache # 对话Prompt模板文件
│ │ └── rag-template.mustache # RAG Prompt模板文件
│ └── test/ # 单元测试与集成测试
│ └── java/
│ └── com/
│ └── example/
│ ├── ChatServiceTest.java # 对话服务测试
│ └── RagServiceTest.java # RAG服务测试
└── docs/ # 项目文档(架构图、API文档)
└── api-docs.md # API接口文档
核心目录说明
| 目录/文件 | 核心作用 | 关键注意点 |
|---|---|---|
config/ |
AI客户端、向量库、安全等配置 | 基于Spring Boot自动配置,通过@Configuration声明 |
controller/ |
对外提供REST API接口 | 区分对话、RAG、工具调用等场景,使用@RestController |
service/ |
封装AI核心业务逻辑 | 隔离控制器与AI组件,便于测试和维护 |
repository/ |
数据访问层(向量库+数据库) | 向量库操作通过Spring AI标准化API封装,屏蔽底层差异 |
ai/prompt/ |
Prompt模板管理 | 推荐使用Mustache外部化模板,避免硬编码 |
ai/tool/ |
工具定义(Function Calling) | 工具类需实现Function接口,声明参数和描述 |
resources/prompts/ |
外部化Prompt模板文件 | 支持动态参数替换,便于多环境适配和修改 |
util/DocumentUtils |
文档处理工具(加载、分割、嵌入) | 复用Spring AI内置的DocumentReader和TextSplitter |
目录设计原则
- 分层解耦:遵循「接口层→业务层→数据访问层→AI组件层」分层,职责清晰;
- 配置与代码分离:敏感信息(API密钥)通过环境变量注入,配置文件按环境拆分;
- 标准化与定制化平衡:优先使用Spring AI标准化API,特殊场景通过自定义适配器扩展;
- 可测试性:业务逻辑封装在Service层,便于单元测试(可Mock AI客户端);
- 外部化管理:Prompt模板、配置参数等外部化,避免硬编码导致的频繁部署。
三、Spring AI 核心技术解析
1. 快速入门:Spring Boot 集成 OpenAI 实现对话
Spring AI 提供自动配置 starters,无需复杂配置即可集成主流LLM,以下是最小化对话示例:
步骤1:添加依赖(pom.xml)
xml
<!-- Spring Boot 父依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI 依赖(自动配置) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<!-- Lombok 简化代码(可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
步骤2:配置 OpenAI 密钥(application-dev.yml)
yaml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY:sk-your-openai-key} # 从环境变量读取,默认值兜底
base-url: https://api.openai.com/v1 # 国内用户可配置代理地址
chat:
model: gpt-3.5-turbo # 默认模型
temperature: 0.7 # 随机性:0-1,值越高越随机
步骤3:实现对话 Service(ChatService.java)
java
package com.example.service;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class ChatService {
// Spring AI 自动注入 OpenAI ChatClient
private final ChatClient chatClient;
/**
* 简单对话(直接调用LLM)
*/
public String simpleChat(String userMessage) {
// 构建Prompt并调用LLM
ChatResponse response = chatClient.generate(userMessage);
// 返回LLM生成的内容
return response.getResult().getOutput().getContent();
}
/**
* 基于Prompt模板的对话(优化Prompt结构)
*/
public String templateChat(String userMessage, String systemPrompt) {
// Prompt模板(使用Mustache语法)
String template = """
{{systemPrompt}}
用户问题:{{userMessage}}
""";
// 模板参数
Map<String, Object> params = new HashMap<>();
params.put("systemPrompt", systemPrompt);
params.put("userMessage", userMessage);
// 构建Prompt
Prompt prompt = new PromptTemplate(template, params).create();
// 调用LLM
return chatClient.generate(prompt).getResult().getOutput().getContent();
}
}
步骤4:实现对话接口(ChatController.java)
java
package com.example.controller;
import com.example.model.req.ChatReq;
import com.example.model.resp.ChatResp;
import com.example.service.ChatService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/chat")
@RequiredArgsConstructor
public class ChatController {
private final ChatService chatService;
/**
* 简单对话接口
*/
@PostMapping("/simple")
public ChatResp simpleChat(@RequestBody ChatReq req) {
String response = chatService.simpleChat(req.getUserMessage());
return new ChatResp(response);
}
/**
* 模板对话接口
*/
@PostMapping("/template")
public ChatResp templateChat(@RequestBody ChatReq req) {
// 系统提示词(定义AI角色)
String systemPrompt = "你是一个专业的技术顾问,回答简洁明了,基于技术事实";
String response = chatService.templateChat(req.getUserMessage(), systemPrompt);
return new ChatResp(response);
}
}
步骤5:请求模型(ChatReq.java)与响应模型(ChatResp.java)
java
// ChatReq.java
package com.example.model.req;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class ChatReq {
@NotBlank(message = "用户消息不能为空")
private String userMessage;
}
// ChatResp.java
package com.example.model.resp;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ChatResp {
private String content;
}
运行与测试
- 启动应用(需配置
OPENAI_API_KEY环境变量); - 发送POST请求到
http://localhost:8080/api/chat/template:
json
{
"userMessage": "什么是Spring AI?"
}
- 响应结果:
json
{
"content": "Spring AI是Spring生态下的AI应用开发框架,提供标准化API集成LLM、向量库、AI工具,简化Java开发者构建企业级AI应用的流程..."
}
2. 核心特性:流式响应(SSE)
AI对话场景中,流式响应能提升用户体验(无需等待完整结果),Spring AI 原生支持 SSE 流式输出:
代码示例:流式对话接口
java
// ChatService.java 新增流式方法
public Flux<String> streamChat(String userMessage, String systemPrompt) {
String template = """
{{systemPrompt}}
用户问题:{{userMessage}}
""";
Map<String, Object> params = new HashMap<>();
params.put("systemPrompt", systemPrompt);
params.put("userMessage", userMessage);
Prompt prompt = new PromptTemplate(template, params).create();
// 流式生成(返回Flux<String>)
return chatClient.stream(prompt)
.map(chatResponse -> chatResponse.getResult().getOutput().getContent());
}
// ChatController.java 新增流式接口
@PostMapping("/stream")
public Flux<ServerSentEvent<String>> streamChat(@RequestBody ChatReq req) {
String systemPrompt = "你是一个专业的技术顾问,回答简洁明了";
// 转换为SSE格式(前端可通过EventSource接收)
return chatService.streamChat(req.getUserMessage(), systemPrompt)
.map(content -> ServerSentEvent.<String>builder()
.data(content)
.build());
}
前端调用示例(JavaScript)
javascript
// 前端通过EventSource接收流式响应
const eventSource = new EventSource('http://localhost:8080/api/chat/stream');
eventSource.onmessage = (event) => {
console.log('流式响应:', event.data);
// 渲染到页面
document.getElementById('chat-content').innerText += event.data;
};
eventSource.onerror = (error) => {
console.error('流式响应错误:', error);
eventSource.close();
};
3. 核心特性:RAG 检索增强生成(企业知识库问答)
RAG(Retrieval-Augmented Generation)是解决LLM知识时效性、准确性的核心方案,Spring AI 内置RAG全流程组件:
步骤1:添加向量库依赖(以Milvus为例)
xml
<!-- Spring AI Milvus 向量库依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-milvus-store</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<!-- Milvus Java SDK -->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.4.3</version>
</dependency>
步骤2:配置向量库(application-dev.yml)
yaml
spring:
ai:
vectorstore:
milvus:
host: localhost # Milvus服务地址
port: 19530 # Milvus端口
database: default # 数据库名
collection-name: knowledge_base # 向量集合名(类似数据库表)
dimension: 1536 # 嵌入向量维度(OpenAI Embedding为1536)
embedding:
openai:
api-key: ${OPENAI_API_KEY}
model: text-embedding-3-small # Embedding模型
步骤3:RAG核心流程实现(RagService.java)
java
package com.example.service;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.search.SearchResult;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class RagService {
private final VectorStore vectorStore; // 向量库(Milvus)
private final EmbeddingClient embeddingClient; // Embedding客户端(OpenAI)
private final ChatClient chatClient; // LLM客户端
/**
* 1. 文档入库(加载→分割→嵌入→存储)
*/
public void addDocumentToVectorStore(String documentContent, String documentId) {
// 1.1 加载文档(Spring AI支持PDF/Word/Markdown等,此处简化为纯文本)
Document document = new Document(documentContent);
document.getMetadata().put("documentId", documentId);
// 1.2 文本分割(避免单文档过长,提升检索精度)
List<Document> splitDocuments = DocumentUtils.splitDocument(document);
// 1.3 嵌入生成+向量库存储(Spring AI自动完成)
vectorStore.add(splitDocuments);
}
/**
* 2. RAG检索增强生成(检索→构建Prompt→生成回答)
*/
public String ragQuery(String userQuery) {
// 2.1 检索相关文档(基于用户查询生成嵌入,向量库相似度匹配)
List<SearchResult> searchResults = vectorStore.similaritySearch(userQuery, 3); // 取Top3相关文档
// 2.2 构建检索结果上下文
String context = searchResults.stream()
.map(result -> result.getDocument().getContent())
.collect(Collectors.joining("\n\n"));
// 2.3 构建RAG Prompt(结合上下文和用户查询)
String ragTemplate = """
基于以下上下文信息回答用户问题,仅使用上下文内容,不要编造信息:
上下文:{{context}}
用户问题:{{userQuery}}
回答要求:简洁明了,直击要点
""";
Prompt prompt = new PromptTemplate(ragTemplate,
"context", context,
"userQuery", userQuery)
.create();
// 2.4 调用LLM生成回答
return chatClient.generate(prompt).getResult().getOutput().getContent();
}
}
步骤4:文档分割工具(DocumentUtils.java)
java
package com.example.util;
import org.springframework.ai.document.Document;
import org.springframework.ai.text.splitter.RecursiveCharacterTextSplitter;
import org.springframework.ai.text.splitter.TextSplitter;
import java.util.List;
public class DocumentUtils {
/**
* 文本分割(递归字符分割,避免破坏语义)
*/
public static List<Document> splitDocument(Document document) {
// 配置分割参数:chunkSize=500字符,chunkOverlap=50字符(重叠部分提升连贯性)
TextSplitter textSplitter = new RecursiveCharacterTextSplitter(500, 50);
return textSplitter.split(document);
}
}
步骤5:RAG接口(RagController.java)
java
package com.example.controller;
import com.example.model.req.RagQueryReq;
import com.example.model.req.DocumentUploadReq;
import com.example.model.resp.ChatResp;
import com.example.service.RagService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/rag")
@RequiredArgsConstructor
public class RagController {
private final RagService ragService;
/**
* 文档入库接口
*/
@PostMapping("/document/add")
public String addDocument(@RequestBody DocumentUploadReq req) {
ragService.addDocumentToVectorStore(req.getContent(), req.getDocumentId());
return "文档入库成功";
}
/**
* RAG问答接口
*/
@PostMapping("/query")
public ChatResp ragQuery(@RequestBody RagQueryReq req) {
String response = ragService.ragQuery(req.getUserQuery());
return new ChatResp(response);
}
}
4. 核心特性:工具调用(Function Calling)
Spring AI 支持LLM自动触发工具调用(如查询数据库、调用第三方API),核心是「工具注册→LLM决策→工具执行→结果返回」:
步骤1:定义工具(WeatherTool.java)
java
package com.example.ai.tool;
import lombok.Data;
import org.springframework.ai.chat.tools.Function;
import org.springframework.stereotype.Component;
import java.util.Random;
/**
* 天气查询工具(模拟调用天气API)
*/
@Component
public class WeatherTool {
// 标记为工具函数(Spring AI自动注册)
@Function(name = "queryWeather", description = "查询指定城市的天气,参数为城市名称")
public WeatherResult queryWeather(@Function.Parameter(description = "城市名称(如北京、上海)") String city) {
// 模拟调用天气API,返回随机天气数据
String[] weathers = {"晴", "阴", "雨", "雪", "多云"};
String weather = weathers[new Random().nextInt(weathers.length)];
int temperature = new Random().nextInt(30) + 10; // 10-40℃
WeatherResult result = new WeatherResult();
result.setCity(city);
result.setWeather(weather);
result.setTemperature(temperature + "℃");
return result;
}
// 工具返回结果模型
@Data
public static class WeatherResult {
private String city;
private String weather;
private String temperature;
}
}
步骤2:工具调用Service(ToolService.java)
java
package com.example.service;
import com.example.ai.tool.WeatherTool;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.tools.ToolCall;
import org.springframework.ai.chat.tools.ToolRegistry;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class ToolService {
private final ChatClient chatClient;
private final ToolRegistry toolRegistry; // Spring AI工具注册中心
private final WeatherTool weatherTool; // 天气工具
/**
* 初始化工具注册(将自定义工具注册到工具中心)
*/
public void initToolRegistry() {
toolRegistry.register(weatherTool);
}
/**
* 工具调用流程
*/
public String toolCallChat(String userMessage) {
// 1. 构建Prompt(告知LLM可使用的工具)
String toolPrompt = """
你可以使用以下工具解决用户问题:
{{tools}}
若用户问题需要工具支持,返回工具调用指令;否则直接回答。
""";
// 获取已注册的工具描述
String toolsDesc = toolRegistry.getTools().stream()
.map(tool -> tool.getName() + ":" + tool.getDescription())
.collect(Collectors.joining("\n"));
Prompt prompt = new PromptTemplate(toolPrompt, "tools", toolsDesc)
.addMessage(userMessage)
.create();
// 2. 调用LLM,获取工具调用指令(若有)
var response = chatClient.generate(prompt);
List<ToolCall> toolCalls = response.getResult().getToolCalls();
// 3. 执行工具调用(若LLM返回了工具调用指令)
if (!toolCalls.isEmpty()) {
ToolCall toolCall = toolCalls.get(0);
// 执行工具并获取结果
Object toolResult = toolRegistry.execute(toolCall);
// 4. 将工具结果反馈给LLM,生成最终回答
String finalPrompt = String.format(
"用户问题:%s\n工具调用结果:%s\n请基于工具结果生成自然语言回答",
userMessage, toolResult.toString()
);
return chatClient.generate(finalPrompt).getResult().getOutput().getContent();
}
// 无需工具,直接返回LLM回答
return response.getResult().getOutput().getContent();
}
}
步骤3:工具调用接口(ToolController.java)
java
package com.example.controller;
import com.example.model.req.ChatReq;
import com.example.model.resp.ChatResp;
import com.example.service.ToolService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
@RestController
@RequestMapping("/api/tool")
@RequiredArgsConstructor
public class ToolController implements CommandLineRunner {
private final ToolService toolService;
// 应用启动时初始化工具注册
@Override
public void run(String... args) throws Exception {
toolService.initToolRegistry();
}
/**
* 工具调用对话接口
*/
@PostMapping("/chat")
public ChatResp toolChat(@RequestBody ChatReq req) {
String response = toolService.toolCallChat(req.getUserMessage());
return new ChatResp(response);
}
}
测试工具调用
发送POST请求到http://localhost:8080/api/tool/chat:
json
{
"userMessage": "查询北京的天气"
}
响应结果:
json
{
"content": "北京当前天气为晴,气温25℃"
}
四、生产级实践:关键功能集成
1. 多模型切换(OpenAI → Gemini)
Spring AI 标准化API支持多模型无缝切换,仅需修改依赖和配置:
步骤1:替换依赖(pom.xml)
xml
<!-- 移除OpenAI依赖,添加Gemini依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-google-gemini-spring-boot-starter</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
步骤2:修改配置(application-dev.yml)
yaml
spring:
ai:
google:
gemini:
api-key: ${GEMINI_API_KEY:your-gemini-key}
chat:
model: gemini-pro
temperature: 0.7
embedding:
google:
gemini:
api-key: ${GEMINI_API_KEY}
model: text-embedding-004 # Gemini Embedding模型
核心亮点
- 业务代码(Service/Controller)无需修改,完全遵循「面向接口编程」;
- 支持动态切换模型(通过配置中心修改模型参数,无需重启应用)。
2. 安全配置(API密钥校验+权限控制)
企业级应用需保障AI接口安全,结合Spring Security实现:
步骤1:添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
步骤2:安全配置(SecurityConfig.java)
java
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// 自定义API密钥过滤器(校验请求头中的X-API-KEY)
@Bean
public ApiKeyFilter apiKeyFilter() {
return new ApiKeyFilter();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // 前后端分离,禁用CSRF
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 无状态
.authorizeHttpRequests(auth -> auth
.antMatchers("/api/public/**").permitAll() // 公开接口无需校验
.anyRequest().authenticated() // 其他接口需认证
)
.addFilterBefore(apiKeyFilter(), UsernamePasswordAuthenticationFilter.class); // 添加API密钥过滤器
return http.build();
}
}
// API密钥过滤器(ApiKeyFilter.java)
package com.example.config;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ApiKeyFilter extends OncePerRequestFilter {
private static final String API_KEY_HEADER = "X-API-KEY";
private static final String VALID_API_KEY = System.getenv("APP_API_KEY"); // 从环境变量读取有效API密钥
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String apiKey = request.getHeader(API_KEY_HEADER);
// 校验API密钥
if (VALID_API_KEY == null || !VALID_API_KEY.equals(apiKey)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("无效的API密钥");
return;
}
// 密钥有效,设置认证信息
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
"api-user", null, null);
SecurityContextHolder.getContext().setAuthentication(authToken);
filterChain.doFilter(request, response);
}
}
3. 监控与可观测性(Spring Boot Actuator + Prometheus)
步骤1:添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
步骤2:配置监控(application-dev.yml)
yaml
management:
endpoints:
web:
exposure:
include: health,info,prometheus # 暴露的监控端点
metrics:
tags:
application: spring-ai-app # 应用标签(Prometheus查询用)
endpoint:
health:
show-details: always # 显示健康详情
步骤3:自定义AI监控指标(AiMetrics.java)
java
package com.example.util;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class AiMetrics {
private final MeterRegistry meterRegistry;
// LLM调用计数器
private final Counter llmCallCounter = meterRegistry.counter("spring.ai.llm.call.count");
// LLM调用失败计数器
private final Counter llmErrorCounter = meterRegistry.counter("spring.ai.llm.error.count");
// 向量库检索计数器
private final Counter vectorSearchCounter = meterRegistry.counter("spring.ai.vector.search.count");
// 记录LLM调用成功
public void recordLlmCallSuccess() {
llmCallCounter.increment();
}
// 记录LLM调用失败
public void recordLlmCallError() {
llmErrorCounter.increment();
}
// 记录向量库检索
public void recordVectorSearch() {
vectorSearchCounter.increment();
}
}
步骤4:在Service中使用监控指标
java
// ChatService.java 改造
public String simpleChat(String userMessage) {
try {
ChatResponse response = chatClient.generate(userMessage);
aiMetrics.recordLlmCallSuccess(); // 记录成功
return response.getResult().getOutput().getContent();
} catch (Exception e) {
aiMetrics.recordLlmCallError(); // 记录失败
throw new RuntimeException("LLM调用失败", e);
}
}
监控访问
- 健康检查:
http://localhost:8080/actuator/health - Prometheus指标:
http://localhost:8080/actuator/prometheus(可导入Grafana可视化)
4. 部署方案(Docker + K8s)
步骤1:编写Dockerfile
dockerfile
# 构建阶段
FROM maven:3.8.8-openjdk-17 AS builder
WORKDIR /app
# 复制依赖文件
COPY pom.xml .
# 下载依赖
RUN mvn dependency:go-offline
# 复制源代码
COPY src ./src
# 编译打包
RUN mvn package -DskipTests
# 运行阶段
FROM openjdk:17-jdk-slim
WORKDIR /app
# 复制编译产物
COPY --from=builder /app/target/*.jar app.jar
# 暴露端口
EXPOSE 8080
# 启动命令(通过环境变量注入配置)
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]
步骤2:K8s部署文件(deploy.yaml)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-ai-app
namespace: ai-apps
spec:
replicas: 3
selector:
matchLabels:
app: spring-ai-app
template:
metadata:
labels:
app: spring-ai-app
spec:
containers:
- name: spring-ai-app
image: spring-ai-app:latest
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: ai-secrets
key: openai-api-key
- name: APP_API_KEY
valueFrom:
secretKeyRef:
name: ai-secrets
key: app-api-key
resources:
limits:
cpu: "1"
memory: "2Gi"
requests:
cpu: "0.5"
memory: "1Gi"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: spring-ai-service
namespace: ai-apps
spec:
selector:
app: spring-ai-app
ports:
- port: 80
targetPort: 8080
type: ClusterIP
五、框架对比:Spring AI vs LangChain Java vs 原生LLM SDK
| 框架/方案 | 生态集成 | 标准化API | 企业级特性 | 易用性 | 适用场景 |
|---|---|---|---|---|---|
| Spring AI | 极强(Spring生态无缝集成) | 支持 | 完善(安全/监控/事务) | 高 | 企业级AI应用、Java后端集成AI、微服务AI组件 |
| LangChain Java | 一般(需手动集成Spring) | 支持 | 薄弱(缺乏企业级特性) | 中 | Java轻量AI应用、快速原型开发 |
| 原生LLM SDK(OpenAI/Gemini) | 无(需手动封装) | 无(各厂商API差异大) | 无 | 低 | 简单AI需求、无需框架依赖的场景 |
六、总结
Spring AI 作为 Spring 生态下的AI开发框架,以「标准化API、生态无缝集成、企业级特性」为核心竞争力,完美解决了Java开发者构建AI应用的痛点(多模型兼容、生态集成复杂、缺乏企业级保障)。其关键亮点总结如下:
- 目录结构:遵循Spring Boot规范,分层清晰、职责明确,支持中大型企业级AI应用扩展;
- 核心技术:LLM调用、流式响应、RAG检索增强、工具调用等核心能力开箱即用,API标准化,多模型无缝切换;
- 生产实践:原生支持安全校验、监控告警、容器化部署、多环境配置,满足企业级应用的稳定性和安全性要求;
- 易用性:自动配置、注解驱动、Prompt模板化,降低Java开发者的AI技术门槛;
- 适用场景:尤其适合企业知识库、智能客服、AI助手、微服务AI组件、Java后端AI集成等场景。
学习 Spring AI 的关键是「熟悉Spring生态+掌握AI核心概念」,建议从快速入门示例(集成OpenAI实现对话)入手,逐步掌握RAG、工具调用等核心特性,再结合Spring Security、Actuator等组件实现企业级部署。
推荐优先参考 Spring AI 官方文档 ,其文档详尽、示例丰富,是快速上手的核心资源。