
1. 为什么我强烈建议Java开发者关注Spring AI过去一年我身边几乎每个搞Java后端的朋友都在问同一个问题团队里Python那边用LangChain写得飞起Java这边有没有对应的轮子有的而且它已经不算新了——Spring AI。我第一次接触这个项目是在Spring生态的官方仓库里看到的当时它还很早期连1.0都没发到但设计思路已经令我眼前一亮。它解决的问题非常朴素让Java开发者能用统一的、Spring风格的方式去对接各类大模型而不是每个模型一套SDK每换一个厂商改一遍业务代码。很多人把它类比成Java世界的LangChain这个说法在思路上是对的但实际上Spring AI更接近Spring家族一贯的做事方式——把复杂的东西包装成熟悉的抽象让开发者在原有知识体系上平滑扩展。你在Spring Boot里怎么用RestTemplate、怎么用JdbcTemplate在Spring AI里就怎么用ChatClient、ChatModel。这种一致性带来的上手成本极低我实测下来一个平时只写CRUD的Java工程师从零到跑通第一个对话案例大概只需要半小时。这个项目特别适合几类人已经在做Spring Boot后端开发的团队、希望在不引入Python技术栈的情况下快速验证AI功能的产品小组、以及想低成本接触大模型API的独立开发者。作为入门博文我不会堆概念接下来我会从设计思路讲起带你把环境搭好用最朴素的代码跑通一个真实可用的对话案例然后聊一聊我踩过的坑。如果你之前完全没有接触过AI编程只要懂Spring Boot基础照着操作也能跑起来。2. Spring AI的核心设计统一抽象与和Spring生态的融合2.1 统一模型抽象它到底在抽象什么大模型接入这件事本质上就是“把文本请求发给模型接口然后把模型返回的文本接回来”。听起来简单但真正落到工程上就全是细节不同厂商有不同的请求格式、不同的鉴权方式、不同的超时策略有的模型还支持流式输出、多模态输入、function calling。如果你直接在各家SDK之上写业务代码等要换模型或者同时接多个模型的时候那酸爽程度不亚于在三个数据库方言之间手写迁移层。Spring AI把这一层统一成了几个非常清晰的接口ChatModel是聊天模型的总入口ChatClient是面向业务代码的同步/流式调用门面Prompt封装完整请求ChatResponse封装模型响应。我在实际使用中最舒服的一点是业务代码几乎不感知模型差异。你对接OpenAI和对接Ollama本地模型写的调用代码是一样的区别只在配置项——就像DataSource一样一个接口MySQL和PostgreSQL各自的Driver各不相同但业务代码永远只面向DataSource编程。2.2 Prompt与结构化输出AI接口也可以强类型很多人第一次接触Spring AI的时候会忽略Prompt对象的含义。它不只是“一段字符串”而是把System消息、User消息、历史对话记录、输出格式要求、温度参数等打包成一个结构化的请求体。我更喜欢把它理解成“把开发者在网页聊天框里手动调参数的过程固化到代码里”。Spring AI还内置了结构化输出解析。举个例子你让模型返回一个JSON它偶尔会夹带解释性文字或者markdown代码块直接解析JSONObject会炸。Spring AI的BeanOutputConverter可以指定目标类型它在构造Prompt时就把“只输出JSON、不要任何解释”写进System消息并且能把返回文本自动映射到Java对象上。我第一次用这个功能的时候有点意外居然连这类最容易出错的格式对齐问题都内建处理了。2.3 和Spring生态的联动不是空降兵是自家人Spring AI真正区别于其他Java AI框架的地方在于它不是“嵌在Spring里的一座孤岛”而是融入了Spring的整个基础设施。你可以直接通过application.yml配置模型参数可以像用Service一样注册自定义组件也可以借助RestTemplate、WebClient的既有经验去理解它底层的请求逻辑。更关键的是它能直接复用Spring Boot的Actuator端点做健康检查用Spring Cache缓存模型结果用Spring Retry做重试策略。接入成本低到有些像“在已经建好的房子里多接了一根水管”不需要你推倒重来。3. 环境准备与项目初始化先把路铺平3.1 版本选择与依赖导入按照我目前的最佳实践建议先准备JDK 17及以上版本配合Spring Boot 3.x。Spring AI的版本迭代在2025年下半年开始明显加速早期的一些API在后续版本里有调整比如曾经的OpenAiAutoConfiguration以及AiClient相关写法逐步被替换成以ChatClient和ChatModel为核心的新模型所以你在网上搜索时可能会看到旧版教程记得留意文章标注的版本。我用的是当前较新的稳定版本如果你不确定选哪个去官方文档找参考文档对应版本即可核心思路没变。Maven坐标方面Spring AI提供了一组专门的Starter典型像spring-ai-starter-model-openai、spring-ai-starter-model-ollama。在选择时有一个容易踩的坑不要只用一个spring-ai-core就以为万事大吉不同模型对应不同Starter依赖引入错位会导致ChatModel注入失败。我个人入门阶段的建议是从OllamaStarter开始因为它不依赖外部网络和API Key调试起来最省心。3.2 镜像仓库配置这一项很多人没说透国内拉取Spring AI依赖时默认Maven中央仓库经常会因为网络问题卡住。我这里直接说我的做法将阿里云公共仓库作为主要下载源同时在settings.xml里保留中央仓库作为fallback。这样做的原因是Spring AI的部分依赖传递路径比较深如果只配一个镜像偶尔会出现某个传递依赖在镜像里还没有同步的情况双仓库兜底能明显减少头疼程度。配置完仓库后首次构建会下载不少依赖耐心等待即可。3.3 Ollama本地模型最干净的入门环境如果你不想在注册云厂商和申请API Key上花时间我建议直接用Ollama跑一个本地小模型。安装Ollama之后在命令行执行一条拉取命令即可把模型下载到本地例如拉一个轻量对话模型。首次对话时模型需要在内存中加载响应会比后续慢一些但体验已经足够流畅。本地模型的好处是数据不出机器、无额外费用网络隔离环境下也能运行这对很多企业内部验证特别友好。入门阶段完全可以用它来跑通全链路等理解了核心流程再切换到云端模型就非常顺手了。4. 入门案例实操十分钟写出你的第一个AI对话接口4.1 项目骨架与核心配置我直接以实际操作为例。新建一个Spring Boot项目JDK版本选择17Spring Boot版本选择3.x。项目结构只需要一个启动类、一个Controller和一个Service不需要额外引入web以外的组件。关键的依赖坐标和application.yml配置我先给出解释放在代码后。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-ollama/artifactId /dependency /dependenciesspring: ai: ollama: base-url: http://localhost:11434 chat: model: qwen2.5:7b options: temperature: 0.8 max-tokens: 2048这两段配置的作用我用大白话解释一下base-url是Ollama服务的地址默认是本地11434端口model指定你本地已经拉好的模型名称temperature是控制模型回答“自由发挥程度”的参数0到1之间越高回答越发散越低回答越保守我常用0.8得到有内容但不至于乱跑的回复max-tokens限制单次回复最大长度防止某些场景下模型长篇大论把接口响应时间拖垮。4.2 一个能直接复用的ChatClient配置ChatClient是Spring AI提供的流式门面接口它在较新版本中是推荐用法。我不建议直接用ChatModel裸调因为ChatClient把很多繁琐的请求组装细节都收敛了。定义一个Bean如下Configuration public class AiConfig { Bean public ChatClient chatClient(ChatClient.Builder builder) { return builder.build(); } }这段代码的作用是向容器中注册一个ChatClient实例后面业务代码里直接注入即可。有个小提醒如果你的依赖引入正确ChatClient.Builder会自动被Spring装配好不需要额外配置。这里的Builder模式让我想起来WebClient.Builder保持了Spring一贯的设计感。4.3 写一个简单的对话接口Controller层的代码是最直接的RestController RequestMapping(/ai) public class AiController { private final ChatClient chatClient; public AiController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(/chat) public String chat(RequestParam(name msg, defaultValue 你好) String msg) { return chatClient.prompt() .user(msg) .call() .content(); } }这段代码的逻辑非常清晰接收请求参数msg把它作为用户消息发给模型同步调用最终返回模型生成的文本。.call()表示同步阻塞等待结果.content()提取回复文本。整个流程让我想起用JdbcTemplate查数据——骨架固定、意图明确唯一要关心的就是业务逻辑本身。4.4 增加历史对话能力改造为多轮对话如果你想实现真正的连续对话客户端每次把一个用户消息发给模型服务端还需要把历史消息一起传过去。Spring AI里可以用ChatMemory或者在每次请求时手动拼接消息列表。对于入门案例我更推荐先理解“手动拼接”的方式因为它直观展示了请求的构成本质消息数组。伪代码如下RequestMapping(/chat/history) public String chatWithHistory(RequestParam String msg, RequestParam String history) { return chatClient.prompt() .system(你是一个乐于助人的助手回答简洁直接。) .user(msg) .call() .content(); }这里的.system()方法设置了系统提示词它决定了模型的“人设”和回答风格。多轮对话则是把历史消息按照system、user、assistant交替拼进Prompt这跟直接调用模型API时组织消息列表的思路完全一致。一旦你理解了这个组织形式再看任何大模型API文档都很轻松。4.5 流式输出与JSON返回入门后必做的两个增强同步接口足够演示核心流程但真实项目里往往需要流式输出——模型边生成边返回像打字机一样把字敲出来。Spring AI的流式写法也非常简洁调用.stream()替代.call()拿到FluxString后通知前端。后面有需要做SSE推送的这个能力会很关键。第二个增强是让模型返回JSON。我用的BeanOutputConverter示例大致是这样public record BookInfo(String title, String author, int pages) {} BeanOutputConverterBookInfo converter new BeanOutputConverter(BookInfo.class); String response chatClient.prompt() .user(请以JSON格式返回一本你推荐的技术书籍信息) .system(converter.getSystemInstruction()) .call() .content(); BookInfo book converter.convert(response);使用getSystemInstruction()之后模型会被明确告知必须输出指定结构的JSON。实测下来格式规范的比率远高于直接靠“请返回JSON”这种方式。有人可能会问为什么不做成自动注入因为不同业务对目标类型的要求差异太大手动指定反倒让意图更清晰。5. 实战中逃不开的坑我的问题排查速查表5.1 Bean注入失败八成是starter引错了芯片圈里有一句玩笑话叫“Debug就是找谁在偷摸改配置”放到Spring AI这里很贴切。我遇到的第一类问题是运行时ChatClient.Builder或ChatModel无法注入报错信息看起来像“找不到类型为ChatClient.Builder的Bean”。排查时先确认是不是Starter引错了比如你只在pom里引入了spring-ai-core但没引入任何xxx-starter-model-xxxSpring自然不知道要装配哪个模型实现。换个具体的模型Starter之后这个问题基本消失。5.2 连接本地模型失败不只是地址写错Ollama模式下的报错往往是Connection refused。很多人第一反应是检查base-url但实际上还有几个容易忽略的点第一Ollama服务是否真的在运行第二端口是否被占用第三Ollama版本是否过旧导致接口不兼容。我的建议是先用curl直接请求一次Ollama的本地接口确认能通再去查Spring配置。这一步能帮你快速把问题锁定在“网络层”还是“框架层”。5.3 模型答非所问先别怀疑模型看看你的System提示词很多初学者在案例跑通后会急着把模型换到各厂商的大模型上然后反馈“模型回答质量不行”。我的排查经验是先看System Prompt是不是写得太空泛。你是AI助手和你是一名资深的Java架构师回答时先分析需求再给出代码示例最后总结注意事项前者几乎得不到有效输出。另外temperature设置太高也会让回答变得“飘”需要稳定业务回答时我通常会调到0.3以下。5.4 返回内容带Markdown代码块结构化输出了解一下早期我直接在代码里调用.content()拿结果然后尝试用Fastjson解析遇到模型输出中夹着json和的情况怎么都解析不通过。换用BeanOutputConverter后问题直接解决它不仅把格式要求写进System提示词还能在反序列化时做容错处理。如果你外接的模型不支持这种严谨的指令遵循能力也可以自己写一个后处理字符串的清理方法去掉三段式代码块标记再交给JSON解析器。5.5 超时问题本地模型比云端模型更需要耐心Ollama首次加载模型或者机器性能一般时响应时间可能会到几十秒甚至一分钟以上。Spring AI默认的超时时间在实际使用中可能不够宽裕。遇到超时报错我的做法是在Starter对应的配置属性里把响应超时调大至少给到60秒。这里顺便解释下我为什么推荐先搞模型加载预热把模型在本地跑起来让它先完成一次生成后面再调接口就快很多。5.6 常见问题速查表为了方便你按图索骥我把上面几个问题整理成一张表格实际排查的时候对照状态找原因即可。现象常见原因解决思路启动报错找不到ChatModel缺少模型对应的Starter在pom.xml引入具体模型Starter调用时报Connection refusedOllama服务未启动或端口异常先curl本地接口确认服务可用响应超时模型加载慢或配置超时短调大超时配置提前预热模型返回内容不是预期格式System提示词不明确细化System Prompt明确输出规范解析JSON失败返回内容夹带Markdown标记使用BeanOutputConverter或做后处理并发调用时内存占用高本地模型完全加载到内存调小模型参数量或改用云端API5.7 日志查看技巧把请求体打出来如果你需要进一步调试我个人强烈建议把Spring AI的请求日志打开方法是在application.yml中为org.springframework.ai设置DEBUG级别。这样每次调用时都会打印出完整的Prompt内容和模型响应摘要排查问题效率翻倍。我遇到过一种情况业务代码上写的Prompt看着没问题但打印出来发现历史消息的顺序是乱的导致模型理解错误。有了日志这个问题一眼就能看出来。6. 从入门到进阶两条值得深入的方向6.1 走通RAG让AI基于你的私有数据回答单纯问答的Spring AI只是皮真正的业务价值在于让模型“知道”你的数据。我当时接触RAG时的感受是它的机制特别像“给AI配了一个知识库秘书”。核心流程是把文档切块用向量化模型转成向量存入向量数据库用户提问时先把问题向量化在知识库里检索最相关的片段最后把片段组装进Prompt发给模型。Spring AI在这套流程里提供了非常自然的抽象VectorStore接口屏蔽了多种向量数据库的差异你自己实现一个类似“从本地知识目录加载文本”的服务再配合Embedding模型就能串完整链路。6.2 接Function Calling让模型学会调你的服务Function Calling是另一个值得尽早了解的能力。通俗解释模型在回答一个问题时发现需要某个实时数据比如天气信息、库存数量、订单状态它会把“调用某个函数”的意图以结构化参数返回给你由你的业务系统真正执行函数然后把函数结果返回给模型继续生成回答。Spring AI通过Tool注解或函数定义的方式让Java方法暴露给模型实现起来并不复杂。这代表着一个重要的方向转变AI不只是文本转换工具还能成为业务动作的编排者。6.3 谨慎选择模型参数别被默认值坑了入门阶段最容易被忽略的参数是temperature和max-tokens。我见过有人直接不配置用默认值跑业务结果在需要精确输出的场景里经常得到啰嗦甚至重复的回答。做FAQ类问答建议temperature调低做文案生成、头脑风暴可以调高。max-tokens如果给得太小回答会被截断模型生成到一半就停了字符串会明显不完整。设置这些参数时的核心思路只有一个根据具体场景的实验反馈来调整而不是照搬任何人的默认值。7. 写在最后从我的实操体会说起整套Spring AI入门流程走下来我最大的感受是这个框架把“接入大模型”的工程门槛降到了Spring Boot体系内不需要额外技术栈不需要学习新的请求规范大部分精力可以留在业务建模上。它不是一个多么惊艳的创新但它把一件原本有些凌乱的事情标准化了——就像当年Spring JDBC把数据访问统一起来一样。如果你也是刚接触这块我的建议不太一样不建议一开始就搭复杂的RAG或Agent先在本地模型上跑通一个最简单的对话接口把Prompt、ChatClient、BeanOutputConverter这些概念亲手用一遍再去尝试进阶能力。踩坑不可怕可怕的是在没弄懂基础流程的时候一头扎进复杂系统出了问题不知道是模型问题、网络问题还是代码问题。最后分享一个小技巧在你的Spring Boot工程里把不同模型的配置封装成独立的配置类切换模型时只需要调整Profile或配置项而业务代码不动。这样后续无论是引入新模型还是做A/B测试都只需要面对配置文件的增删。AI应用开发还远没有到定型的阶段但有一个顺手的基础框架后面怎么折腾都会从容很多。