1. 引言
Spring AI 是 Spring 官方推出的 AI 应用开发框架,旨在为 Java 开发者提供一套统一、简洁的 API,用于对接 OpenAI、Azure OpenAI、Hugging Face、Ollama 等主流大语言模型。它借鉴了 Spring 生态一贯的「约定优于配置」理念,让开发者可以用熟悉的依赖注入、自动配置和模板模式快速构建 AI 应用。
本文将从环境搭建开始,逐步讲解 Spring AI 的核心概念、常用 API,并通过多个可运行的实战案例,带你完成从「Hello World」到「RAG 知识库问答」的完整落地。
2. 环境准备
2.1 技术栈要求
- JDK:17 及以上(推荐 21)
- 构建工具:Maven 3.8+ 或 Gradle 7.5+
- Spring Boot:3.2.x 及以上
- Spring AI:1.0.0-M6 及以上(本文以 1.0.0-M6 为例)
- 模型服务:OpenAI API Key,或本地 Ollama 部署的开源模型
2.2 创建项目
推荐使用 Spring Initializr 创建项目,选择 Spring Boot 3.2.x,并添加以下依赖:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency>如果使用 Maven,还需要在pom.xml中配置 Spring AI 的里程碑仓库:
<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 配置 API Key
在application.yml中配置 OpenAI 的 API Key 和基础地址:
spring: ai: openai: api-key: ${OPENAI_API_KEY} base-url: https://api.openai.com chat: options: model: gpt-4o-mini temperature: 0.7如果使用本地 Ollama,则配置如下:
spring: ai: ollama: base-url: http://localhost:11434 chat: options: model: qwen2.5:7b3. 第一个 AI 应用:ChatClient 入门
3.1 核心概念
Spring AI 的核心抽象是ChatClient,它提供了流式(Fluent)API,用于构建和发送聊天请求。与传统的RestTemplate风格不同,ChatClient支持链式调用,代码可读性更强。
3.2 编写 Controller
下面创建一个简单的 REST 接口,接收用户问题并返回 AI 回答:
import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient = builder.build(); } @GetMapping("/chat") public String chat(@RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } }3.3 测试接口
启动应用后,访问以下地址即可测试:
curl "http://localhost:8080/chat?message=用一句话介绍Spring%20AI"返回结果示例:
Spring AI 是 Spring 官方推出的 AI 应用开发框架,帮助 Java 开发者以统一、简洁的方式集成大语言模型能力。
4. 流式输出:打字机效果
在实际业务中,流式输出能显著提升用户体验。Spring AI 通过Flux支持响应式流式返回:
import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class StreamChatController { private final ChatClient chatClient; public StreamChatController(ChatClient.Builder builder) { this.chatClient = builder.build(); } @GetMapping("/chat/stream") public Flux<String> streamChat(@RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); } }前端可以使用fetch配合ReadableStream实现打字机效果,也可以直接使用 SSE(Server-Sent Events)协议接收数据。
5. 结构化输出:让 AI 返回 JSON
5.1 定义实体类
很多时候我们需要 AI 返回结构化数据,而不是纯文本。Spring AI 提供了BeanOutputConverter来实现这一需求。首先定义一个实体类:
public record BookInfo(String title, String author, int year, String summary) {}5.2 使用 BeanOutputConverter
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.converter.BeanOutputConverter; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class StructuredOutputController { private final ChatClient chatClient; public StructuredOutputController(ChatClient.Builder builder) { this.chatClient = builder.build(); } @GetMapping("/book") public BookInfo getBookInfo(@RequestParam String name) { BeanOutputConverter<BookInfo> converter = new BeanOutputConverter<>(BookInfo.class); String json = chatClient.prompt() .user("请介绍书籍《" + name + "》的信息,包括书名、作者、出版年份和内容简介。" + converter.getFormat()) .call() .content(); return converter.convert(json); } }这里的关键是converter.getFormat()会在提示词中追加 JSON Schema 约束,引导模型输出符合实体类结构的 JSON,再由converter.convert()完成反序列化。
6. 提示词模板:复用 Prompt
在实际项目中,提示词往往需要动态拼接。Spring AI 提供了PromptTemplate,支持占位符替换:
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController public class PromptTemplateController { private final ChatClient chatClient; public PromptTemplateController(ChatClient.Builder builder) { this.chatClient = builder.build(); } @GetMapping("/translate") public String translate(@RequestParam String text, @RequestParam String targetLang) { PromptTemplate template = new PromptTemplate( "你是一位专业翻译。请将以下内容翻译成{targetLang},只输出翻译结果:\n{text}"); return chatClient.prompt(template.create(Map.of( "text", text, "targetLang", targetLang ))).call().content(); } }7. 实战案例:RAG 知识库问答
7.1 什么是 RAG
RAG(Retrieval-Augmented Generation,检索增强生成)是一种将外部知识库与大语言模型结合的技术。它先根据用户问题检索相关文档片段,再将这些片段作为上下文注入提示词,让模型基于真实资料回答,从而减少幻觉、提升准确性。
7.2 添加向量数据库依赖
本文使用 Redis 作为向量数据库,需要添加以下依赖:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-redis-store-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency>7.3 配置向量数据库
spring: data: redis: host: localhost port: 6379 ai: openai: api-key: ${OPENAI_API_KEY} embedding: options: model: text-embedding-3-small7.4 文档加载与向量化
import org.springframework.ai.document.Document; import org.springframework.ai.reader.TextReader; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Service; import java.util.List; @Service public class KnowledgeBaseService { private final VectorStore vectorStore; private final ResourceLoader resourceLoader; public KnowledgeBaseService(VectorStore vectorStore, ResourceLoader resourceLoader) { this.vectorStore = vectorStore; this.resourceLoader = resourceLoader; } public void loadDocument(String resourcePath) { Resource resource = resourceLoader.getResource(resourcePath); // 1. 读取文档 TextReader reader = new TextReader(resource); List<Document> documents = reader.get(); // 2. 切分文档 TokenTextSplitter splitter = new TokenTextSplitter(); List<Document> chunks = splitter.apply(documents); // 3. 向量化并存储 vectorStore.add(chunks); } }7.5 实现问答接口
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; @RestController public class RagController { private final ChatClient chatClient; private final VectorStore vectorStore; public RagController(ChatClient.Builder builder, VectorStore vectorStore) { this.chatClient = builder.build(); this.vectorStore = vectorStore; } @GetMapping("/rag") public String ask(@RequestParam String question) { // 1. 检索相关文档 List<Document> documents = vectorStore.similaritySearch( SearchRequest.query(question).withTopK(3)); // 2. 拼接上下文 String context = documents.stream() .map(Document::getText) .collect(Collectors.joining("\n\n")); // 3. 构造提示词并调用模型 return chatClient.prompt() .user("请基于以下资料回答问题。如果资料中没有相关信息,请如实说明。\n\n" + "资料:\n" + context + "\n\n问题:" + question) .call() .content(); } }7.6 测试 RAG 流程
首先加载知识库文档:
curl -X POST "http://localhost:8080/kb/load?resourcePath=classpath:docs/spring-ai-guide.txt"然后提问:
curl "http://localhost:8080/rag?question=Spring%20AI支持哪些向量数据库"8. 函数调用:让 AI 执行工具
8.1 定义工具方法
函数调用(Function Calling)允许模型在回答过程中调用外部工具。下面实现一个查询天气的功能:
import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Component; @Component public class WeatherTools { @Tool(description = "根据城市名称查询当前天气") public String getWeather(String city) { // 实际项目中可调用第三方天气 API return "城市:" + city + ",天气:晴,温度:26℃,湿度:45%"; } }8.2 在 ChatClient 中注册工具
import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class FunctionCallingController { private final ChatClient chatClient; public FunctionCallingController(ChatClient.Builder builder, WeatherTools weatherTools) { this.chatClient = builder .defaultTools(weatherTools) .build(); } @GetMapping("/weather") public String askWeather(@RequestParam String question) { return chatClient.prompt() .user(question) .call() .content(); } }当用户问「北京今天天气怎么样」时,模型会自动调用getWeather("北京")方法,并将返回结果组织成自然语言回答。
9. 多模态:图片理解
Spring AI 还支持多模态输入。以下示例演示如何让模型理解图片内容:
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.model.Media; import org.springframework.core.io.ClassPathResource; import org.springframework.util.MimeTypeUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class VisionController { private final ChatClient chatClient; public VisionController(ChatClient.Builder builder) { this.chatClient = builder.build(); } @GetMapping("/vision") public String describeImage() { Media image = new Media( MimeTypeUtils.IMAGE_PNG, new ClassPathResource("sample.png").getInputStream()); UserMessage message = new UserMessage( "请描述这张图片的内容", List.of(image)); return chatClient.prompt(new Prompt(message)) .call() .content(); } }10. 生产实践与注意事项
10.1 错误处理与重试
调用外部模型服务时,网络抖动、限流等问题不可避免。建议使用 Spring Retry 或 Resilience4j 增加重试和熔断机制:
import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; @Service public class AiService { @Retryable( retryFor = {RuntimeException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2)) public String callWithRetry(String prompt) { // 调用 ChatClient return chatClient.prompt().user(prompt).call().content(); } }10.2 成本控制
- Token 限制:通过
maxTokens限制单次输出长度。 - 模型选择:简单任务使用小模型(如 gpt-4o-mini),复杂任务才使用大模型。
- 缓存:对高频、结果稳定的请求做本地缓存。
- 流式输出:优先使用流式接口,减少等待时间。
10.3 安全与合规
- 敏感信息过滤:在发送请求前对用户输入做脱敏处理。
- 输出审核:对模型输出进行内容安全检测。
- API Key 管理:使用环境变量或配置中心管理密钥,切勿硬编码。
- 日志脱敏:避免在日志中记录完整的用户输入和模型输出。
11. 总结
本文从零开始,系统讲解了 Spring AI 的核心用法:
- 环境搭建:通过 Spring Initializr 快速创建项目并配置模型服务。
- 基础对话:使用
ChatClient实现同步和流式问答。 - 结构化输出:通过
BeanOutputConverter让 AI 返回 JSON。 - 提示词模板:使用
PromptTemplate复用和管理提示词。 - RAG 实战:结合向量数据库实现知识库问答。
- 函数调用:让模型自动调用外部工具。
- 多模态:支持图片理解等能力。
Spring AI 仍在快速迭代中,建议持续关注官方文档和 Release Notes。下一步可以尝试将 Spring AI 集成到你的业务系统中,结合函数调用和 RAG 构建更智能的应用。