资讯动态

Spring AI Alibaba Graph:用工作流编排驯服失控的AI Agent

发布时间:2026/10/20 8:05:56 来源:尧图企业网站定制
1. 先从单Agent的“失控感”说起我去年带着一个团队维护企业内部服务台的智能工单系统最开始架构特别简单一个ReAct Agent、五六个工具函数、一个聊天入口模型自己决定要查什么、按什么顺序查最后把答案拼出来。Demo跑得是真顺领导看完连连点头但一上真实流量问题就开始往外冒。说句实话不是模型变笨了而是让模型自由发挥下一步这个设计在企业流程里本身就很难兜底。ReAct这个名字来自Reasoning和Acting核心机制并不复杂模型先生成一段推理说清楚我现在需要知道什么然后调用一个工具拿到新信息再接着推理下一步。这种循环非常适合单点问题比如查一下这个账号在12点到13点之间的登录日志判断有没有异常。但业务一旦变成查完日志如果判断是网络问题就自动重启服务重启后还要验证验证失败要升级人工同时给用户回执纯ReAct就开始失控。1.1 ReAct擅长的恰恰是企业流程不擅长的ReAct把下一步做什么完全交给了模型的概率输出。这对开放域的问答没问题但对那些需要闭环、需要审计、需要在某个环节插入人工审批的企业流程概率输出意味着不可控。你可以通过提示词约束模型让它按步骤走可提示词是文本不是程序控制。换一个版本的模型哪怕只是参数微调可能就有工单流到一半不走的情况。后来我们把所有线上异常事故统计了一下发现大部分失败的根因都不在工具调用而在流程没有确定性保障。比如说模型明明已经判断出账号是锁定状态后面又绕回去查了一遍网络连通性再比如说同一个工单描述上午走的是网络修复分支下午因为模型输出的微小变化走到了人工审批分支。这种不确定性在Demo里完全看不出来但放到生产环境运营和客服第一个不答应。在企业里下一步是谁、在什么条件下触发、失败怎么办应该是明确写出来的规则而不是模型临场发挥的结果。这也是我后来下定决心把ReAct Agent拆开放到一个可编排流程图里的原因。既然流程是公司多年业务经验沉淀出来的那就应该由代码稳定地表达出来模型只负责其中需要理解力的部分。1.2 单个Agent做复杂流程的几个麻烦这里我总结几个真实踩过的坑第一长链路状态会丢。单Agent为了让模型记得上下文会把很多信息塞进对话历史。对话一长就有两个副作用一是token消耗上涨二是模型可能忽略之前的关键结论。比如它前面已经判断账号是锁定的后面又去查了一次网络连通性。不是它笨是记忆不可靠你不能指望一个基于概率的模型天然维护好业务状态。第二分支逻辑难以稳定处理。工单类型本来就分网络、账号、权限、硬件等好几类不同类别处理动作完全不一样。ReAct Agent无法保证每次都先分类再按分类走对应子流程它可能会跳过分类直接处理这在业务上很危险。最讽刺的是你可以在提示词里写一百遍请先分类它还是偶尔不分类。第三运维观察不到中间状态。用ReAct时整个运行过程就是一段对话记录没有明确的节点概念。出了故障你只能翻模型日志去猜它在哪一步卡住了。有一次用户反馈工单没有及时处理我们查了半小时日志才发现模型在第七次工具调用后输出了一段无关内容流程就断了。而工作流里每个节点都有名字、状态和耗时一眼就能看出是哪一段出问题。第四治理和审计困难。企业流程往往要求记录谁在哪一步做了什么。纯Agent很难给出这个能力因为它没有一个可枚举的动作集合。Graph工作流天然有节点与边每一步都能落日志审计需求轻松满足。对于要过等保、要应付内部合规检查的企业这一条基本是刚需。1.3 什么情况下该切换不是说ReAct不好。我的经验是判断标准很简单如果这个任务允许模型答不上来或者换条路再试单Agent没问题如果任务要求必须走完这几步且每一步结果决定下一步那就要用工作流来兜底。后者正是Graph编排的领域。Spring AI Alibaba Graph做的就是这件事它把一个工作流抽象成有向图节点放业务逻辑边决定流转方向模型只是某个节点上的一个执行者。这样既保留了AI语义理解的能力又收回了流程控制权。接下来我先把它的核心设计讲清楚。2. Spring AI Alibaba Graph是怎么把流程变成图的如果之前你没接触过Graph类的工作流可以把Spring AI Alibaba Graph理解成Java版的状态机加DAG。你定义一批节点每个节点干一件明确的事再定义节点之间的边规定从那走到哪。图被编排好之后调用一次就相当于从入口节点开始沿着边一路执行到终点所有中间数据放在一个共享状态对象里流动。2.1 状态就是整个流程的“公共白板”Graph运行的核心是一个状态模型你可以把它当作整个流程的公共白板。所有节点往白板上写东西后面的节点再基于白板内容做判断。节点执行时都能拿到当前这趟运行的状态节点修改完状态返回图框架负责把更新合并进整个运行上下文。这个设计和LangGraph的思路很像理解起来不费劲你只需要想清楚流程里有哪些数据需要被共享和传递。我经常用一张表格来设计白板字段工单ID、用户反馈内容、故障分类、检查结果列表、处理动作、处理结果、验证结论、通知状态。每个节点读自己需要的字段写自己负责的字段跟多人协同编辑同一个文档差不多。节点之间不直接通信一切通过白板这让每个节点都能单独测试这一点在企业项目里非常重要。举个例子如果诊断节点发现账号锁定它只需要在白板上写categoryACCOUNT_LOCKED和evidence登录失败次数超过5次后续的账号修复节点自然会去读这两个字段并决定下一步动作。流程整体是一条数据流而不是一堆互相调用的Java方法这让代码的可读性和可维护性都上升了一个台阶。2.2 节点做执行边做路由条件边做分支图模型里有三类核心东西节点、普通边、条件边。节点好理解一段Java代码一个方法或者lambda负责一段业务逻辑。普通边就是A结束后一定去B比如受理节点结束后一定进入诊断节点。条件边是重点它从源节点出发根据当前状态里的某个字段动态决定去哪个目标节点。比如诊断节点后面有三个出口条件去向故障分类 网络网络修复节点故障分类 账号账号解锁节点其他人工审批节点条件边把原来ReAct里模型自由选择的下一步变成了基于明确状态做出的路由决策。你可以让模型负责分类它只输出一个分类结果然后程序根据这个结果走边。这就是模型的归模型流程的归流程。这种拆分看起来简单却是整个企业级AI流程稳定性的基石。2.3 为什么这个能力放在Java生态里很重要很多团队用Python做AI原型但落地到企业核心链路时Java仍然占主流。Spring AI Alibaba Graph能直接嵌在Spring Boot应用里因为它的节点就是普通Java方法不引入Python运行时不增加异构服务。你可以在一个项目里同时维护REST接口、MQ消费、定时任务和这个AI工作流用同一套日志、监控、链路追踪体系。对我们这种要交付给运维团队的工程来说这个收益非常实在——你的AI流程不是一个独立的黑盒系统而是一个能被工程化管理的模块。模型请求失败可以走Spring的重试机制状态快照可以存到数据库节点方法可以写单元测试一切都可以纳入现有的DevOps流程。这是很多Python脚本项目做不到的也是企业技术负责人最关心的一点。3. 上手准备先从一个能用的Agent开始说再多概念不如先跑起来一个真实可用的Agent。我建议不要一上来就写Graph先把Agent本身调通确认模型能调用工具、能回答业务问题再把它放流程里。3.1 工程与配置我用的是Spring Boot加Spring AI Alibaba依赖方式直接看项目文档关键是配置文件里把模型服务地址和密钥填好。下面是一个简化的配置示例具体参数名以你项目里实际依赖版本为准spring: ai: alibaba: cloud: api-key: ${CLOUD_API_KEY} chat: options: model: your-chat-model temperature: 0.3temperature我习惯设在0.2到0.4之间。工具调用场景下温度太高会带出随机性太低又可能让模型生成过于保守的表述。实测下来0.3是个不错的起点。如果你想追求极致的确定性甚至可以调到0.1但那样会让模型在需要一点发散思维的场景里变得呆板所以建议按任务性质分开配置。3.2 注册工具函数Spring AI里工具函数就是普通的java.util.FunctionBean注册后可以被模型自动调用。业务上常见的就是查询工单状态、查登录日志、查配置项这一类。这里我给一个简化版public record QueryRequest(String ticketId) {} public record QueryResponse(String status, String owner) {} Bean Description(根据工单编号查询当前处理状态) FunctionQueryRequest, QueryResponse queryTicketStatus() { return request - { // 真实环境这里会查数据库或调用内部服务 return new QueryResponse(PROCESSING, ops-team); }; }工具定义的细节很重要Description写得好不好直接影响模型能不能正确选择这个工具。我见过很多人随便填一句结果模型把查库存的工具拿去回答查订单的问题。描述里要写清楚输入参数是什么、返回什么、适合什么场景最好加上一两个典型例句。比如当用户询问工单处理进度时使用就比查询工单状态要明确得多。3.3 让模型自动决定调用在Controller里创建一个ChatClient把工具注册进去模型就能在对话中自主选择调用RestController public class AgentController { private final ChatClient chatClient; public AgentController(ChatClient.Builder builder) { this.chatClient builder .defaultSystem(你是企业服务台助手只根据事实回答不要编造工单状态。) .build(); } PostMapping(/assistant) public String assistant(RequestBody String question) { return chatClient.prompt() .user(question) .functions(queryTicketStatus) .call() .content(); } }跑起来之后你可以先做几组冒烟测试把工单ID给模型看它会不会主动调用查询工具给它一个库里不存在的ID看它会不会胡编。如果它能正确调用工具并且不编答案说明这个Agent基本合格。这时候你已经能解决单点查询类的问题但离企业级流程还差得远。3.4 为什么只靠Agent还不够我来说个更具体的例子。有一次测试人员问Agent工单WO-1001处理到哪一步了它正确查了工单状态回答处理中。测试人员又问那这个工单为什么还没好模型开始根据工单描述猜测可能是网络问题接着自己调了网络工具然后直接断言是网络问题。问题来了模型跳过了账号检查、应用日志检查等多个步骤直接得出结论而且没有验证。这在单点问答里问题不大但如果这个结论直接推给用户或者自动触发修复动作那就是事故。所以我才坚持要把Agent拆进Graph用流程约束住它。模型可以给出假设但流程必须负责验证和收口。4. 用Graph编排一个真正的多步工作流还记得我前面说的那个业务吗先查工单再做分类按分类走处理路径结束后验证并通知用户。现在我用Graph把它建模出来。先说一个通用提示Spring AI Alibaba Graph还在快速迭代下面代码是按当时的API风格写的你实际用的时候对照一下当前版本的类名和方法签名整体结构不用大改。4.1 定义流程状态我先定义一个简单的状态模型用一个Map承载所有共享数据。Graph框架需要知道状态的类型所以用一个工厂方法或者构造器来产生状态StateGraphMapString, Object graph new StateGraph.Builder( MapStateSchema::new )实际工程里我更推荐定义一个工单状态的POJO字段类型清晰不容易在协作中写错key。Map状态写起来快但团队大了之后ticketId和ticket_id这种命名混乱会浪费很多排查时间。所以这里给个建议小型demo用Map没问题正式项目里建议用带类型的StateSchema。状态类设计得好整个流程的逻辑都会清晰很多。4.2 把业务动作拆成节点节点是一个个方法每个方法接收状态、处理一段业务、返回新的状态。把之前Agent的动作拆开MapString, Object receiveNode(MapString, Object state) { // 这里写受理日志把工单ID登记到流程上下文 state.put(status, RECEIVED); state.put(category, classify(state.get(message))); return state; } MapString, Object networkFixNode(MapString, Object state) { // 这里调用网络服务重启、刷新配置等动作 state.put(handleResult, NETWORK_RESET_DONE); return state; } MapString, Object verifyNode(MapString, Object state) { // 调用验证服务看业务是否恢复 state.put(verifyPassed, verify(state.get(ticketId))); return state; }每个节点只感知自己需要的字段不依赖别的节点内部逻辑。一台服务器上的重启脚本、一个库存接口、一个审批服务都可以包成节点。这样做还有个额外好处就算某个节点崩溃整个图不会跟着崩框架可以按你设定的重试策略重新执行单个节点。4.3 用条件边控制分支接下来定义边。受理之后进入分类分类节点要判断去哪个处理路径这就是条件边.addConditionalEdges(分类, state - { String category (String) state.get(category); if (网络.equals(category)) return 网络修复; if (账号.equals(category)) return 账号解锁; return 人工审批; }, Map.of( 网络修复, 网络修复, 账号解锁, 账号解锁, 人工审批, 人工审批 ))这里的关键是把分类这个判断交给了大模型但把按分类走哪条路交给了程序。模型即使在分类上有一点抖动后续路由仍然是确定性的。你甚至可以不做LLM分类改用规则关键词分类那整个路由就完全由代码控制稳定性更高。我在生产环境里混合了两种先跑规则匹配匹配不上才让模型分类。4.4 组合成一个图并运行普通边和条件边组合起来StateGraphMapString, Object graph new StateGraph.Builder(MapStateSchema::new) .addNode(受理, this::receiveNode) .addNode(分类, this::classifyNode) .addNode(网络修复, this::networkFixNode) .addNode(账号解锁, this::accountFixNode) .addNode(人工审批, this::approvalNode) .addNode(验证, this::verifyNode) .addNode(通知, this::notifyNode) .addEdge(START, 受理) .addEdge(受理, 分类) .addConditionalEdges(分类, state - { ... }, Map.of(...)) .addEdge(网络修复, 验证) .addEdge(账号解锁, 验证) .addEdge(人工审批, 验证) .addEdge(验证, 通知) .addEdge(通知, END) .build(); StateSnapshot result graph.invoke(Map.of( ticketId, WO-2025-0312, message, CRM系统登录页一直在转圈 ));调用返回一个StateSnapshot记录了整趟运行最终状态和节点执行信息。在生产里我会把这个快照持久化一份作为流程审计日志。你甚至可以把这个快照关联到工单记录上后续排查问题时按工单ID直接查全链路非常方便。这只是一张顺序加分支的图已经能覆盖不少企业场景了。如果业务里还要并行检查和等待超时Spring AI Alibaba Graph也给并行节点这类扩展点我把真实场景里的并行拆解放到下一节讲。5. 回到那个让我头疼的场景故障工单闭环前面拆得比较抽象我把真实场景完整摆出来。当时做的是企业服务台故障工单用户报内部系统不可用工单系统会把问题推给这个AI流程。最开始的纯Agent版本有两次比较大的线上事故一次是模型把账号锁定误判成网络问题让网络团队白忙半天另一次是模型在验证步骤说应该没问题了但用户实际仍然登录不了问题又被退回客服。这两次之后没人敢直接信Agent的结论了。5.1 流程设计稿于是我们重新设计了流程按先确定性分类、再模板化处理、最后强制验证的节奏来步骤节点输入输出1受理工单内容工单ID、状态受理中2智能分类用户描述故障分类网络/账号/应用/其他3并行检查工单ID、分类检查结果集合4分支处理分类检查结果处理动作与结果5强制验证工单ID验证是否通过6通知与归档验证结果通知记录、归档状态智能分类节点内部调用大模型但它只输出一个枚举值不直接做处理动作。这一步把风险隔离在很小的范围里就算模型分类错了顶多路由到错误的处理分支后面还有验证节点兜底。所以我不追求模型的分类准确率百分之百我更关心流程能不能靠验证步骤兜住分类错误。5.2 并行节点是怎么用的检查环节用到了并行执行。三个检查动作彼此独立网络连通性检查、账号状态检查、应用日志检查。如果用普通顺序节点一次工单要串行几十秒用户和客服都等不住。并行节点把它们同时跑最终汇总成一份检查报告后面再按结果处理。并行节点的价值不仅在于快还在于它会强制你思考节点之间的依赖关系。哪些动作可以同时做哪些必须等前一步结果这本身就是一次很好的流程梳理。我见过不少团队写流程时把所有步骤串成一长条一加并行就懵原因就是没有认真想清楚数据依赖。Graph的并行能力逼着你去建模哪些数据是彼此独立的这个思考过程比框架本身更值钱。5.3 上线之后实际看到的变化切到Graph工作流之后最直观的变化是可观测性。每次调用我们都能从StateSnapshot里看到它走过了哪几个节点、每个节点耗时、状态字段的变更过程。以前排查一个工单要靠日志里的模型token猜测现在直接看节点执行记录五分钟就能定位到是网络修复节点超时还是验证节点返回false。流程的稳定性比纯Agent时代明显提升。因为所有分支都是显式定义我们给每个分支都补了测试覆盖了网络故障恢复账号锁定解锁验证失败转人工等典型路径。回归测试从原来的跟模型猜谜变成了断言状态结果意义完全不一样。模型在流程里退化成一个个被约束的组件反而发挥了自己的优势——分类、总结、生成通知文案这些事它干得很好。6. 踩坑记录直接照抄会翻车的几个细节这段我想重点写。Graph工作流框架能帮我们解决很多问题但踩坑也不少。我把最典型的几个列出来希望你不用重走一遍。6.1 共享状态不允许随意覆盖Graph里的状态是整个流程共享的所有节点都能读写。团队成员一多就有人为了图方便在一个节点里覆盖掉别的节点写过的字段。比如通知节点顺手把status改成了DONE后面审计节点拿到的状态就被污染了。我们后来的规范是每个节点只写自己负责的字段前缀比如检查节点都写check_开头的key处理节点都写handle_开头的key不准跨字段修改。Review的时候盯着这一点问题少了很多。这跟多线程编程里不要随便改别人的共享变量是一个道理只不过这里共享的是流程状态。6.2 条件边的目标节点别写错条件边返回值和目标节点名必须完全一致否则运行时会出现找不到下一个节点的异常。我们曾犯过一个低级错误条件返回人工审批但图上节点名写的是人工审批处理排查了半天才发现少打了两个字。建议你把目标节点名定义成常量集合别到处手写字符串。另外条件边的handler最好写成纯函数不要在里面调外部服务。因为你可能给条件边也加了重试一旦外部服务超时路由本身就被拖住了。条件路由应该是基于状态的快速判断所有需要外部信息的判断提前放到前置节点里去。6.3 重试要保证幂等工作流跑起来之后网络或服务抖动是常态。你可能很自然地给节点加重试策略但必须注意节点本身要能做幂等重新执行一遍不能产生副作用。比如通知用户节点如果重复执行用户会被短信轰炸。我们把通知节点设计成先查是否已通知过如果通知过了就直接返回已通知保证重试安全。这个设计要提前想好不然后面加班改代码的就是你。写节点之前先问一句这个节点如果执行两次会产生什么后果答不上来就先别写。6.4 别只依赖整图测试Graph最好测了因为每个节点都是普通方法。我会做三层测试第一层单节点测试。直接给状态对象调用节点方法断言返回状态不启动Spring容器跑得飞快。第二层子图测试。从某个中间节点作为入口开始跑只覆盖一条业务路径。这一步能抓住大部分边配置错误。第三层整图回归。模拟完整输入走到END用真实模型做端到端验证。因为涉及模型调用这条测试慢、贵不能天天全量跑。企业项目建议至少把第二层做扎实多数路由错误都发生在中间子路径上。如果团队里有刚接触Agent的同学我会建议他从第一层开始写这样他对节点职责会理解得更深。6.5 模型输出要限制格式在Graph里我用模型做分类时一开始让模型直接输出网络、账号、应用、其他这几个词结果模型偶尔输出是网络问题或者网络/账号导致路由判断失败。后来把所有分类节点改成让模型输出JSON{category: 网络}再用程序解析这个JSON字段。解析失败就落到默认分支走人工审批绝不让流程因为格式问题卡死。这也算一个通用经验跟模型打交道时给它的输出加上硬约束而不是靠提示词软约束。硬约束可能仍然有失败率但通常很稳定软约束失败率则是“薛定谔式”的。7. 一点编排体会项目上线后我经常被人问既然Graph什么都能编排是不是以后都用Graph别用Agent了我不这么看。Graph给的是流程骨架Agent给的是场景里的智能。一个会写诗、会翻译的Agent放到流程图里反而有可能被约束住反过来一个需要走审批、走验收入库的流程如果完全交给Agent自由发挥企业是承担不起那种不确定性的。7.1 我的取舍原则我现在每次接到需求先问三个问题。第一这个任务是不是一次性的单点问答如果是ReAct Agent直接上。第二这个任务是否需要多步骤闭环有没有明确的成功标准如果有必须走工作流。第三这个流程允许分支和回退吗如果允许Graph就是最合适的选择。在这个项目里Spring AI Alibaba Graph最大的价值不是性能也不是什么炫酷效果而是把AI流程变成了普通Java工程就能维护的东西。节点是普通方法边是显式声明状态字段可审计测试可断言。这一套组合拳下来模型升级、提示词调整、流程新增分支都不再是那个让人晚上睡不着觉的AI黑盒子。7.2 给后来者的三个建议最后给大家三个小建议。第一第一版永远先做顺序图把主线跑通再加条件边和并行节点。我见过太多人上来就想做并行和分支结果图越画越乱最后自己都解释不清每一条边为什么存在。流程编排的核心是清晰不是复杂。第二状态字段一定要克制。每加一个字段都会影响后面所有节点的判断逻辑。字段越少流程越不容易出错。我们后来砍掉了一半的冗余字段节点的写权限也收紧了状态冲突的概率立刻降了下来。第三记得把StateSnapshot落库。哪怕你只是简单存成JSON排查问题时都会省很多力气。这个快照就是AI流程的操作日志没有它出问题的时候你只能靠回忆和猜。如果你也正在纠结怎么编排企业里的复杂AI流程我建议你不要在理论上想太久先拿一个真实的小流程跑起来哪怕只是查状态、判断、通知三步。跑起来之后你自然能感受到流程确定性这四个字的分量。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑