ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring AI 2.0实战:Java接入大模型、Agent开发与Dify迁移全攻略

Spring AI 2.0实战:Java接入大模型、Agent开发与Dify迁移全攻略 做 Java 后端的兄弟这两年被 AI 浪潮逼得够呛。Python 那边有 LangChain、LlamaIndex生态一个比一个热闹公司一上 AI 需求Java 团队想原生接入大模型要么硬套 RestTemplate 调 HTTP 接口要么自己封装 Prompt 模板要么被逼着去抄 Python 那套。Spring AI 算是把这条路趟出来了。这框架刚出来的时候我也观望了一阵后来 1.0 正式 GA再到现在的 2.0我前后跟进了差不多一年。这期间带团队搞定过几个实际项目有接大模型做智能客服的有做企业知识库问答的还有从低代码平台往 Java 代码迁移的。今天就围绕 Spring AI 这个主题把我从入门到落地这一路摸出来的经验、踩过的坑、看过的代码全部整理出来。这篇内容适合谁看正准备在 Spring Boot 项目里接大模型的 Java 开发者在低代码平台比如 Dify上跑通了流程、想迁回 Java 代码的团队还有那些对 Agent、Function Calling 这些概念只听了个名字、想搞清楚底层原理的人。我尽量不堆概念直接说怎么搭、怎么写、怎么避坑。1. 先说清楚 Spring AI 到底解决了什么问题1.1 Java 生态里为什么需要 Spring AI很多人第一次接触 Spring AI 会问大模型不就是一个 HTTP 接口吗我用 OkHttp 调一下不就行了这么想没毛病但真做起来就发现问题了。一个真实项目要接的从来不是单次对话而是对话历史管理、工具调用、向量检索、多模型切换、Token 计费、流式响应这些工程化能力。你用自己的方式调接口等于从一个 AI 应用的角度重新造轮子而且造得还不一定对。Spring AI 的定位不是帮你发明新东西它做的事情是把 AI 应用的常见模式抽象成一套统一的 API。你写业务代码的时候不需要关心底层到底是接了 OpenAI、通义千问还是本地 Ollama。这种抽象思维方式跟 Spring 全家桶一脉相承——今天你用的是 MySQL明天要换 PostgreSQL数据源换了但你的 Service 层代码不动。Spring AI 想让你在切换大模型供应商的时候也有这种体验。官网对它的定义是“面向 Java 的 AI 应用框架”说实话这个定义有点太温和了。我更愿意把它理解成把大模型变成像数据库一样的基础设施。你需要对话能力就注入一个 ChatModel需要向量能力就注入一个 EmbeddingModel需要存记忆就用一个 ChatMemory一切都是 Spring 熟悉的依赖注入风格。1.2 从热搜词里看到的真实需求我注意到跟Spring AI绑定的热门搜索里有几个高频方向Spring AI 2.0 连接百炼 qwen3.7、Spring AI Agent、Dify 工作流转成 Spring AI Java 代码。这三个方向其实映射出三类典型人群。第一种是已经在用阿里云百炼平台的大模型 API想在 Spring Boot 里正规接入的工程团队第二种是希望用 Java 做 Agent 开发的进阶玩家不想再写各种乱七八糟的 JSON 工具调用协议第三种更直接之前在 Dify 上拖拽搭了工作流发现节点多了以后图表复杂到没法维护想回到代码世界找回掌控感。这三点我后面都会展开聊。尤其是 Dify 迁移这个方向网上几乎找不到系统性的教程我花了两个周末踩完坑把思路捋清楚了回头单独写了一大段。1.3 Spring AI 的核心模块一览先记住 Spring AI 的几个关键抽象后面所有代码都建立在它们上面抽象作用生活中找类比ChatModel统一的大模型对话入口相当于一个接线员你只管说话它帮你转接到不同大模型EmbeddingModel负责把文本转成向量相当于给每个词打上一串特征坐标机器才能算相似度Message / ChatClient封装消息结构、对话上下文相当于你在微信里的一段完整聊天记录不只是单条消息Advisor在对话前/后做增强处理相当于客服经理在接电话前后帮你查资料、做记录Tool / Function Calling让模型能主动调用你的 Java 方法相当于给 AI 配了个工具箱你说“我热”它就自己去开空调有了这张图打底就知道 Spring AI 的套路了。它不是一个大而全的 AI 平台而是一套可以按需组合的积木。你不需要全部用上做最简单的问答只需要 ChatModel做知识库就加 EmbeddingModel 和向量库做复杂 Agent 就再上 Tools 和 Advisors。2. 从零搭一个 Spring AI 2.0 工程连接百炼 qwen3.72.1 版本怎么选别再踩 1.0 的坑Spring AI 版本迭代速度远超很多 Java 开发者的预期。1.0 GA 发布在 2025 年随后 1.1 带来一堆新特性紧接着 2.0 又出来了。我见过很多人从教程里复制 0.8 版本的依赖跑到 1.0 项目里直接报错问题多数出在包名和自动配置类上面。我的建议是新项目直接用 2.0.x 系列如果你想用跟百炼平台适配比较完整的版本可以在 GitHub 上搜“spring ai alibaba”的 release 记录找到跟 spring-ai 2.0 版本匹配的 release。注意 spring-ai-alibaba 是基于 Spring AI 官方核心做的扩展两者版本号是独立的不一定要同一节奏更新。Maven 依赖写法如下注意把版本号统一用spring-ai-bom管理避免各个 starter 版本不一致parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.4.5/version relativePath/ /parent properties spring-ai.version2.0.1/spring-ai.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies !-- 接入阿里云百炼 DashScope通义千问系列模型 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-dashscope/artifactId /dependency !-- Web 支持后面要写接口 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies如果你用的是 spring-ai-alibaba 生态依赖坐标会有一点区别比如引入com.alibaba.cloud.ai:spring-ai-alibaba-starter-dashscope并且配置项前缀也有所不同。这里需要到仓库确认当前版本的坐标因为该项目的包名改过几次。2.2 在百炼上配好 qwen3.7百炼阿里云 Model Studio是阿里云的大模型服务平台在上面创建 API Key 之后可以调用多个通义千问版本的模型包括大家搜到的 qwen3.7。Spring AI 的 dashscope starter 已经把调用协议封装好了你只需要在application.yml里配置几个关键参数spring: application: name: spring-ai-demo ai: dashscope: api-key: ${DASHSCOPE_API_KEY} # 建议用环境变量注入别硬编码 chat: options: model: qwen3.7 # 或者你在百炼控制台看到的模型别名 temperature: 0.8 max-tokens: 2048这里有个细节很多人第一次没注意百炼平台的模型名在不同时段的别名会调整。比如早先用qwen-max、qwen-plus后来新版本上架之后就多了qwen3.7这样的名字。你要么去控制台的模型列表里确认当前可用的 model id要么在应用里做一层模型名映射方便以后切换。2.3 第一个可运行的 ChatModel 示例配置好之后就是见证奇迹的时刻。写一个 Controller注入 ChatModel 或 ChatClientRestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder.build(); } GetMapping(/chat) public MapString, String chat(RequestParam String message) { String answer chatClient.prompt(message) .call() .content(); return Map.of(answer, answer); } }启动 Spring Boot访问http://localhost:8080/chat?message你好如果能返回一段正常的自然语言回复说明从 Spring Boot 到 Spring AI 再到百炼平台的链路已经打通了。ChatClient是 Spring AI 1.0 之后主推的门面 API它比直接用 ChatModel 更方便的地方在于它把 Prompt、上下文、Advisor、工具调用这些东西全部收敛在一个流式调用链上。你在业务里要加上下文记忆、加 RAG 检索、加工具调用都只要在这一条链上做增强不用大改代码。2.4 流式输出比你想的更简单聊天场景如果不做流式输出用户体验会很僵硬尤其是模型生成的 token 比较多的时候用户盯着光标转圈能急死。Spring AI 的流式写法长这样GetMapping(value /chat/stream, produces text/plain;charsetUTF-8) public SseEmitter streamChat(RequestParam String message) { SseEmitter emitter new SseEmitter(); chatClient.prompt(message) .stream() .content() .subscribe(chunk - { try { emitter.send(SseEmitter.event().data(chunk)); } catch (IOException e) { emitter.completeWithError(e); } }, emitter::completeWithError, emitter::complete); return emitter; }这段代码核心就三步.stream()拿到响应流.subscribe()监听每个数据块然后在回调里把数据块通过 SSE 推给前端。前端用 EventSource 或者 fetch 流式读取都能接。实际经验告诉我流式输出跟非流式在返回格式上不要混在一起写最好拆成两个接口一个管文本展示、一个管流式交互否则前端处理解析逻辑会非常痛苦。3. Spring AI Agent从 Function Calling 到真正的自动化3.1 别把 Agent 想得太玄乎Agent 这个词被炒得很热但落地到代码里最先抓住的核心机制就是 Function Calling。什么是 Function Calling简单说你给模型一段背景说明和一批可调用的函数清单模型在生成回复的过程中判断“这个问题我需要调用某个函数”然后输出结构化的调用请求。你的程序收到这个请求后执行真正的 Java 方法再把结果回传给模型模型基于结果继续生成面向用户的最终答案。过程听起来绕其实本质就是一句话让大模型学会按需调用你写好的工具。Spring AI 把这种机制抽象得非常干净你只需要在方法上加上Tool注解框架自动帮你完成函数描述、参数 JSON Schema 生成、结果回传这一整套流程。3.2 一个实打实的 Agent 小例子我来写一个特别像实际业务的场景让 Agent 帮你查订单并计算运费。运费计算规则很死板不需要模型算直接调一个 Java 方法即可。这比让模型硬猜更合适——准确率直接拉满。Service public class OrderTools { private final MapString, Double zoneBase Map.of( 华东, 8.0, 华北, 10.0, 华南, 12.0 ); Tool(根据收货区域计算运费区域支持华东、华北、华南) public double calculateShipping(String zone) { return zoneBase.getOrDefault(zone, 15.0); } }然后在需要 Agent 能力的地方注入这个 Service 并告诉 ChatClient 有哪些工具可用RestController public class AgentController { private final ChatClient chatClient; public AgentController(ChatClient.Builder builder, OrderTools orderTools) { this.chatClient builder .defaultTools(orderTools) .build(); } GetMapping(/agent/order) public String orderQuestion(RequestParam String question) { return chatClient.prompt(question) .call() .content(); } }当你问“我有一个从华东发货的订单运费多少”模型就会先输出调用calculateShipping的请求框架自动执行并把计算完的8.0返回给模型模型再组织成“您的运费为8元”这种自然语言回复。整个过程中模型只负责理解和表达计算环节完全由你的 Java 代码掌控误差接近于零。3.3 Agent 的演进路线从单工具到多工具编排一个 Tool 只能算开胃菜。真实项目里 Agent 要同时面对多个工具查天气、查库存、查物流、算运费。这时候你会感受到 Spring AI 在 2.0 里做的优化2.0 版本中 Agent 相关的模块被大幅增强支持更复杂的工具调用链。模型可以连续发起多个工具调用先查库存再根据库存结果决定是否调用其他接口Java 侧只需要定义好工具方法框架会自动处理多轮调用。我在项目里做过的比较典型的 Agent 流程是用户问“这个商品明天能送到吗”Agent 先调用订单查询工具拿到商品所在仓库再调用物流时效工具查询该仓到目的地的配送时长最终结合当前时间给出答复。定义两个工具 ChatClient 默认工具集就能跑通这比硬编码 if-else 判断链路机动性高不少。3.4 Advisors给 Agent 加上记忆和检索增强Agent 只靠工具还不够你要是想要多轮对话记忆、知识库检索增强Spring AI 提供了一个扩展点叫 Advisor。Advisors 的工作方式是在用户请求发送给模型之前、和模型结果返回之后这两个时间点插入逻辑听上去有点像中间件本质上就是一个责任链模式。ChatClient chatClient builder .defaultAdvisors( new MessageChatMemoryAdvisor(new InMemoryChatMemory()), new QuestionAnswerAdvisor(vectorStore) ) .build();第一行把对话记忆加进来模型就能记住之前聊过的内容。第二行把向量检索加进来用户提问时会先在自己的知识库里找相关片段再丢给模型参考回答——这就是 RAG检索增强生成的工程化实现。我觉得 Spring AI 最值得学习的地方就在这里它把 Agent、记忆、RAG 这些东西全部收敛到几条链路上让你可以用很标准的 Spring 风格把它们组合起来而不需要去研究各家模型的 system prompt 写法。4. Dify 工作流迁移到 Spring AI Java 代码的实战心法4.1 Dify 的优点在哪里问题又出现在哪里Dify 这类低代码 LLMOps 平台确实香做原型验证特别快。拖几个节点连上线一个带工具调用和知识库的问答机器人十分钟就能跑起来。但原型和产品之间隔着一道很大的坎。我经历过两个典型痛苦。第一个是工作流节点多了以后没法维护图表变蜘蛛网改一个分支逻辑要看好久连线。第二个是权限和事务问题Dify 里的 HTTP 请求节点都是独立调用想把它们放进一个本地事务里根本做不到只能在外部硬拼接口。第三个是用私有化模型或特殊鉴权的时候Dify 的插件市场不一定有你想要的那个集成自己开发插件又等于脱离低代码路线丧失平台价值。所以在很多技术团队里Dify 的角色是“方案验证工具”最后的生产系统还是要回到代码。Spring AI 的价值这时候就看出来了——它本身就是 Java 代码你可以把它嵌入到现有的微服务体系里共享注册中心、配置中心、监控链路这是低代码平台天然做不到的。4.2 工作流节点到 Spring AI 架构的映射表Dify 工作流里最常用的几类节点跟 Spring AI 生态有比较清晰的对应关系我整理了一张表Dify 工作流节点Spring AI 方案说明LLM 节点ChatClientPromptTemplate用 Java 侧组装 Prompt响应走统一大模型通道知识检索节点VectorStoreQuestionAnswerAdvisor对接 Redis、Milvus 或 PGVector自动做向量检索条件分支节点Javaif-else或RouterAdvisor代码逻辑显然比画连线直观得多变量聚合节点Java 对象 简单拼接天然就不需要这种节点代码直接组合代码节点直接写 Java 方法比 Python 脚本节点更像是工程化组件HTTP 请求节点RestTemplate / WebClient配合自动注入的地址、鉴权、超时配置工具节点Tool方法更灵活函数签名直接作为模型工具描述这张表不是让你一比一照搬而是提供一个转化思路。真正的迁移不是把 Dify 节点改成 Java 方法那么简单而是把原来靠可视化连线的逻辑思维重新用代码分层思想表达出来。4.3 一个迁移案例从 Dify 聊天助手到 Spring Boot 服务我之前在公司带过一个实际迁移一个智能客服助手原来在 Dify 里有六个节点串联——起始节点、知识检索、LLM、条件判断、HTTP 请求查订单、回复结束。流程逻辑是用户先问问题系统判断是否与订单相关相关则查订单接口不相关则直接检索知识库回答。迁移到 Spring AI 之后的代码主干是这样public String handleUserMessage(String userId, String message) { // 1. 先判断是否订单相关这个判断也可以让模型来做但更稳的做法是先走关键词/意图识别规则 boolean orderRelated message.contains(订单) || message.contains(物流); if (orderRelated) { return orderChatClient.prompt(message) .advisors(new MessageChatMemoryAdvisor(userMemory(userId))) .tools(new OrderQueryTool(userId)) .call() .content(); } // 2. 订单不相关走知识库 RAG 通道 return knowledgeChatClient.prompt(message) .advisors( new MessageChatMemoryAdvisor(userMemory(userId)), new QuestionAnswerAdvisor(vectorStore) ) .call() .content(); }这一步迁移完原来需要盯着 Dify 画布才能理解的流程现在读代码一目了然。给客服加新知识直接更新向量库给 Agent 加新能力直接注册新 Tool。迁移之后我们还顺手接入了公司的监控系统每次对话耗时、Token 消耗、工具调用成功率都进了指标看板这在低代码平台里是很难做到的。4.4 迁移时最容易翻车的三个地方迁移 Dify 工作流最隐性的坑有三个。第一是 Prompt 迁移。Dify 里的系统 Prompt 可能埋了很多变量引用比如{{#sys.query#}}迁移到 Spring AI 时要仔细把变量替换成 Java 参数绑定否则模型拿到的就是这个变量名本身效果直接崩塌。我的建议是先用 PromptTemplate 把系统 Prompt 拆成模板文件再由Map.of()传参渲染。第二个坑是知识库 embedding 维度不一致。Dify 里做的知识库切片用的 embedding 模型迁移到 Spring AI 之后你可能会换一个 embedding 模型这样原来切好的向量和新切的向量对不上检索质量直线下滑。要么把向量全部重新生成一遍要么一开始就固定好 embedding 模型和参数。这个教训导致我浪费过整整一天最后只能把 Redis 里的向量全删了重灌。第三个坑是用户身份隔离。Dify 的知识库有权限空间但迁移到 Spring AI 后你自己搭的向量库如果不做 namespace 隔离A 用户的私人知识就可能被 B 用户检索到。生产系统里这属于安全事故级别的问题一定要用向量库的分区或者元数据过滤来做隔离。5. Spring AI Alibaba 停更了吗生态现状的正确认知5.1 关于停更传言的前因后果热搜词里有一条“spring ai alibaba 停更了吗”这说明很多开发者确实遇到过依赖找不到版本、或者发现阿里仓库里的那个模块很久没发新版本的困惑。我特意去翻过它的 GitHub 仓库和 Maven 中央仓库的记录结论是spring-ai-alibaba 只是更新节奏放缓但核心 Spring AI 官方项目一直都在活跃迭代。为什么会有停更错觉因为 spring-ai-alibaba 这类围绕上游框架做适配的扩展项目它的版本节奏完全取决于上游 Spring AI 的 release。Spring AI 官方在 2025 年到 2026 年这段时间版本迭代很快每次适配都要跟着改动包名、配置项、自动装配类。扩展项目本身人力有限做不到跟上游首发同步更新中间出现几个星期的空窗期很常见。所以不必恐慌如果你看到某个模块的 maven 坐标长时间没有新版本不代表整个项目凉了很可能只是它已经稳定在上一个上游版本上了。如果必须要用最新的 Spring AI 2.0 特性建议直接看官方spring-ai核心模块有没有覆盖你的需求阿里扩展主要负责百炼平台和本地落地的优化两者的边界还是要分清的。5.2 配置兼容性切换模型供应商时最怕遇到的事Spring AI 官方设计思想的精髓在于“供应商适配层”。只要你的业务代码用的是ChatClient和ChatModel抽象切换模型只需要换 starter 依赖和配置。我实际做过从本地 Ollama 切到百炼 qwen3.7 的操作步骤只有三步删掉spring-ai-starter-model-ollama依赖换成spring-ai-starter-model-dashscope再改application.yml里的配置前缀。业务代码不动。这里有个小坑得提醒一下不同模型的temperature、max-tokens支持范围不一样有些模型的top_p参数不允许超过 1有些模型不支持response_format这种参数。你最好把配置抽到一个独立的 properties 类里切模型时只改配置不改代码要不然代码里写死了某个模型的专属参数切到另一个模型直接 400 报错。5.3 哪些坑只会在生产环境暴露我在本地开发和测试环境跑得好好的代码上生产之后经常出幺蛾子。第一个高发问题是超时设置。大模型响应慢是常态尤其多轮 Agent 涉及多次工具调用时请求时间可能超过默认 60 秒。RestTemplate 或者 WebClient 的超时要单独配置别让它用默认值。第二个是限流和回退百炼这类平台的 API 有 QPS 限制生产流量一高就疯狂 429。Spring AI 的RetryTemplate在这里派上用场配好退避策略配合响应缓存兜底。第三个问题是 Token 成本失控。代码没问题但用户疯狂刷问题话账单会教你做人。我的做法是在 ChatClient 调用链外层封装一个拦截器统计每次请求的 prompt token 和 completion token超过阈值直接熔断或者引导用户换更小的模型。6. 实操中会遇到的问题与排查技巧实录6.1 对话模型返回内容不对先说人话还是先说代码模型返回的结果不对新手第一反应就是换模型、调 temperature其实绝大多数情况下不是模型的问题而是Prompt 写得不清楚。我调试时一定会先手动把用户输入的上下文拼出来打印到日志里看看模型实际拿到的是什么再判断问题出在哪。如果模型没按预期回复先检查 system prompt 有没有明确约束然后检查用户消息的上下文长度有没有被截断。Spring AI 还没自动语义截断消息发得太长模型可能丢失关键信息。如果你的对话记忆用的是MessageChatMemoryAdvisor默认只保留一定窗口的消息超过窗口的早期消息会被丢弃这也会导致模型“失忆”。6.2 Function Calling 工具没被调用八成是描述写得太模糊很多人在 Spring AI 里注册了 Tool却发现模型从来不调用它。这种情况我总结过一个排查顺序先从日志确认工具定义是否真的进入了 Prompt看 ChatClient 调用链打印的消息列表工具描述应该在 system 消息里出现。再检查工具描述是否包含足够的触发条件。Tool(根据收货区域计算运费)这类描述基本把触发条件说清楚了。最后检查模型能力是否支持强制工具调用。部分模型可以在请求参数里强制某个函数Spring AI 的ChatOptions里一般有toolChoice字段调试时可以临时设置为 required。有一次我遇到工具描述写得还行但模型就是不调用的情况最后发现是temperature设得过高导致模型发挥了太多“创意”每次都在生成一段看似合理的假数字回复。把 temperature 降到 0.2 之后工具调用立刻被触发了。6.3 流式响应时前端丢字后端却一切正常这个问题我遇到过不止一次。后端用 SSE 推流前端用 EventSource 接收收到的内容中间总有几个字丢失。排查下来发现不是后端丢是SSE 传输的编码问题。中文内容在部分网关或 Nginx 上没正确设置响应头浏览器解析时按错误字符集切割出现了乱码或丢字。解决方法是把响应头Content-Type设为text/event-stream;charsetUTF-8并且确认你用的网关没有对 SSE 做缓冲。6.4 向量检索效果差问题往往不在 Spring AI 而在数据RAG 效果不佳大家第一反应是调整 Spring AI 的 advisor 参数但多数时候问题出在数据切片策略上。切得太小语义不完整切得太大又会把很多不相关内容塞进上下文。我的经验是一般用 500 到 800 个字作为一个切片配合 10% 到 20% 的重叠度效果最好。切片把标题、表格结构考虑进去而不是傻傻地按字符数切。有些复杂文档直接按 Markdown 标题分块效果反而出奇地好。向量效果差的另一大原因是 embedding 模型和用户问题的语言风格差异太大。用户习惯说口语但知识库全是书面语相似度直接拉垮。我兼职办法是在投喂向量库之前先用一个简单的小模型把用户问题改写一遍提取出更正式的关键词再拿去检索。这个方案笨但有效。6.5 我的调试三板斧最后分享一个屡试不爽的调试思路。Spring AI 这种框架最大的特点就是调用链很长不直观。我每次遇到问题都按三板斧备案处理第一步打开 Trace 日志把org.springframework.ai.chat.client的日志级别调到 DEBUG看一眼实际的请求消息和响应消息。第二步用单元测试隔离单条链路。把用户消息、上下文、工具定义全部固定下来单独验证这个链路能否正常工作。第三步给每个关键环节加计时埋点。ChatClient 本身不区分耗时来自模型还是工具手动埋点能看出来问题卡在哪一段。我自己的体会是Spring AI 真正难的不是 API 怎么用而是把聊天和代码这两套完全不同的思维模式在同一个系统里缝起来。低代码平台帮你把这块布料烫平了但代价是你失去了针线的控制权。回到 Spring AI 之后你重新拥有这块布该怎么裁剪的权力也就需要重新习惯缝合过程中的各种误差和阵痛。如果说最后还有什么可以扩展的方向我建议新手从个人的知识库加聊天机器人这个组合开始练习因为它能同时覆盖 ChatModel、EmbeddingModel、VectorStore、Advisor 四块核心能力一套流程走完Spring AI 的骨架你就基本掌握了。后续再往 Agent 方向深入加上工具调用和记忆一个像模像样的生产级 AI 服务就立起来了。
返回列表