ARTICLE DETAIL

资讯详情

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

Spring AI Alibaba实战:ReactAgent智能体与Workflow工作流编排

Spring AI Alibaba实战:ReactAgent智能体与Workflow工作流编排 如果你正在做 Java 后端的 AI 应用最近大概率被几个名词反复刷屏Spring AI、Spring AI Alibaba、ReactAgent、Workflow。它们听起来像是一套东西但又不像传统 Web 框架那样有明确边界。笔者在尝试把大模型接入业务系统时也被“智能体到底怎么落地”、“工作流编排到底是框架能力还是自己写”这类问题绕了好几圈。这篇文章会把 Spring AI Alibaba 生态下的智能体ReactAgent与工作流Workflow扩展体系拆开讲清楚包含可复制的代码示例、配置思路和我在实践中遇到的坑。1. 从一次智能体开发说起为什么要涉及 Spring AI Alibaba先说结论Spring AI Alibaba 解决的是“Java 后端如何稳定、低成本地接入大模型”的问题。它不是一套全新的 AI 框架而是在 Spring AI 的抽象体系之上针对阿里云百炼平台的模型服务做了一层适配与扩展。当你在业务中需要做以下事情时Spring AI Alibaba 就是最自然的选型在 Spring Boot 服务中调用通义千问 / 百炼平台上的大模型把 LLM大语言模型能力封装成可以被业务代码统一调用的 Service让模型能够调用你已有的 Java 方法、HTTP 接口或者内部服务把“用户提问、模型思考、工具调用、结果组装”的过程编排成可维护的流程。从这个角度看Spring AI Alibaba 并不是一个花哨的玩具而是把大模型接入、模型切换、多工具调用、提示词模板、记忆管理等琐碎能力收敛成统一 API 的工程化框架。这篇文章适合三类读者正在做 Spring Boot 后端想在项目中接入大模型能力的开发者对 Agent智能体概念感兴趣但不知道在 Java 中如何实现推理、工具调用闭环的读者接触过 Dify / Coze 等工作流工具想在代码层面复现同类编排能力的研发同学。读完本文你将掌握Spring AI Alibaba 的基础接入方式ReactAgent 的基本实现思路ReAct 模式的 Java 落地以及一种不依赖额外编排引擎的业务工作流组织方式。2. 四个关键概念先分清在进入代码之前有几个高频出现但又容易被混用的概念值得先单独梳理。2.1 Spring AIJava 生态的大模型抽象层Spring AI 是 Spring 官方推出的 AI 应用开发框架。它的目标是把接入大模型这件事统一抽象成类似 JDBC、Spring Data 那样的规范。// 典型使用方式不关心底层是 qwen 还是其它模型 ChatResponse response chatClient.prompt() .user(用一句话介绍 Spring AI) .call();这段代码后面无论接的是阿里云百炼、OpenAI 还是其它兼容协议的服务业务代码本身不需要大改。Spring AI 提供了ChatClient / ChatModel统一对话入口ChatOptions模型参数温度、最大 Token、模型名等抽象Message用户消息、系统消息、工具消息的标准化结构Tool / Function Calling统一工具注册与调用机制。所以Spring AI 解决的是“不同模型接入方式不一致”的问题让 AI 能力像数据源一样可以被标准化使用。2.2 Spring AI Alibaba把百炼模型接入 Spring 生态的适配层Spring AI Alibaba 是阿里巴巴开源的 Spring AI 适配组件。它做的事情可以概括为把阿里云百炼平台的大模型服务适配成 Spring AI 的 ChatModel提供基于百炼平台的配置文件与自动装配能力将阿里云模型特有的能力例如某些工具的返回结构、流式输出格式映射到 Spring AI 统一模型上。如果你只走 OpenAI 兼容协议Spring AI 本身也能工作。但如果你希望稳定接入通义千问系列模型并使用百炼平台的密钥管理、模型路由能力Spring AI Alibaba 是更直接的选择。有读者可能会问“Spring AI Alibaba 停更了吗”这其实是一个版本变迁造成的误解。早期 spring-ai-alibaba 作为 Spring AI 仓库中的子模块维护后来又独立为单独仓库与版本线演进所以如果你在主仓库里搜不到新增模块不要急着判断项目停止维护建议直接看官方独立仓库的发布记录。2.3 ReactAgent基于 ReAct 范式的自定义智能体ReactAgent 目前并不是 Spring AI 官方库中开箱即用的一个具体类而是社区基于 ReActReasoning Acting范式实现的智能体模式。ReAct 的核心思路是让大模型不只是“直接回答”而是在回答之前先经历一个循环接收用户问题结合已有工具列表进行推理Reasoning判断是否需要调用工具如果需要发起工具调用Acting拿到工具结果后再次推理直到模型认为信息足够生成最终回答。在 Java 中你可以用 Spring AI 的 ChatClient 和 Function Calling 能力把这个循环封装成自己的 ReactAgent 类。这就是标题里“ReactAgent 智能体”的本质它并不是一个神秘框架而是一个可自行实现的编码模式。2.4 Workflow把多个原子能力编排成业务步骤Workflow工作流在 AI 应用中有两种含义一类是 Dify、Coze 这类产品提供的可视化流程编排节点包括 LLM 调用、知识库检索、代码执行、条件分支、HTTP 请求等另一类是你在代码里自己实现的业务编排把“意图识别、参数抽取、工具调用、答案生成”串成一个可复用流程。在 Spring AI Alibaba 的场景里我们通常不必为了做一个简单 Agent 而引入重量级流程引擎。大多数业务场景下用责任链模式、简单状态机或者一个 Workflow 编排类就能实现清晰可控的工作流。3. 环境准备与依赖引入3.1 技术栈与版本说明本文示例以常见稳定组合为例JDK 17 或 JDK 21Spring Boot 3.2.x 及以上Spring AI 1.0 需要 Boot 3.xMaven 3.8Spring AI Alibaba 2.0.1以实际仓库发布版本为准阿里云百炼 API Key版本说明Spring AI 演进速度较快。如果你在接入时发现某个类不在预期包路径下优先检查你是否把 Spring AI Alibaba 与 Spring AI 的版本拉齐。建议以官方 release 页面标注的兼容版本为准不要自行混用大版本。3.2 创建 Spring Boot 工程推荐直接使用 Spring Initializr 创建工程手动添加依赖。下面是一个最小化的 pom.xml 核心片段parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.5/version relativePath/ /parent properties java.version17/java.version spring-ai-alibaba.version2.0.1/spring-ai-alibaba.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId version${spring-ai-alibaba.version}/version /dependency /dependencies如果你的工程当前没有可用的 Spring AI BOM也可以单独引入 Spring AI 核心依赖再叠加 Alibaba 适配层。具体依赖坐标建议以官方文档为准因为 AI 框架的改动频率远高于普通中间件。3.3 配置阿里云百炼模型接入在application.yml中配置百炼平台的 API Key 和默认模型spring: application: name: spring-ai-alibaba-demo ai: dashscope: api-key: ${AI_DASHSCOPE_API_KEY:your-api-key} chat: options: model: qwen-plus这里有几个关键点AI_DASHSCOPE_API_KEY是环境变量。不要直接把 Key 硬编码到代码或配置仓库中尤其是生产环境model指定默认的大模型名称比如qwen-plus、qwen-max。不同模型的能力与价格差异较大建议根据业务场景切换如果某些工具调用场景中plus版本模型报错而max模型正常通常与模型对 Function Calling 参数的支持差异有关可以先用max验证链路再针对plus调整工具描述或参数结构。完成依赖引入和基础配置后写一个简单的 Controller 验证链路是否通// 文件路径src/main/java/com/example/agentdemo/ChatController.java 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(); } }启动项目后访问/chat?message你好如果能返回模型结果说明环境已经就绪。4. ReactAgent 智能体实战让模型学会“边想边做”4.1 ReAct 模式的执行流程ReAct 模式本质上是一个循环这个循环可以用下面的步骤表示组装 System Prompt告诉模型它有哪些工具可用、应该按什么规则执行将用户问题与历史消息发送给模型模型返回结果。如果结果包含工具调用请求进入第 4 步如果直接返回自然语言答案则结束执行对应工具拿到真实结果将工具结果作为 Tool 消息追加到上下文中再次发送给模型重复步骤 3-5直到模型给出最终回答。用文字描述显得抽象但在 Spring AI 中Function Calling 已经帮你处理了工具调用请求的解析与回传你真正要做的是“允许多轮工具调用”的组织逻辑。4.2 基于 ChatClient 封装一个最小 ReactAgent在 Spring AI 1.x 中ChatClient 原生支持工具调用。下面这个类演示了如何把一个带工具调用的对话操作封装成可复用 Agent。// 文件路径src/main/java/com/example/agentdemo/agent/ReactAgent.java Component public class ReactAgent { private final ChatClient chatClient; public ReactAgent(ChatClient.Builder builder) { this.chatClient builder .defaultSystem( 你是一个智能助手。你可以使用下方工具完成用户请求 - getCurrentWeather: 根据城市名查询实时天气 当用户询问天气时你必须调用工具获取数据再根据工具结果回答。 ) .build(); } public String execute(String userMessage) { return chatClient.prompt() .user(userMessage) .call() .content(); } }注意defaultSystem并不触发工具自动调用它只是给模型提供了行为规则。真正让模型具备“调用动作”能力的是注册工具。4.3 注册外部工具让 Agent 能够“动手”在 Spring AI 中使用Tool注解即可让普通 Java Bean 方法暴露为大模型可调用的工具。// 文件路径src/main/java/com/example/agentdemo/tool/WeatherTool.java Component public class WeatherTool { Tool(根据城市名查询当前天气) public String getCurrentWeather(String city) { // 实际项目中可以替换为真实天气服务 return switch (city) { case 北京 - 晴12℃; case 上海 - 小雨17℃; default - city 暂无数据; }; } }然后在构建 ChatClient 时把工具实例传进去// 文件路径src/main/java/com/example/agentdemo/config/ChatClientConfig.java Configuration public class ChatClientConfig { Bean public ChatClient chatClient(ChatClient.Builder builder, WeatherTool weatherTool) { return builder .defaultTools(weatherTool) .build(); } }这样 ReactAgent 在收到“北京天气怎么样”时大模型会先决定调用getCurrentWeather工具拿到真实结果后再组织语言回答。4.4 多轮工具调用的可靠性问题单工具调用很简单但真实业务往往是“多工具连续调用”比如先查订单状态再根据状态查询物流最后汇总回复。在多轮调用场景下你可能会遇到两个典型问题模型在工具结果返回后没有继续追问而是直接结束。这种情况通常是 System Prompt 中没有说明“根据工具结果继续推理”工具返回的数据过于复杂占满上下文。建议在工具方法内部就做字段裁剪只返回必要信息。一个稳妥的做法是在 System Prompt 里显式声明“你可以连续调用多个工具每次调用完工具后根据结果决定是否需要继续调用。”从实践来看qwen-max在复杂工具调用上通常比qwen-plus表现更稳定。如果调试时发现plus模型对某个复杂工具描述理解不到位优先优化工具描述文本而不是盲目换常用的随机参数。5. Workflow 工作流编排实战5.1 为什么不能只用“单次 Prompt”实现业务需求业务中的 AI 功能很少是“问一句答一句”。典型场景用户说“帮我查一下今天北京天气如果下雨就提醒我带伞”。这条请求其实包含意图识别查天气 条件判断、参数抽取北京、工具调用查天气、条件分支是否下雨、结果生成提醒带伞。如果只靠一次 Prompt模型的随机性会让整个链路不可控。你需要把这些步骤显式地编排起来。这里的核心思路是确定性步骤用代码控制不确定性步骤交给 LLM。5.2 在 Java 中实现一个轻量 Workflow不引入额外流程引擎用一个简单编排类就能组织多个处理节点。先定义一个通用节点接口// 文件路径src/main/java/com/example/agentdemo/workflow/WorkflowStep.java public interface WorkflowStepT { /** * 执行当前节点并返回上下文或结果 */ T execute(WorkflowContext context); }上下文对象用于在节点之间传递数据// 文件路径src/main/java/com/example/agentdemo/workflow/WorkflowContext.java public class WorkflowContext { private final MapString, Object data new ConcurrentHashMap(); public void set(String key, Object value) { data.put(key, value); } public T T get(String key) { return (T) data.get(key); } public String getInput() { return (String) data.get(userInput); } public void setInput(String input) { data.put(userInput, input); } }接下来把业务拆成三个节点// 节点1意图识别调用 LLM 判断用户想做什么 Component public class IntentRecognitionStep implements WorkflowStepWorkflowContext { private final ChatClient chatClient; public IntentRecognitionStep(ChatClient chatClient) { this.chatClient chatClient; } Override public WorkflowContext execute(WorkflowContext context) { String userInput context.getInput(); String intent chatClient.prompt() .system(你是意图识别助手。只输出以下枚举值之一WEATHER、ORDER、OTHER) .user(userInput) .call() .content(); context.set(intent, intent); return context; } }// 节点2参数抽取从文本中抽取城市名等参数 Component public class ParameterExtractStep implements WorkflowStepWorkflowContext { private final ChatClient chatClient; public ParameterExtractStep(ChatClient chatClient) { this.chatClient chatClient; } Override public WorkflowContext execute(WorkflowContext context) { String userInput context.getInput(); String params chatClient.prompt() .user(从这句话中抽取城市\n userInput) .call() .content(); context.set(params, params); return context; } }// 节点3工具执行与回答生成 Component public class ToolExecuteStep implements WorkflowStepWorkflowContext { private final ReactAgent reactAgent; public ToolExecuteStep(ReactAgent reactAgent) { this.reactAgent reactAgent; } Override public WorkflowContext execute(WorkflowContext context) { String input context.getInput(); String answer reactAgent.execute(input); context.set(answer, answer); return context; } }最后用一个 Workflow 类把这些节点串成流程// 文件路径src/main/java/com/example/agentdemo/workflow/WeatherWorkflow.java Component public class WeatherWorkflow { private final ListWorkflowStepWorkflowContext steps; public WeatherWorkflow(IntentRecognitionStep intentStep, ParameterExtractStep extractStep, ToolExecuteStep toolStep) { this.steps List.of(intentStep, extractStep, toolStep); } public String process(String userInput) { WorkflowContext context new WorkflowContext(); context.setInput(userInput); for (WorkflowStepWorkflowContext step : steps) { step.execute(context); } return context.get(answer); } }这个设计的好处是步骤之间通过WorkflowContext解耦新增节点只需要实现WorkflowStep接口并调整步骤列表关键逻辑是显式代码不依赖模型的随机表现。5.3 Dify 工作流如何映射到 Java 实现很多团队先用 Dify / Coze 验证流程再要求后端用 Java 重写。这里给一个可参考的映射关系Dify 节点Java 实现方式LLM 节点ChatClient.prompt() 调用工具节点Tool 注解方法 或 HTTP Client 调用条件分支if/switch 或 Spring 的 ConditionalOnExpression代码节点普通 Service 方法知识库检索节点向量数据库 client 调用变量赋值WorkflowContext.set()回答节点return 结果字符串如果你正在做 Dify 工作流转 Java 代码的工作建议先画出节点 DAG再按“顺序节点 条件节点 并行节点”三类结构映射到代码。不要让业务逻辑散落在 Controller 中。6. Spring AI 扩展的三个高频方向6.1 扩展自定义 ChatModel默认接入百炼模型已经够用但有的场景需要同时对接公司内部的私有化模型服务。此时可以自定义ChatModel// 示例思路实现 Spring AI 的 ChatModel 接口 public class CustomChatModel implements ChatModel { Override public ChatResponse call(ChatRequest request) { // 将 request 中的消息转发给私有模型服务 // 将私有模型返回内容封装为 ChatResponse return null; } Override public ChatResponse stream(ChatRequest request) { return call(request); } }这里不要求完整实现重点是理解扩展点只要实现ChatModel接口并注册为 Spring Bean你的 ReactAgent 和 Workflow 代码就不需要感知底层模型差异。6.2 扩展自定义 Tool除了Tool注解方式ToolCallback接口也允许你动态定义工具描述与执行逻辑很适合从配置中心读取工具列表的场景。// 文件路径src/main/java/com/example/agentdemo/config/DynamicToolConfig.java Configuration public class DynamicToolConfig { Bean public ToolCallback dynamicTool() { return ToolCallbacks.from(lookup_order, 根据订单号查询订单状态, { type: object, properties: { orderId: {type: string, description: 订单号} }, required: [orderId] } , json - { // 解析参数并执行业务查询 return 订单已发货; }); } }ToolCallbacks.from是 Spring AI 提供的便捷构造方法不同版本参数顺序略有差异建议以当前版本源码为准。6.3 扩展对话记忆默认ChatClient每次调用都是独立上下文。生产环境需要把历史对话保存下来常用方案有两种使用MessageWindowChatMemory在内存中维护最近 N 条消息使用 Redis 存储按会话维度的消息列表。Spring AI 提供了ChatMemory抽象你可以把历史消息持久化到任何存储中然后组合进ChatClient。这样 ReactAgent 才能真正具备连续多轮对话能力。7. 高频问题与排查清单以下问题来自我实际使用和社区反馈按出现频率排序问题现象常见原因解决思路请求报错 400模型名称不存在或当前账号未开通对应模型检查百炼平台是否开通 qwen-plus / qwen-max 服务plus 版本调工具报错max 正常不同模型对 Function Calling 的 JSON Schema 支持不一致先用 max 验证调整工具参数描述后再测 plus工具结果返回后模型不继续推理System Prompt 未声明多轮工具调用规则在 System Prompt 明确“根据工具结果决定是否继续调用工具”Spring AI Alibaba 依赖拉不下来仓库地址或版本号错误检查独立仓库 release 页面确认阿里云 Maven 仓库已配置上下文过长导致费用激增工具结果未裁剪、历史消息未控制工具方法只返回摘要字段设置 ChatMemory 窗口大小模型回答不稳定相同输入在不同时间得到不同结果将温度调低固定 System Prompt必要时用流式输出展示过程排查建议清单如果你在使用过程中遇到 400 报错推荐按下面步骤排查确认spring.ai.dashscope.chat.options.model的值是否与百炼平台开通模型一致临时在日志中打印完整请求报文观察是否是工具参数格式问题检查 API Key 是否有权限调用目标模型换成qwen-max做交叉验证排除模型本身限制查看 Spring AI Alibaba 版本更新记录确认是否存在已知 Bug。8. 从 Demo 到生产工程化最佳实践8.1 密钥与配置安全不要把百炼 API Key 直接写死在application.yml。推荐使用环境变量、KMS 或配置中心。如果用的是配置中心注意密钥的密文存储与权限隔离。8.2 超时、重试与限流大模型接口的响应时间波动较大。建议在调用 ChatClient 时设置合理的超时时间并针对网络抖动增加重试机制。但重试时要小心生成类接口不是幂等的重试可能导致重复扣费建议只在超时或连接类错误时重试不要对正常返回的结果做无理由重试。同时百炼 API 有 QPS 限制。如果你在 Workflow 中频繁调用模型建议在内部加入简单的令牌桶限流防止某个上游流量突然打满配额。8.3 可观测性生产环境必须能观测到完整的调用链路记录每次 ChatClient 调用的模型名称、输入 token 数、输出 token 数、耗时记录 ReactAgent 每轮「推理 - 调用工具 - 拿到结果」的循环信息如果搭配 Redis 存储 ChatMemory还需要统计会话 Redis 访问耗时对异常响应400、限流、超时做结构化日志输出方便后续告警。8.4 版本演进与兼容性Spring AI Alibaba 与 Spring AI 的版本兼容关系是生产环境必须关注的点。建议锁定一个已验证的版本组合而不是每次用最新版升级前阅读官方 changelog重点关注 ChatClient API 是否变化、工具调用方式是否变化为模型接入层编写单元测试和集成测试确保升级后行为不回归。8.5 明确 AI 能力的确定性边界最后也是最关键的一条Agent 与 Workflow 的处理范围必须分清。Agent 适合开放式任务用户意图不明确、工具组合动态变化Workflow 适合确定性任务业务步骤固定、输入输出结构清晰生产中优先用 Workflow 包住核心业务链路把 Agent 限制在意图识别和内容生成环节。这样既能发挥大模型的灵活性也能把不可控风险锁在一个可控范围内。9. 写在最后Spring AI Alibaba 的价值在于它让 Java 开发者不需要离开 Spring 生态就能完成大模型接入、工具调用与智能体编排。ReactAgent 不是神秘架构它就是 ReAct 模式在 Java 中的一个落地封装Workflow 也不一定非要重量级引擎几个接口加一个上下文类就能把业务步骤编排得清清楚楚。下一步你可以尝试把自己已有的一个查询型接口改造成Tool写一个能调用它的 ReactAgent把日常中固定流程的业务例如请假审批、工单分类用WorkflowStep组织起来阅读 Spring AI Alibaba 官方仓库中的示例项目对照新版本 API 调整自己的实现。在你动手过程中遇到最多的坑往往是版本和模型参数问题而不是架构问题。所以我的建议是先确保最小对话链路能跑通再逐步叠加工具、记忆和流程编排。这样哪怕出问题排查范围也是可控的。如果本文对你搭建 Spring AI Alibaba 应用有帮助可以先收藏备用。后续我会继续更新智能体调用、工作流并发编排以及百炼模型调优相关的实战笔记。
返回列表