前言
当LLM浪潮席卷全球,Python似乎成了AI的"官方语言"时,我们Java开发者该如何快速拥抱这个新时代?Spring AI给出了官方答案:像整合数据库一样整合AI,像写Controller一样调用大模型。
本文将从零开始,带你一步步掌握Spring AI的核心用法。学完本文,你将能够:
-
✅ 10分钟搭建一个AI聊天应用
-
✅ 掌握提示词工程的最佳实践
-
✅ 构建一个带知识库的RAG问答系统
一、Spring AI是什么?
一句话定义:Spring AI是Spring官方推出的AI应用开发框架,为Java开发者提供统一的AI模型调用接口。
1.1 为什么需要Spring AI?
在没有Spring AI之前,Java调用大模型是这样的:
java
// 原生HTTP调用
OkHttpClient client = new OkHttpClient();
String json = "{ \"model\": \"gpt-3.5-turbo\", \"messages\": [...] }";
Request request = new Request.Builder()
.url("https://api.openai.com/v1/chat/completions")
.header("Authorization", "Bearer sk-xxx")
.post(RequestBody.create(json, MediaType.parse("application/json")))
.build();
Response response = client.newCall(request).execute();
// 手动解析JSON...
有了Spring AI之后:
java
@RestController
public class ChatController {
@Autowired
private ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient.prompt().user(msg).call().content();
}
}
简洁、优雅、符合Spring风格------这就是Spring AI的魅力。
1.2 核心能力一览
| 能力模块 | 说明 | 支持的提供商 |
|---|---|---|
| Chat(对话) | 大语言模型对话 | OpenAI、DeepSeek、通义千问、智谱AI等 |
| Embedding(向量化) | 文本转向量 | OpenAI、Ollama、阿里云等 |
| Image Generation(图像生成) | 文生图 | OpenAI DALL-E、Stability AI |
| Vector Store(向量数据库) | 向量存储与检索 | PGVector、Chroma、Qdrant、Redis等 |
| Function Calling(函数调用) | 模型调用业务API | 各模型均已支持 |
Spring AI支持超过15种AI提供商,通过统一的API抽象,你可以轻松切换底层模型而无需修改业务代码。
二、环境准备
2.1 前置要求
| 工具 | 版本要求 | 说明 |
|---|---|---|
| JDK | 17+ | Spring AI基于Spring Boot 3.x |
| Maven | 3.6+ | 或Gradle 7.x |
| IDE | IntelliJ IDEA / VS Code | 推荐IDEA |
2.2 获取API Key(二选一)
方案一:DeepSeek(推荐国内开发者)
DeepSeek与OpenAI API完全兼容,价格便宜且无需科学上网。
bash
# 访问 https://platform.deepseek.com/ 注册并获取API Key
# 新用户通常赠送免费额度
方案二:OpenAI(国际用户)
bash
# 访问 https://platform.openai.com/api-keys 创建API Key
# 需要科学上网和海外信用卡
2.3 创建Spring Boot项目
使用 Spring Initializr 创建项目,选择以下依赖:
| 依赖 | 用途 |
|---|---|
| Spring Web | 提供REST API能力 |
| Spring AI OpenAI | 集成大模型 |
Maven依赖配置:
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
</properties>
<!-- Spring AI BOM 版本管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
</project>
三、快速开始:第一个AI应用
3.1 配置文件
在 src/main/resources/application.yml 中添加配置:
yaml
spring:
ai:
openai:
# DeepSeek / OpenAI API Key
api-key: ${AI_API_KEY:your-api-key-here}
# DeepSeek的base-url
base-url: https://api.deepseek.com
chat:
options:
model: deepseek-chat
temperature: 0.7 # 0-2之间,越高越有创造性
⚠️ 安全提示 :不要将API Key硬编码在配置文件中!推荐使用环境变量
AI_API_KEY。
3.2 创建Controller
java
package com.example.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/ai")
public class AIController {
private final ChatClient chatClient;
// 构造器注入
public AIController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
/**
* 基础对话接口
* GET /api/ai/chat?msg=你好
*/
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient.prompt()
.user(msg)
.call()
.content();
}
}
3.3 运行测试
bash
# 启动应用
mvn spring-boot:run
# 测试接口
curl "http://localhost:8080/api/ai/chat?msg=用Java写一个Hello World"
预期输出:
java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
恭喜!你已经成功搭建了第一个AI应用 🎉
四、进阶用法:提示词模板
在实际业务中,我们通常需要更结构化的提示词。Spring AI提供了 PromptTemplate 来支持动态模板。
4.1 翻译接口示例
java
package com.example.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/ai")
public class AIController {
private final ChatClient chatClient;
public AIController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
/**
* 翻译接口
* POST /api/ai/translate
* Body: {"sourceLang": "中文", "targetLang": "英文", "text": "今天天气真好"}
*/
@PostMapping("/translate")
public String translate(@RequestBody TranslateRequest request) {
// 定义提示词模板
PromptTemplate promptTemplate = new PromptTemplate("""
请将以下{sourceLang}文本翻译成{targetLang}:
{text}
只返回翻译结果,不要添加任何解释。
""");
// 填充模板变量
promptTemplate.add("sourceLang", request.sourceLang());
promptTemplate.add("targetLang", request.targetLang());
promptTemplate.add("text", request.text());
// 调用并返回
return chatClient.prompt(promptTemplate.render()).call().content();
}
// 请求DTO
public record TranslateRequest(String sourceLang, String targetLang, String text) {}
}
4.2 结构化输出:让AI返回JSON
java
import com.fasterxml.jackson.databind.ObjectMapper;
@PostMapping("/analyze")
public Map<String, Object> analyze(@RequestBody String text) {
String prompt = """
分析以下文本的情感倾向和关键词,以JSON格式返回:
格式:{"sentiment": "positive/negative/neutral", "keywords": ["关键词1", "关键词2"]}
文本:%s
""".formatted(text);
String response = chatClient.prompt(prompt).call().content();
// 解析JSON返回
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.readValue(response, Map.class);
} catch (Exception e) {
return Map.of("error", "解析失败");
}
}
五、RAG知识库集成
RAG(检索增强生成)是让AI回答私有数据问题的核心技术。Spring AI提供了完整的RAG支持,包括向量存储和检索增强。
5.1 添加依赖
xml
<!-- PGVector 支持 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
<!-- PDF 文档解析 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>
5.2 配置PGVector
首先启动PostgreSQL并安装PGVector扩展:
sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS vector;
配置文件:
yaml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/rag_db
username: postgres
password: password
ai:
vectorstore:
pgvector:
index-type: HNSW
distance-type: COSINE_DISTANCE
dimensions: 1536 # 向量维度,需与embeddings模型一致
5.3 实现RAG服务
java
package com.example.ai.service;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.ai.chat.client.ChatClient;
import java.util.List;
@Service
public class RagService {
private final VectorStore vectorStore;
private final ChatClient chatClient;
private final TokenTextSplitter textSplitter;
public RagService(VectorStore vectorStore,
ChatClient.Builder chatClientBuilder) {
this.vectorStore = vectorStore;
this.chatClient = chatClientBuilder.build();
// 文本分块:每块800字符,重叠200
this.textSplitter = new TokenTextSplitter(800, 200, 10, 10000);
}
/**
* 将文档加载到知识库
*/
public void loadDocument(Resource pdfResource) {
// 1. 读取PDF文档
TikaDocumentReader reader = new TikaDocumentReader(pdfResource);
List<Document> documents = reader.read();
// 2. 文本分块(RAG的关键步骤)
List<Document> chunks = textSplitter.apply(documents);
// 3. 向量化并存入数据库
vectorStore.add(chunks);
System.out.println("已加载 " + chunks.size() + " 个文档片段");
}
/**
* 基于知识库问答
*/
public String ask(String question) {
// 1. 检索相关文档片段(RAG召回)
List<Document> relevantDocs = vectorStore.similaritySearch(question, 3);
// 2. 构建上下文
StringBuilder context = new StringBuilder();
for (int i = 0; i < relevantDocs.size(); i++) {
context.append("【文档").append(i + 1).append("】\n");
context.append(relevantDocs.get(i).getContent()).append("\n\n");
}
// 3. 构建提示词
String prompt = """
请基于以下参考内容回答问题。
参考内容:
%s
问题:%s
要求:
- 如果参考内容包含答案,基于内容回答
- 如果不包含,请说"根据现有资料无法回答"
- 回答时请引用来源
""".formatted(context.toString(), question);
// 4. 调用大模型生成回答
return chatClient.prompt(prompt).call().content();
}
}
5.4 创建REST接口
java
package com.example.ai.controller;
import com.example.ai.service.RagService;
import org.springframework.core.io.FileSystemResource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
@RequestMapping("/api/rag")
public class RagController {
private final RagService ragService;
public RagController(RagService ragService) {
this.ragService = ragService;
}
/**
* 上传文档到知识库
*/
@PostMapping("/upload")
public String upload(@RequestParam MultipartFile file) throws IOException {
// 保存临时文件并加载
String tempPath = "/tmp/" + file.getOriginalFilename();
file.transferTo(new java.io.File(tempPath));
ragService.loadDocument(new FileSystemResource(tempPath));
return "文档加载成功: " + file.getOriginalFilename();
}
/**
* 基于知识库问答
*/
@GetMapping("/ask")
public String ask(@RequestParam String question) {
return ragService.ask(question);
}
}
六、高级配置
6.1 模型参数调优
yaml
spring:
ai:
openai:
chat:
options:
model: gpt-4o-mini # 模型名称
temperature: 0.7 # 创造性 (0-2)
max-tokens: 2000 # 最大输出长度
top-p: 1.0 # 核采样
frequency-penalty: 0 # 重复惩罚
presence-penalty: 0 # 话题惩罚
6.2 流式响应(打字机效果)
java
import org.springframework.ai.chat.client.StreamingChatClient;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/stream")
public class StreamController {
private final StreamingChatClient streamingChatClient;
public StreamController(StreamingChatClient streamingChatClient) {
this.streamingChatClient = streamingChatClient;
}
/**
* 流式输出,实现"打字机"效果
*/
@GetMapping(value = "/chat", produces = "text/plain; charset=UTF-8")
public Flux<String> streamChat(@RequestParam String msg) {
return streamingChatClient.stream(msg);
}
}
6.3 函数调用(Function Calling)
让AI能够调用你的业务代码:
java
import org.springframework.ai.model.function.FunctionCallback;
@Configuration
public class FunctionCallConfig {
@Bean
public FunctionCallback weatherFunction() {
return FunctionCallback.builder()
.function("getWeather", (String city) -> {
// 调用天气API
return "北京:晴,25°C";
})
.description("获取指定城市的天气信息")
.inputType(String.class)
.build();
}
}
// 使用时,AI会自动判断是否需要调用天气函数
七、常见问题与最佳实践
7.1 常见错误及解决
| 错误 | 原因 | 解决方案 |
|---|---|---|
401 Unauthorized |
API Key无效 | 检查配置是否正确 |
Connection timed out |
网络不通 | 检查base-url,DeepSeek用户注意配置正确 |
Context window exceeded |
输入太长 | 减少max-tokens或使用RAG分段处理 |
JSON parse error |
模型输出不是有效JSON | 在prompt中明确要求JSON格式 |
7.2 最佳实践清单
✅ 生产环境配置
-
使用环境变量管理API Key
-
配置超时和重试机制
-
添加限流保护
✅ 提示词工程
-
System Prompt明确定义AI角色
-
Few-shot示例提升准确率
-
输出格式要明确(JSON、Markdown等)
✅ RAG优化
-
文档分块大小:500-1000字符
-
块之间有重叠(overlap)
-
Top-K检索3-5个片段即可
八、总结
本文涵盖了Spring AI的核心使用场景:
-
基础对话:3步集成,一行代码调用大模型
-
提示词模板:结构化的AI交互
-
RAG知识库:让AI回答私有数据问题
-
流式响应:打字机般的交互体验
Spring AI的最大价值在于:用Spring的方式做AI应用,让Java开发者无需学习Python就能快速构建AI能力。
后续学习建议:
-
探索更多模型提供商(Ollama本地部署、通义千问等)
-
深入理解向量数据库和RAG原理
-
学习AI Agent多智能体编排
参考资源:
如果觉得本文对你有帮助,欢迎点赞收藏,有问题评论区交流~