最近在尝试将大模型能力集成到现有 Spring Boot 项目中时发现网上资料要么是零散的 API 调用示例要么是过于复杂的架构设计对于想快速上手、理解核心流程的开发者来说学习路径并不清晰。本文将基于 Spring AI 的最新稳定版本为你梳理一套从零到一的完整实战方案涵盖环境搭建、核心概念、多模型集成、提示工程到项目部署的全流程。无论你是想为应用添加智能对话、内容生成还是意图识别能力都能在这篇教程中找到可复用的代码和清晰的配置思路帮你避开版本兼容、依赖冲突等常见陷阱。1. Spring AI 背景与核心概念在深入代码之前我们有必要理解 Spring AI 是什么以及它试图解决什么问题。这能帮助我们在后续开发中做出更合理的技术选型。1.1 什么是 Spring AISpring AI 是 Spring 官方社区推出的一个项目旨在为基于 Spring 的应用程序集成人工智能AI功能特别是大型语言模型LLM和生成式 AI 能力提供一个抽象和统一的 API。你可以把它看作是 Spring 生态中对 AI 模型访问的“门面模式”或“模板方法”实现。它的核心价值在于“标准化”和“简化”标准化接口无论后端对接的是 OpenAI 的 GPT、Anthropic 的 Claude还是开源的 Llama 2、通义千问你都可以通过几乎相同的 Spring AI 接口进行调用。这极大地降低了更换模型供应商或进行多模型 A/B 测试的成本。简化集成它深度集成 Spring Boot 的自动配置、外部化配置application.properties/application.yml和依赖注入特性。你只需要添加一个 starter 依赖配置好 API Key就可以像使用JdbcTemplate操作数据库一样使用AiClient或ChatClient来调用大模型。丰富的功能抽象除了基础的文本生成Spring AI 还抽象出了提示词模板Prompt Templates、输出解析器Output Parsers、向量存储Vector Stores、文档加载器Document Loaders等高级概念为构建复杂的 AI 应用如 RAG提供了基础框架。1.2 核心组件与架构理解 Spring AI 的几个核心抽象是高效使用它的关键AiClient/ChatClient这是最核心的客户端接口。AiClient提供通用的生成调用而ChatClient更专注于多轮对话场景维护消息历史。我们通常直接注入它们来调用模型。Prompt代表发送给模型的请求。它不仅仅是一个字符串而是一个包含消息列表ListMessage的对象。Message可以是系统消息、用户消息或助手消息这为构建复杂的对话逻辑提供了结构。PromptTemplate提示词模板。允许你创建带有占位符如{topic}的提示词字符串在运行时动态注入变量。这是实现提示工程、构建可复用对话流程的基础。ChatOptions模型参数配置。例如温度temperature、最大令牌数maxTokens、topP 等。这些参数可以全局配置也可以在每次调用时单独指定。OutputParser输出解析器。用于将模型返回的非结构化文本解析成结构化的 Java 对象如 POJO、List等。这是将 AI 输出集成到业务逻辑中的桥梁。VectorStore向量存储接口。用于实现检索增强生成RAG应用将文档转换为向量并存储以便进行语义搜索。Spring AI 支持 Pinecone、Redis、PGVector 等多种后端。一个典型的 Spring AI 应用架构可以简化为Spring Boot 应用 - Spring AI 抽象层 - 具体的 AI 模型供应商 API如 OpenAI、Azure OpenAI。2. 环境准备与项目初始化接下来我们从零开始搭建一个 Spring Boot 项目并集成 Spring AI。请确保你的开发环境满足以下要求。2.1 基础环境要求JDK: 17 或更高版本Spring AI 要求 JDK 17。构建工具: Maven 3.6 或 Gradle 7.x。本文使用 Maven 进行演示。IDE: 任意你熟悉的 Java IDE如 IntelliJ IDEA推荐、Eclipse 或 VS Code。网络: 能够访问你选用的 AI 模型 API 端点例如 api.openai.com。对于国内开发者可能需要配置网络代理或使用国内可访问的模型如通义千问、智谱 AI。2.2 创建 Spring Boot 项目最快的方式是使用 Spring Initializr 。我们选择以下配置Project: MavenLanguage: JavaSpring Boot: 选择最新的稳定版本如 3.2.xGroup:com.exampleArtifact:spring-ai-demoDependencies: 在这里我们暂时只添加Spring Web因为 Spring AI 的依赖我们需要手动添加以控制版本。点击“GENERATE”下载项目压缩包并导入到你的 IDE 中。2.3 添加 Spring AI 依赖Spring AI 的版本管理与 Spring Boot 主版本相对独立。我们需要在pom.xml中显式添加 Spring AI 的 BOM物料清单和具体的 starter 依赖。首先在project标签下添加 Spring AI 的 BOM统一管理所有 Spring AI 相关组件的版本dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version0.8.1/version !-- 请检查并使用最新稳定版 -- typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement然后添加你需要的具体模型 starter。例如如果你要使用 OpenAI 的模型GPT-3.5, GPT-4则添加dependencies !-- Spring Boot Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI OpenAI Starter -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency !-- 可选用于测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies重要提示spring-ai-openai-spring-boot-starter这个依赖会自动传递引入 Spring AI 的核心模块如spring-ai-core以及 OpenAI 的客户端适配器。如果你想切换模型只需更换这个 starter 即可例如换成spring-ai-azure-openai-spring-boot-starter或spring-ai-ollama-spring-boot-starter用于本地模型。2.4 配置 API 密钥在src/main/resources/application.yml或application.properties中配置你的模型访问凭证。以 OpenAI 为例spring: ai: openai: api-key: ${OPENAI_API_KEY:sk-your-openai-api-key-here} # 优先从环境变量读取其次为默认值 chat: options: model: gpt-3.5-turbo # 默认使用的模型 temperature: 0.7 # 默认温度参数安全警告永远不要将真实的 API Key 硬编码在代码或配置文件中提交到版本控制系统如 Git。最佳实践是使用环境变量或配置中心。环境变量在启动应用前设置环境变量OPENAI_API_KEY。Linux/macOS:export OPENAI_API_KEYsk-xxxWindows (CMD):set OPENAI_API_KEYsk-xxxWindows (PowerShell):$env:OPENAI_API_KEY“sk-xxx”配置占位符如上例所示使用${OPENAI_API_KEY:default-value}语法优先从环境变量读取。3. 核心 API 使用实战环境配置好后我们来编写第一个 AI 交互程序。我们将创建一个简单的 REST 控制器来演示核心功能。3.1 基础文本生成首先创建一个控制器AiController.javapackage com.example.springaidemo.controller; import org.springframework.ai.client.AiClient; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.messages.UserMessage; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController public class AiController { private final AiClient aiClient; // 通过构造器注入 AiClient Spring AI 会自动配置 public AiController(AiClient aiClient) { this.aiClient aiClient; } GetMapping(/ai/generate) public String generate(RequestParam(value message, defaultValue 用Java写一个Hello World程序) String message) { // 1. 构建 Prompt 对象包含用户消息 Prompt prompt new Prompt(new UserMessage(message)); // 2. 调用 AiClient 生成内容 String generatedText aiClient.generate(prompt).getGeneration().getText(); // 3. 返回结果 return generatedText; } }启动应用运行SpringAiDemoApplication的 main 方法访问http://localhost:8080/ai/generate?message介绍一下Spring AI。你将看到模型返回的关于 Spring AI 的介绍文本。代码解析AiClient核心生成客户端由 Spring AI 根据你的配置如spring.ai.openai.*自动创建并注入。Prompt包装了输入消息的请求对象。UserMessage是Message接口的一种实现代表用户输入。aiClient.generate(prompt)发起同步调用返回一个AiResponse对象其中包含生成的文本、使用令牌数等信息。3.2 使用 ChatClient 进行多轮对话ChatClient更适合对话场景。虽然AiClient也能通过手动管理消息历史来实现对话但ChatClient的 API 更语义化。Spring AI 会自动配置一个ChatClientBean。package com.example.springaidemo.controller; 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 chatClient) { this.chatClient chatClient; } GetMapping(/chat/single) public String chatSingle(RequestParam String message) { return chatClient.prompt() .user(message) // 设置用户消息 .call() // 执行调用 .content(); // 获取返回的文本内容 } GetMapping(/chat/conversation) public String chatWithHistory(RequestParam String userInput) { // 模拟一个简单的对话流。实际应用中消息历史需要持久化如存在Session或DB中 // 这里为了演示我们假设一个简单的上下文 String systemPrompt 你是一个专业的Java技术顾问回答要简洁准确。; String conversationHistory 用户之前问Spring Boot是什么\n助手回答Spring Boot是一个用于简化Spring应用初始搭建和开发过程的框架。; String fullPrompt systemPrompt \n\n历史对话\n conversationHistory \n\n用户新问题 userInput; return chatClient.prompt() .user(fullPrompt) // 将系统提示、历史和新问题拼接后发送 .call() .content(); } }访问http://localhost:8080/chat/single?message什么是RESTful API进行测试。ChatClient的流式 APIprompt().user().call()让代码更简洁。3.3 使用 PromptTemplate 动态生成提示词硬编码提示词不利于维护和复用。PromptTemplate是解决这个问题的利器。package com.example.springaidemo.service; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.prompt.PromptTemplate; import org.springframework.stereotype.Service; import java.util.Map; Service public class ContentGenerationService { private final ChatClient chatClient; public ContentGenerationService(ChatClient chatClient) { this.chatClient chatClient; } public String generateEmail(String recipientName, String topic, String tone) { // 1. 定义提示词模板使用 {variable} 作为占位符 String templateString 请以{tone}的语气为{recipientName}写一封关于{topic}的电子邮件。 邮件需要包含问候语、主体内容和结束语。 ; // 2. 创建 PromptTemplate 对象 PromptTemplate promptTemplate new PromptTemplate(templateString); // 3. 创建包含变量的 Map MapString, Object variables Map.of( recipientName, recipientName, topic, topic, tone, tone ); // 4. 渲染模板生成最终的 Prompt 对象 org.springframework.ai.prompt.Prompt prompt promptTemplate.create(variables); // 5. 调用模型 return chatClient.prompt(prompt).call().content(); } }然后创建一个控制器来调用这个 ServiceRestController RequestMapping(/email) public class EmailController { private final ContentGenerationService service; public EmailController(ContentGenerationService service) { this.service service; } GetMapping(/generate) public String generateEmail(RequestParam String name, RequestParam String topic, RequestParam(defaultValue 专业) String tone) { return service.generateEmail(name, topic, tone); } }访问http://localhost:8080/email/generate?name张经理topic项目季度汇报tone正式你将得到一封根据参数动态生成的邮件草稿。4. 高级功能输出解析与结构化数据大模型返回的是文本但我们常常希望得到结构化的数据如 JSON、List、自定义对象。Spring AI 的OutputParser可以优雅地处理这个问题。4.1 解析为简单类型或集合假设我们想让模型生成一个关于某个技术的优缺点列表。import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.parser.ListOutputParser; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.stereotype.Service; import java.util.List; Service public class AnalysisService { private final ChatClient chatClient; public AnalysisService(ChatClient chatClient) { this.chatClient chatClient; } public ListString analyzeTechnology(String techName) { String promptText 请列出技术 {tech} 的三个主要优点和两个主要缺点。 请严格按照以下格式返回每行一项不要有序号 优点xxx 优点xxx 优点xxx 缺点xxx 缺点xxx .replace({tech}, techName); // 使用 ListOutputParser并指定每行文本的格式 ListOutputParser parser new ListOutputParser(new DefaultConversionService()); String result chatClient.prompt() .user(promptText) .call() .content(); // 手动解析示例实际可根据parser更智能地处理 // 这里简单按行分割并过滤空行 return List.of(result.split(\\r?\\n)); } }4.2 解析为自定义 Java Bean推荐这是最强大的功能之一。我们定义一个MovieInfo类然后让模型根据描述生成这个对象。首先定义 POJOpackage com.example.springaidemo.dto; import com.fasterxml.jackson.annotation.JsonPropertyDescription; public class MovieInfo { JsonPropertyDescription(电影的中文名称) private String title; JsonPropertyDescription(电影的主要导演) private String director; JsonPropertyDescription(电影上映的年份) private Integer releaseYear; JsonPropertyDescription(简要的电影剧情概述不超过100字) private String plotSummary; JsonPropertyDescription(电影的主要类型如科幻、喜剧、剧情) private ListString genres; JsonPropertyDescription(在豆瓣电影上的评分0-10分) private Double rating; // 省略构造函数、Getter和Setter、toString方法。务必生成它们。 }然后使用BeanOutputParserpackage com.example.springaidemo.service; import com.example.springaidemo.dto.MovieInfo; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Service; Service public class MovieInfoService { private final ChatClient chatClient; public MovieInfoService(ChatClient chatClient) { this.chatClient chatClient; } public MovieInfo extractMovieInfo(String description) { // 1. 创建针对 MovieInfo 类的解析器 BeanOutputParserMovieInfo parser new BeanOutputParser(MovieInfo.class); // 2. 构建提示词将解析器的格式指令嵌入其中 String userPrompt 请从以下描述中提取电影信息 {description} {format} ; // 3. 使用 ChatClient 调用并指定输出解析器 MovieInfo movieInfo chatClient.prompt() .user(u - u.text(userPrompt) .param(description, description) .param(format, parser.getFormat()) // 关键注入格式指令 ) .call() .entity(parser); // 关键指定解析器直接将响应转换为 MovieInfo 对象 return movieInfo; } }创建一个测试控制器GetMapping(/movie) public MovieInfo getMovieInfo(RequestParam String desc) { return movieInfoService.extractMovieInfo(desc); }访问http://localhost:8080/movie?desc这是一部1994年上映的美国电影由弗兰克·德拉邦特执导改编自斯蒂芬·金的小说讲述银行家安迪·杜佛兰含冤入狱后在肖申克监狱寻求自由与救赎的故事主题关于希望与友谊豆瓣评分9.7。你将直接得到一个结构化的MovieInfoJSON 对象。原理BeanOutputParser会生成一段关于 JSON 格式的指令并嵌入到提示词中。模型会“理解”并尝试返回符合该格式的 JSON 字符串然后解析器将其反序列化为 Java 对象。JsonPropertyDescription注解能帮助模型更好地理解字段含义。5. 集成其他模型与配置详解Spring AI 的强大之处在于可轻松切换模型。我们以切换为本地部署的 Ollama运行 Llama 2 等开源模型为例。5.1 集成 Ollama (本地模型)首先修改pom.xml替换或新增依赖!-- 移除或注释掉 openai starter -- !-- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency -- !-- 添加 Ollama starter -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency然后更新application.yml配置spring: ai: ollama: base-url: http://localhost:11434 # Ollama 服务默认地址 chat: options: model: llama2 # 你本地 Ollama 拉取的模型名称如 llama2, mistral, qwen2:7b 等前提你需要在本地安装并运行 Ollama 并通过命令行拉取一个模型例如ollama pull llama2。确保 Ollama 服务在http://localhost:11434运行。神奇之处完成以上两步后你不需要修改任何 Java 代码。之前AiController、ChatController中注入的AiClient或ChatClient会自动指向 Ollama 服务。这就是抽象层的威力。5.2 通用配置参数详解无论是 OpenAI 还是 Ollama其配置项在 Spring AI 中都有统一的抽象。以下是一些关键配置spring: ai: openai: # 或 ollama, azure-openai, anthropic 等 api-key: sk-xxx base-url: https://api.openai.com/v1 # 可自定义端点用于代理或兼容API chat: options: model: gpt-3.5-turbo # 模型名称 temperature: 0.7 # 创造性 (0-2)越低越确定 max-tokens: 500 # 生成的最大令牌数 top-p: 0.95 # 核采样参数 frequency-penalty: 0.0 # 频率惩罚 presence-penalty: 0.0 # 存在惩罚 embedding: options: model: text-embedding-ada-002 # 嵌入模型用于向量化你可以在代码中覆盖这些全局选项public String generateWithOptions(String message) { OpenAiChatOptions options OpenAiChatOptions.builder() .withTemperature(0.2) .withMaxTokens(200) .build(); return chatClient.prompt() .user(message) .options(options) // 传入本次调用的特定选项 .call() .content(); }6. 常见问题与排查思路在实际集成过程中你可能会遇到以下问题。这里提供一个排查清单。问题现象可能原因排查步骤与解决方案启动报错No qualifying bean of type AiClient1. 未添加对应的 Spring AI starter 依赖。2. 依赖冲突或版本不兼容。3. 配置错误导致 Bean 无法创建。1. 检查pom.xml或build.gradle确认已添加正确的 starter如spring-ai-openai-spring-boot-starter。2. 运行mvn dependency:tree检查依赖冲突确保 Spring AI BOM 版本统一。3. 检查application.yml中spring.ai.*的配置是否正确特别是api-key和base-url。调用 API 超时或连接被拒绝1. 网络问题无法访问模型 API 端点。2. Ollama 等服务未启动。3. 代理配置问题。1. 使用curl或 Postman 测试 API 端点是否可达注意直接 curl OpenAI 需要带 Key。2. 检查 Ollama 服务状态 (ollama serve)。3. 如果使用代理可在base-url中配置代理地址或为 HTTP 客户端配置全局代理。返回错误401 UnauthorizedAPI Key 无效、过期或未正确配置。1. 确认api-key配置正确且环境变量已生效。2. 检查 Key 是否有访问目标模型的权限。3. 对于 Azure OpenAI还需要配置api-version等额外参数。返回错误429 Rate limit exceeded请求频率超过模型供应商的限制。1. 降低应用的调用频率加入延迟或队列。2. 检查计费套餐的速率限制。3. 实现重试机制可使用 Spring Retry。BeanOutputParser解析失败1. 模型返回的 JSON 格式不正确。2. POJO 字段与提示词描述不匹配。3. 模型未遵循格式指令。1. 打印出模型返回的原始文本 (result.content())检查是否为合法 JSON。2. 确保 POJO 有无参构造器和 getter/setter。3. 在提示词中强化格式指令或换用更强大的模型如 GPT-4。4. 考虑使用JsonAlias处理字段名映射。内存占用过高长时间运行后1. 大模型响应可能很大占用内存。2. 向量存储或文档处理导致内存增长。1. 合理设置max-tokens。2. 对于流式响应使用流式 API 逐步处理。3. 定期监控 JVM 堆内存调整-Xmx参数。4. 检查是否有内存泄漏如缓存了过大的对话历史。7. 生产环境最佳实践与建议将 Spring AI 应用于生产环境除了功能实现还需关注稳定性、安全性和可维护性。7.1 配置管理与安全密钥管理绝对禁止将 API Key 提交至代码库。使用环境变量、云厂商的密钥管理服务如 AWS Secrets Manager, Azure Key Vault或配置中心如 Apollo, Nacos。配置分离将模型参数如temperature、maxTokens提取到外部配置中便于不同环境测试/生产和场景创意生成/严谨分析切换。端点隔离为生产环境配置独立的模型 API 端点并与测试环境隔离。7.2 稳定性与容错超时与重试配置合理的连接超时、读取超时。对于可重试的错误如网络抖动、429错误集成 Spring Retry 实现自动重试。Retryable(value {ResourceAccessException.class}, maxAttempts 3, backoff Backoff(delay 1000)) public String callAiWithRetry(String prompt) { // 调用 AI 客户端 }熔断与降级使用 Resilience4j 或 Sentinel 实现熔断器。当 AI 服务不稳定时快速失败并返回预设的降级内容如“服务繁忙请稍后再试”或缓存的结果避免拖垮整个应用。限流在应用层对调用 AI 的接口进行限流防止意外流量或恶意请求导致 API 费用激增。7.3 监控与可观测性日志记录记录每次调用的请求提示词可脱敏、响应时间、令牌使用量、模型名称和是否成功。这有助于成本分析和问题排查。指标收集使用 Micrometer 将调用耗时、成功率、令牌消耗等指标暴露给 Prometheus并在 Grafana 中制作监控看板。链路追踪在分布式系统中将 AI 调用纳入链路追踪如 SkyWalking, Zipkin了解其在整体请求链路中的影响。7.4 性能与成本优化提示词优化精心设计提示词明确指令和上下文减少不必要的交互轮次和无效输出这是降低成本最有效的方式。缓存策略对于内容生成类且结果相对固定的请求如根据固定模板生成文案可以考虑将结果缓存一段时间如 Redis避免重复调用。模型选型并非所有任务都需要最强大的模型。根据场景选择性价比合适的模型例如简单的文本分类可用小模型复杂的创意写作再用大模型。异步与非阻塞对于耗时较长的 AI 调用考虑使用Async或 WebFlux 进行异步处理避免阻塞 HTTP 线程提升应用吞吐量。7.5 架构考量服务抽象即使使用 Spring AI也建议在AiClient/ChatClient之上再封装一层业务层的 Service。这有助于未来切换 AI 提供商、增加统一日志/监控、实现降级策略等。对话状态管理对于多轮对话应用需要设计会话Session管理将消息历史持久化到数据库或分布式缓存中而不是存在内存里。RAG 应用如果需要基于自有知识库问答尽早规划向量存储Vector Store的选型如 Redis, PGVector, Milvus、文档切分Chunking策略和检索流程。Spring AI 极大地降低了大模型能力的集成门槛但构建一个健壮、高效、可控的生产级 AI 应用仍然需要我们在软件工程的各个方面下功夫。从清晰的配置管理、完善的异常处理到细致的监控告警每一步都关乎最终系统的稳定性和用户体验。建议从一个小而具体的功能点开始实践逐步迭代积累经验最终构建出真正为企业赋能的智能应用。