
如果你正在寻找一个能快速将大模型能力集成到 Java 应用中的框架却发现市面上的教程要么版本过时、配置复杂要么只讲概念、缺乏实战那么这篇文章就是为你准备的。Spring AI 的出现本质上解决了一个核心痛点让 Java 开发者能以熟悉的 Spring 生态方式像调用数据库或消息队列一样去调用 OpenAI、Azure OpenAI、Ollama 等各类大模型服务。它抽象了不同模型供应商的 API 差异提供了统一的编程模型将大模型从“需要特殊处理的 HTTP 客户端”变成了“Spring 容器中的一个 Bean”。然而很多教程在介绍时容易陷入两个误区一是过度聚焦于某个特定模型如只讲 OpenAI忽略了 Spring AI 作为“抽象层”的核心价值二是把示例写得太“玩具化”没有触及生产环境中的真实配置、异常处理和最佳实践。本文将基于最新的 Spring AI 稳定版本带你一站式掌握其核心用法。我的核心判断是Spring AI 的价值不在于让你“学会”调用某个 API而在于它定义了一套标准化的“模型交互范式”。掌握它你就能以极低的切换成本在项目中对不同模型进行 A/B 测试、实现降级回退甚至构建自己的模型路由策略。文章将避开那些华而不实的介绍直接切入环境搭建、核心 API 使用、流式响应处理、提示词工程管理以及生产级配置等实战环节确保你读完就能在项目中用起来。1. Spring AI 解决了什么问题为什么是现在在 Spring AI 出现之前一个 Java 后端团队想要集成大模型典型的路径是这样的先为选定的模型比如 OpenAI引入一个第三方 Java SDK然后在代码里硬编码 API Key 和 Endpoint自己处理 HTTP 请求、解析 JSON 响应、管理连接池和超时设置。如果想换一个模型比如切换到 Azure OpenAI 或本地部署的 Ollama几乎需要重写所有相关代码。这个过程存在几个明显问题供应商锁定代码与特定模型的 API 强耦合迁移成本高。重复劳动每个微服务都要重复实现一套相似的客户端逻辑。配置繁琐密钥管理、超时、重试等配置分散在各处难以统一管理。能力抽象缺失缺乏对“对话”、“文生图”、“嵌入”等高层概念的统一抽象开发者需要关注底层 HTTP 细节。Spring AI 的定位就是成为Java 大模型应用开发的基础设施。它借鉴了 Spring Data 的成功经验Spring Data 让你用统一的 Repository 接口操作不同的数据库MySQL、MongoDB、Redis而 Spring AI 则让你用统一的ChatClient、EmbeddingClient等接口操作不同的大模型。为什么现在需要关注它因为大模型应用正在从“演示原型”走向“生产系统”。原型阶段可以忍受硬编码和散落的配置但生产系统要求可维护性、可观测性、安全性和弹性。Spring AI 与 Spring Boot 的自动配置、Actuator 监控、Security 安全机制天然集成为构建企业级 AI 应用提供了现成的脚手架。它降低的不是“调用 API”的难度而是“在复杂工程体系中可靠、安全、高效地使用 AI 能力”的难度。2. 核心概念与项目结构理解 Spring AI 的抽象层开始写代码前必须理解 Spring AI 的几个核心抽象。这是避免后续混乱的关键。2.1 核心 API 接口Spring AI 的核心是几个高度抽象的客户端接口ChatClient: 用于与对话模型交互如 GPT-4、Claude。这是最常用的接口。EmbeddingClient: 用于将文本转换为向量嵌入这是构建 RAG检索增强生成应用的基础。ImageClient: 用于文生图、图生图等图像生成任务。AudioClient: 用于语音转录、语音合成等音频任务部分模型支持。VectorStore: 用于存储和检索向量数据与EmbeddingClient配合使用是 RAG 的另一个核心。这些接口是稳定的契约。无论底层是 OpenAI、Azure、Anthropic 还是 Ollama你的业务代码都只依赖这些接口从而实现解耦。2.2 提示词模板与消息抽象与大模型交互的核心是“提示词”。Spring AI 提供了Prompt和PromptTemplate类来结构化提示词。Message: 代表对话中的一条消息有SystemMessage、UserMessage、AssistantMessage等类型。Prompt: 包含一个或多个Message的集合代表一次请求的完整上下文。PromptTemplate: 一个强大的工具允许你创建带有占位符如{topic}的提示词模板并通过传入参数动态渲染。这有助于实现提示词的复用和管理。2.3 项目依赖与模块化Spring AI 采用模块化设计。你的pom.xml或build.gradle中通常会包含两部分依赖Spring AI BOM统一管理所有 Spring AI 模块的版本避免依赖冲突。具体实现 Starter例如spring-ai-openai-spring-boot-starter表示使用 OpenAI 的实现。如果你想换用 Azure OpenAI只需替换为spring-ai-azure-openai-spring-boot-starter业务代码通常无需改动。这种设计使得“更换模型提供商”变成了一个简单的依赖替换和配置更新操作。3. 环境准备与项目初始化我们从一个干净的 Spring Boot 3.x 项目开始。这是目前最稳定、兼容性最好的组合。3.1 前置条件JDK 17 或更高版本Spring Boot 3.x 的最低要求。Maven 3.6 或 Gradle 7.x构建工具。一个可用的模型 API为了完成所有示例你需要准备以下至少一项OpenAI API Key从 platform.openai.com 获取。Azure OpenAI 资源需要 Endpoint、API Key 和 Deployment Name。本地 Ollama在本地安装并运行 Ollama拉取一个模型如llama3。3.2 创建 Spring Boot 项目使用 Spring Initializr (start.spring.io) 创建项目选择Project: MavenLanguage: JavaSpring Boot: 3.2.x (建议选择当前最新的稳定版)Dependencies: 先只选Spring Web。生成项目后在 IDE 中打开。3.3 添加 Spring AI 依赖编辑pom.xml文件。首先添加 Spring AI 的 BOM物料清单来统一版本管理。!-- 在 project 标签下与 parent 标签同级添加 -- dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version0.8.1/version !-- 请检查官网使用最新稳定版 -- typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement然后根据你想使用的模型添加对应的 Starter 依赖。这里我们以OpenAI和Ollama为例因为前者是云服务代表后者是本地运行代表。dependencies !-- Spring Boot 基础依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI OpenAI 实现 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId !-- 版本由上面的 BOM 控制 -- /dependency !-- 可选如果你想同时连接本地 Ollama -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency !-- 开发工具 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies关键点spring-ai-bom的引入至关重要它能确保所有 Spring AI 相关组件的版本一致避免潜在的兼容性问题。4. 基础配置连接你的大模型配置是第一步也是最容易出错的一步。Spring AI 的配置高度统一遵循spring.ai.provider.property的格式。4.1 配置 OpenAI如果你使用 OpenAI在application.properties或application.yml中配置# application.properties # OpenAI 配置 spring.ai.openai.api-key${OPENAI_API_KEY:sk-your-key-here} spring.ai.openai.chat.options.modelgpt-3.5-turbo # 可选设置超时、代理等 spring.ai.openai.chat.options.temperature0.7或者使用 YAML 格式# application.yml spring: ai: openai: api-key: ${OPENAI_API_KEY:sk-your-key-here} chat: options: model: gpt-3.5-turbo temperature: 0.7安全提醒永远不要将真实的 API Key 硬编码在代码或配置文件中提交到代码仓库。这里使用${OPENAI_API_KEY:defaultValue}语法意味着优先从环境变量OPENAI_API_KEY中读取如果不存在则使用冒号后的默认值。生产环境中应使用配置中心或 Secrets 管理工具。4.2 配置本地 Ollama如果你使用本地运行的 Ollama默认地址是http://localhost:11434配置更简单# Ollama 配置 spring.ai.ollama.base-urlhttp://localhost:11434 spring.ai.ollama.chat.options.modelllama3Ollama 无需 API Key适合本地开发和测试。4.3 配置多个模型客户端Spring AI 支持同时配置多个同类型客户端比如两个不同的ChatClient并通过Qualifier注解来注入指定的那个。这为 A/B 测试和故障转移奠定了基础。首先在配置中定义多个连接。以下示例展示如何配置一个 OpenAI 和一个 Ollama 客户端spring: ai: openai: api-key: ${OPENAI_API_KEY} chat: options: model: gpt-4 enabled: true # 启用这个客户端 ollama: base-url: http://localhost:11434 chat: options: model: llama3 enabled: true # 启用这个客户端然后在代码中可以通过 Bean 名称来注入特定的客户端。Spring AI 会自动根据配置生成名为openaiChatClient和ollamaChatClient的 Bean。5. 核心使用从简单对话到流式响应配置完成后我们就可以在 Service 或 Controller 中注入并使用ChatClient了。5.1 基础对话调用创建一个简单的 Service 类// 文件路径src/main/java/com/example/demo/service/ChatService.java package com.example.demo.service; import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Service; Service public class ChatService { private final ChatClient chatClient; // 构造器注入。如果只有一个 ChatClient BeanSpring 会自动注入它。 public ChatService(ChatClient chatClient) { this.chatClient chatClient; } public String generate(String message) { // 最简调用直接传入用户消息字符串 return chatClient.prompt() .user(message) .call() .content(); } }创建一个 REST 控制器来暴露接口// 文件路径src/main/java/com/example/demo/controller/AiController.java package com.example.demo.controller; import com.example.demo.service.ChatService; 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 ChatService chatService; public AiController(ChatService chatService) { this.chatService chatService; } GetMapping(/chat) public String chat(RequestParam(value msg, defaultValue Hello) String message) { return chatService.generate(message); } }启动应用访问http://localhost:8080/chat?msg介绍一下Spring AI你应该能收到模型的文本回复。5.2 使用 Prompt 和 Message 构建复杂上下文上面的例子是单轮对话。要实现多轮对话或设置系统指令需要使用Prompt和Message。// 在 ChatService 中添加新方法 import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import java.util.List; public ChatResponse chatWithContext(String userInput, String systemInstruction) { // 1. 构建消息列表 ListMessage messages List.of( new SystemMessage(systemInstruction), // 系统指令设定 AI 角色 new UserMessage(userInput) // 用户输入 ); // 2. 创建 Prompt Prompt prompt new Prompt(messages); // 3. 调用并返回完整的 ChatResponse包含元数据 return chatClient.call(prompt); } // 调用示例 public String getTranslation(String englishText) { ChatResponse response chatWithContext( Translate the following English text to Chinese: englishText, You are a professional translator. ); // 从 ChatResponse 中提取助理的回复内容 return response.getResult().getOutput().getContent(); }ChatResponse对象包含了更丰富的信息如使用到的 token 数量、模型名称、完成原因等对于监控和调试非常有用。5.3 实现流式响应Server-Sent Events流式响应对于生成长文本时的用户体验至关重要可以实时看到模型生成的内容。Spring AI 的ChatClient原生支持流式调用。// 文件路径src/main/java/com/example/demo/controller/StreamController.java package com.example.demo.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.http.MediaType; 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 StreamController { private final ChatClient chatClient; public StreamController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(value /chat/stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString streamChat(RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); // 返回一个 FluxString每个元素是实时生成的文本块 } }使用curl或前端 EventSource API 测试这个端点你可以看到文字逐词返回的效果curl -N http://localhost:8080/chat/stream?message写一个关于春天的短诗6. 进阶功能提示词模板与函数调用6.1 使用 PromptTemplate 管理提示词将提示词模板化是生产应用的最佳实践。Spring AI 提供了强大的PromptTemplate。// 在 ChatService 中添加方法 import org.springframework.ai.chat.prompt.PromptTemplate; import java.util.Map; public String generateWithTemplate(String topic, String style) { // 定义模板字符串使用 {parameter} 作为占位符 String templateString Please write a {style} paragraph about {topic}. The paragraph should be engaging and suitable for a general audience. ; // 创建 PromptTemplate PromptTemplate promptTemplate new PromptTemplate(templateString); // 传入参数 Map 来渲染模板 MapString, Object params Map.of(topic, topic, style, style); Prompt renderedPrompt promptTemplate.create(params); // 使用渲染后的 Prompt 进行调用 return chatClient.call(renderedPrompt).getResult().getOutput().getContent(); }你可以更进一步将模板字符串存储在数据库或配置文件中实现动态的提示词管理。6.2 函数调用Tool Calling让大模型调用外部工具或函数是构建智能 Agent 的关键。Spring AI 通过Bean定义工具函数并自动将其描述注入到对话上下文中。首先定义一个工具函数接口及其实现// 文件路径src/main/java/com/example/demo/tool/WeatherService.java package com.example.demo.tool; import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Component; Component public class WeatherService { Tool(description Get the current weather for a given city) public String getWeather(String city) { // 这里应该是调用真实天气 API 的逻辑 // 为演示返回模拟数据 return The weather in city is sunny with a temperature of 22°C.; } }然后在配置中启用函数调用并在调用时指定可用的工具// 在 Controller 或 Service 中 import org.springframework.ai.chat.client.advisor.ToolCallAdvisor; GetMapping(/chat-with-weather) public String chatWithTool(RequestParam String question) { return chatClient.prompt() .user(question) .advisors(new ToolCallAdvisor()) // 启用工具调用顾问 .call() .content(); }当你问“北京天气怎么样”时模型会识别出需要调用getWeather工具Spring AI 会拦截这个请求执行真实的getWeather方法并将结果返回给模型模型再整合成最终回答。这极大地扩展了大模型的能力边界。7. 向量数据库与 RAG 快速入门RAG 是当前最主流的让大模型获取“新知识”并减少幻觉的方法。Spring AI 提供了VectorStore抽象和EmbeddingClient来简化实现。7.1 生成嵌入向量首先确保你的模型支持嵌入如 OpenAI 的text-embedding-ada-002。配置EmbeddingClient配置方式与ChatClient类似通常与 Chat 模型来自同一个提供商。// 注入 EmbeddingClient private final EmbeddingClient embeddingClient; public ListDouble embedText(String text) { // 将单段文本转换为向量 ListDouble embedding embeddingClient.embed(text); return embedding; }7.2 使用简单的内存向量存储Spring AI 内置了InMemoryVectorStore适合演示和开发测试。// 文件路径src/main/java/com/example/demo/service/RagService.java package com.example.demo.service; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.vectorstore.InMemoryVectorStore; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; Service public class RagService { private final VectorStore vectorStore; public RagService(EmbeddingClient embeddingClient) { // 初始化一个内存向量存储需传入 EmbeddingClient this.vectorStore new InMemoryVectorStore(embeddingClient); } // 1. 向向量库添加文档 public void addDocuments(ListString texts) { ListDocument documents texts.stream() .map(text - new Document(text)) // 可以添加元数据 .collect(Collectors.toList()); vectorStore.add(documents); } // 2. 相似性搜索 public ListDocument search(String query, int topK) { return vectorStore.similaritySearch(query, topK); } // 3. 简单的 RAG 查询 public String ragQuery(String question) { // a. 检索相关文档 ListDocument relevantDocs search(question, 3); String context relevantDocs.stream() .map(Doc::getContent) .collect(Collectors.joining(\n\n)); // b. 构建增强后的提示词 String promptTemplate Answer the question based only on the following context: {context} Question: {question} If the context doesnt contain the answer, say I cannot answer based on the provided information. Answer: ; // 使用 PromptTemplate 渲染此处省略具体渲染代码 // c. 调用 ChatClient 获取答案 // ... 调用 chatClient return 最终答案; } }对于生产环境你需要将InMemoryVectorStore替换为PgVectorStorePostgreSQL、RedisVectorStore或MilvusVectorStore等持久化存储。8. 生产环境配置与最佳实践将 Spring AI 用于生产需要注意以下几点8.1 配置管理密钥管理使用环境变量、云厂商的 Secrets Manager如 AWS Secrets Manager、Azure Key Vault或 Spring Cloud Config。连接池与超时配置 HTTP 客户端参数防止慢请求拖垮应用。spring: ai: openai: client: connect-timeout: 10s read-timeout: 30s多环境配置使用application-dev.yml,application-prod.yml区分不同环境的模型和配置。8.2 异常处理与重试大模型 API 调用可能因网络、限流等原因失败。务必添加健壮的异常处理和重试机制。import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Backoff; Service public class RobustChatService { Retryable( value { RuntimeException.class }, // 重试的异常类型 maxAttempts 3, backoff Backoff(delay 1000, multiplier 2) // 指数退避 ) public String reliableGenerate(String prompt) { // 调用 ChatClient // 如果抛出 RuntimeException会自动重试最多3次 return chatClient.prompt().user(prompt).call().content(); } // 还可以使用 Recover 定义重试全部失败后的降级处理 Recover public String recover(RuntimeException e, String prompt) { return Service is temporarily unavailable. Please try again later.; } }同时在pom.xml中添加spring-retry依赖以启用此功能。8.3 监控与可观测性Actuator 端点Spring Boot Actuator 可以暴露应用健康指标。自定义指标使用 Micrometer 记录每次调用的耗时、Token 使用量、成功率等。日志记录为ChatClient和EmbeddingClient的调用添加详细的日志但注意不要记录包含敏感信息的完整提示词和响应。8.4 成本与性能优化缓存对相似的 Embedding 请求或 Chat 结果进行缓存如使用 Spring Cache。批处理对于 Embedding 操作如果可能将多个文本批量发送以减少请求次数。模型选择在非关键路径或内部工具中使用更便宜、更快的模型如 GPT-3.5-Turbo在面向用户的核心功能中使用能力更强的模型如 GPT-4。9. 常见问题与排查思路在开发和集成过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案启动报错No qualifying bean of type ChatClient1. 未添加对应模型的 Starter 依赖。2. 配置错误如 API Key 为空。3. 多个ChatClientBean 存在冲突。1. 检查pom.xml依赖。2. 检查application.yml配置确保api-key或base-url正确。3. 查看启动日志确认 Bean 创建情况。1. 添加正确的 Starter。2. 配置正确的密钥或地址。3. 使用Qualifier(beanName)指定注入的 Bean。调用 API 超时1. 网络问题。2. 模型响应慢。3. 客户端超时设置过短。1. 检查网络连通性。2. 查看模型服务状态。3. 检查connect-timeout和read-timeout配置。1. 优化网络或使用代理。2. 考虑使用更快的模型或优化提示词。3. 适当增加超时时间并配置重试。流式响应不工作1. 控制器 produces 类型不是MediaType.TEXT_EVENT_STREAM_VALUE。2. 前端 EventSource 使用方式错误。3. 模型不支持流式响应。1. 检查GetMapping注解。2. 用curl -N测试后端接口是否正常流式输出。3. 查阅模型供应商文档。1. 确保 produces 类型正确。2. 修正前端代码或使用正确的测试工具。3. 更换模型或使用非流式调用。提示词模板渲染错误1. 模板字符串中的占位符与传入的 Map key 不匹配。2. 使用了不支持的表达式语法。1. 检查PromptTemplate创建时传入的参数 Map。2. 查看 Spring AI 文档确认模板语法。1. 确保参数 Map 的 key 与模板中的{key}一致。2. 使用简单的{key}语法或查阅文档使用高级特性。向量搜索返回空结果1. 向量库中未添加文档。2. Embedding 模型与创建向量时使用的模型不一致。3. 搜索相似度阈值过高。1. 确认addDocuments方法被成功调用。2. 检查EmbeddingClient的配置是否一致。3. 调试查看生成的向量维度。1. 确保数据已成功入库。2. 确保查询和入库使用相同的EmbeddingClient配置。3. 调整搜索的相似度阈值参数如果向量存储支持。掌握 Spring AI 意味着你掌握了在 Java 世界高效集成 AI 能力的标准化方法。它不是一个孤立的工具而是 Spring 生态向 AI 时代自然延伸的一部分。建议从本文的示例出发先在一个小的内部工具或辅助功能中尝试集成重点体验配置的简洁性和 API 的一致性。然后逐步探索更复杂的场景如基于 RAG 的智能知识库、利用函数调用连接内部业务系统的智能助手或是构建多模型路由的策略服务。在这个过程中持续关注官方文档和版本更新因为 Spring AI 正在快速发展越来越多的模型供应商和向量数据库正在被集成进来。