从ReAct Agent到Graph Workflow:企业级AI流程编排实战与踩坑记录

发布时间:2026/10/12 5:49:53
从ReAct Agent到Graph Workflow:企业级AI流程编排实战与踩坑记录 弄企业AI的时候我最初跟大多数人一样脑子里只有“ReAct Agent”这个概念——让大模型自己想、自己调工具、自己给结论。但真正放到生产环境规范化流程跑起来之后问题就来了大模型一个请求里既要做意图判断又要做数据查询还要生成回复链路一长某一环出错整个对话就废了。最典型的是我们内部那个工单自动处理项目用户问一句“我的订单为什么还没审核通过”Agent得查订单、查库存、查物流、查审核状态任何一个查询失败或者返回格式不对整条链路就白跑。就在那个节点我开始研究Spring AI Alibaba Graph这套编排框架用图结构把流程拆成显式节点让每一步都看得见、控得住。这篇文章不写官方文档里已有的东西就把我从ReAct Agent迁到Graph Workflow的真实经历、设计思路、踩坑过程完整讲一遍。这内容适合正在做企业级AI应用、被大模型不可控性折磨、想把多个Agent编排成稳定业务流的开发者。尤其适合那种“AI只是流程里的一环而不是整个流程本身”的场景——比如风控审核、工单处理、客服质检、供应链异常处理。下面从设计思路开始一步步拆解。1. 先想清楚Agent和Workflow的边界到底划在哪1.1 ReAct Agent再快也顶不住流程里的“顺序约束”很多人一开始迷信ReAct是因为它确实灵活。你给大模型一堆工具它自己决定先调哪个、后调哪个、什么时候结束。这种模式对付单轮问答、开放域闲聊绰绰有余但企业流程恰恰是“反灵活”的必须先做A才能做BB的结果不合格就必须走C分支D步骤要等待人工确认超时就自动升级到主管。这种强烈的顺序约束和状态依赖ReAct在结构上就不支持。我举个最直观的例子某公司有个内部报销审核流程正常顺序是“票据识别→真伪校验→预算检查→审批”每一步的结果都会被下一步当作输入。如果只是让ReAct Agent自己一路调工具那它大概率会在“预算检查”时突然想起还应该确认一下报销人有没有违规记录于是多调一个接口——这本身没错但一旦这个额外查询拖慢了主流程审批时效就崩了。更麻烦的是一旦某一步超时Agent没有显式的重试策略它会自己编造一个“看起来合理但其实不对”的中间结果。所以ReAct的本质是“推理驱动的工具调用循环”适合探索性和弹性场景而企业的核心链路优先要的是“确定性的步骤”。这两种诉求的根本矛盾决定了我们得在Agent外面包一层流程控制壳。1.2 Graph工作流存在的理由把路由、分支、回退都变成显式节点Spring AI Alibaba Graph给我的第一感觉就是“用有向无环图约束大模型的自由”。每个节点负责一件事节点之间通过边连接条件边决定流程往哪走状态对象在节点之间传递数据。这样一来一个完整的业务流不再是大模型自己“临场发挥”的舞步而是完全受控的轨道。我们还是拿报销审核来说。Graph化之后流程是这样的入口节点“票据识别”调用OCR模型输出票据文本然后边走到“真伪校验”节点调验真接口校验结果作为条件边的判断依据——通过则进入“预算检查”不通过则直接进入“人工复核”。整个过程中大模型唯一的职责是做好“票据识别”这一步或者“预算检查”时读一下历史数据给个建议——它不再需要负责整条链路整个流程的推进完全由图结构控制。这种做法的好处有三层第一每一步都可观测日志里能看到当前执行到哪个节点卡在哪第二每一步都可重试单节点失败只影响该节点不再把整个对话搞崩第三条件路由是写死的不会出现“模型自作主张跳过关键校验”这种低级事故。说白了Graph是把“AI的能力框进业务的形状里”而不是让AI自己长成业务的形状。1.3 选Agent还是Workflow我的判断标准总结下来我现在的判断标准就三条有明确步骤顺序和状态流转的无脑用Workflow。比如审核、风控、工单处理步骤是业务定义的不是模型定义的。步骤本身不确定需要“探索”的用Agent。比如知识库问答用户问什么不确定模型需要自己判断查哪些文档、怎么组合答案。混合场景用Graph把Agent嵌在节点里。比如先分类再按类别进入不同的Agent子流程——这是我们后面要重点讲的做法。这三条不是拍脑袋想出来的是跑了几个项目之后总结出来的。早期我只要看到“复杂”两个字就上Agent结果被超时、幻觉、不可复现虐得体无完肤后来凡是跟业务强关联的流程一律Graph编排Agent只做理解、生成、总结这些“纯AI”的事线上稳定性明显提升了一个量级。2. Spring AI Alibaba Graph的核心设计拆解节点、状态、条件边2.1 节点就是“函数”状态就是“全局变量”边就是“路由规则”Spring AI Alibaba Graph的实际写法和LangGraph非常接近这让我一个从Python转过来的人特别亲切。核心就三个概念节点Node一个处理单元。Spring AI Alibaba里节点就是一个Java方法输入是当前的状态对象输出也是状态对象。每个节点只干一件事比如“调用大模型生成一句问候语”是一个节点“调用SQL查询用户订单”是另一个节点绝不混在一起。这样设计最大的好处是单元测试特别好写——一个节点就是一个纯函数给它一组输入断言它的输出完事。状态State一个贯穿全流程的数据对象。你可以把它理解成一个“全局变量”所有节点都能读它也都能改它。Spring AI Alibaba Graph里通常用一个Map或者自定义的POJO来承载。状态里该放什么、不该放什么直接决定了你能不能排查问题。我的习惯是原始输入、每个节点的中间产物、最终输出全部放进State方便事后审计但“临时变量”这类不入状态的东西坚决不塞进去否则状态会膨胀得没法看。边Edge节点之间的连接关系。边分两种普通边表示“无条件执行下一节点”条件边表示“根据当前状态判断下一个节点是谁”。条件边是Workflow的灵魂——它把“业务规则”和“模型任意性”隔离开来。比如校验失败走A节点、校验成功走B节点这个判断可以是一段Java代码而不是让大模型来拍板。2.2 状态机思维从入口到出口为什么路径设计比模型能力更关键如果你以前写过状态机上手Spring AI Alibaba Graph会非常顺畅。本质上它就是一张“流程图”入口节点是start出口节点是end中间任何节点都可以作为状态判断的分水岭。但这里有经验的差别路径到底该设计成“多步串行”还是“主路分支回退”这决定了流程的健壮度。我的经验是宁可多画一步也不要让某个节点里塞了太重的逻辑判断。比如有个场景“判断用户是否在黑名单里”这一步我一开始是放在“用户信息查询”节点内部一起做的结果黑名单接口超时连带用户信息也查不了整个节点就只能报错。后来拆成两个节点“用户信息查询”和“黑名单校验”前者失败了还能走缓存兜底后者失败了只影响风控分支路径清晰容错也好做。另一个点是入口要有校验出口要有兜底。入口校验是说在进入正式业务分支之前先做一个“输入合法性判断”节点数据不对直接走error结束别让脏数据进主流。出口兜底是说所有末端节点都尽量接一个“兜底回复”节点以防某个分支忘了处理某种边界情况导致用户收到的是一段大模型硬编的报错话术。2.3 条件路由怎么配三种写法和我的选型习惯Spring AI Alibaba Graph的条件路由有三种常见实现方式我都试过第一种是在节点内部直接改State然后在边配置里用SpEL表达式判断路由。这种方式好在“配置驱动”路由规则和数据是解耦的适合规则经常变的场景缺点是SpEL写多了之后不够直观调试要额外花一点时间。第二种是在节点里通过返回值指定下一节点ID然后Graph执行引擎根据返回值路由。这个方式体验最好就像写一个方法时returnnextNodeA一样逻辑一目了然缺点是把路由规则散落在各个节点的代码里流程图不能一眼看到全貌。第三种是结合Spring AI Alibaba自身的条件边API来写你可以在构建Graph时对每条条件边定义自己的判断函数。这个方式改动大但最灵活适合那种判断逻辑复杂的场景。我个人现在的习惯是简单分支用第一种复杂分支用第二种极少用第三种。因为第一种的配置集中在Graph定义处流程图看着清晰排查问题的时候看一眼配置就明白整个流程的走向体验最接近“用纸笔画流程图”的心智模型。3. 实战构建一个企业风控多Agent协作工作流3.1 需求拆解为什么风控必须用Graph而不是裸ReAct风控是所有企业流程里最适合用Graph演示的因为它有一条天然的“次序链路”先识别主体再查基础信息再做规则判断最后人工复核。每一步之间数据依赖非常强而且必须有明确的分支处理。如果用裸ReAct大模型在“识别主体”之后可能直接跳到“规则判断”跳过了“信息完整性校验”产生误判尚可忍受产生漏判就完全不可接受了。所以下面这个实战例子我用一个简化的“风控审核工作流”来演示Graph的核心用法入口先做用户输入解析把“被审核对象”提取出来接着并行拉取“用户基本信息”和“历史违规记录”再进“规则引擎”判断风险等级最后根据风险等级走不同分支——高风险进人工复核中低风险自动通过。整个流程里大模型只出现在两个位置输入解析节点和人工复核辅助建议节点。其他全是确定性逻辑这就是Graph在风控里最好的形态。3.2 第一个里程碑三个基础节点串联成功先用最简单的三节点穿起来跑通骨架解析输入 → 查询用户信息 → 输出结果。这个阶段不涉及任何条件路由纯粹验证Graph基本API能不能转起来。Graph graph new Graph(); graph.addNode(parse, ParseNode::execute); graph.addNode(queryUser, QueryUserNode::execute); graph.addNode(output, OutputNode::execute); graph.setEntryPoint(parse); graph.addEdge(parse, queryUser); graph.addEdge(queryUser, output); graph.setEndPoint(output);这三个节点的代码都不复杂核心是State的读取和写入。ParseNode从输入字符串里抽取出userId放进StateQueryUserNode根据userId调RPC查数据把查到的userInfo放回StateOutputNode从State里组一段话术作为最终结果。这里有个细节必须提一下每个节点写完State之后最好打印一行日志否则后面节点多了你根本不知道数据是哪个节点塞进去的这个问题越到后期越致命。跑通这个骨架之后我再把代码改成Spring AI Alibaba Graph的Spring Boot风格配置通过ApplicationRunner在启动时构建流程定义每个Node注册成BeanState用ConcurrentHashMap做实现。一定有读者问为什么不直接用并发安全的Map——我的回答是企业流程里大概率会有多实例并发State本身必须线程隔离后面我专门说这个坑。3.3 条件分支让流程自己决定走哪条路骨架通了之后加分支。现在的需求是查询完用户信息之后判断该用户是不是VIPVIP走“VIP专属处理”非VIP走“普通处理”。这个判断我们用条件边实现路由信息放在State的一个字段里。graph.addConditionalEdge(queryUser, state - { UserInfo user (UserInfo) state.get(userInfo); return user.isVip() ? vipProcess : normalProcess; }, List.of(vipProcess, normalProcess));这段代码的运行效果是queryUser节点结束之后Graph会调用这个Lambda去判断该去哪个节点——没错这个Lambda就是条件路由的“业务规则”完全由我们写死不经过大模型。这看起来是个小步骤但价值非常大它把流程中“决策”的逻辑从模型手里夺了回来。这里我踩过一个坑条件路由里返回的节点名必须跟addNode时的名字完全一致大小写都不能错。一旦不一致执行引擎会报“找不到目标节点”的错而且报错信息里只会给出一个节点ID不提示候选列表定位起来非常浪费时间。所以我的习惯是所有节点名定义一个常量类统一管理绝不裸写字符串。3.4 子图与并行避免把图做成“意大利面条”当流程节点超过10个之后Graph定义会变得越来越长把全部节点铺在一个平面上维护起来非常痛苦。以风控举例光是“规则引擎”内部就包含了命中条件组装、规则执行、结果解析三个子节点如果平铺画出来主线图和分支图交织在一起阅读性极差。Spring AI Alibaba Graph支持子图嵌套这个设计非常像编程里的函数封装。我把“规则引擎”那三个节点包成一个子图对外只暴露一个“ruleEngine”节点。这样在大图层面看起来从“queryUser”到“ruleEngine”再到“judgeResult”只有三个节点清晰明了但子图内部又是完整的Graph执行逻辑。并行这块我也一并说了。在风控场景里“查基本信息”和“查历史记录”之间没有依赖关系完全可以并行。Spring AI Alibaba Graph的并行实现跟常规JAVA并发没什么区别——状态对象各自构建、Future来聚合结果最后把多个结果合并进同一个State。这里有一句忠告并行节点的State写回必须非常小心。两个并行分支同时改State的同一个key后写会覆盖先写导致数据丢失。我的做法是并行分支里绝不写同一个主键每个分支写独立的key汇合节点统一合并这样才能彻底避开并发写冲突。4. 从ReAct Agent到Graph Workflow的一次真实重构工程师工单自动处理4.1 业务背景旧方案的三个核心痛点这个案例是某公司内部的IT支持工单自动处理系统。最初的方案是纯ReAct Agent大模型接到员工工单后自行决定调“身份查询”“故障知识库”“工单系统”等工具最后生成处理建议。模型很聪明demo演示效果也好但一上线问题就露出来了。第一个痛点是查“身份”和查“工单”的接口可能各自返回超时Agent会在没有完整上下文的情况下“聪明地”生成一个建议后果是员工被建议去重启一台根本没有问题的服务器。第二个痛点是工单有严格的“服务等级协议”要求——高危故障必须在2小时内处理而ReAct Agent不会主动给工单分级它把所有问题一视同仁结果高危工单经常被埋在普通工单后面。第三个痛点是排错过程完全不可控产品同学要求我解释“这个建议是怎么来的”我根本说不清大模型中间做了多少次工具调用。这三个痛点本质上都源于“AI主导了整个流程”。而从ReAct迁到Graph之后这三个问题迎刃而解。4.2 Graph重构后的流程设计重构后我画了一张图入口节点接收工单文本 → “分类节点”用大模型给工单打标签网络类/账号类/硬件类→ “分级节点”根据标签和故障描述判断SLA等级P0/P1/P2→ “查询节点”并行拉取员工身份、设备资产、历史工单 → “知识库检索节点”按标签查相关解决方案 → “回复生成节点”汇总以上所有信息生成最终处理建议。这个流程跟原来的ReAct Agent看起来很像但本质区别在于每一步都从“模型可能做”变成了“流程必须做”。分类错了后面流程照走但日志里能看到“分类节点输出错误标签”查询失败流程会走“重试→降级→转人工”的分支而不是让模型自己编数据。尤其是“分级”这一步它完全是业务规则驱动的工单标题里有“无法开机”“蓝屏”“数据丢失”这类词直接判为P0普通网络问题判为P2。这一步根本不需要大模型一个关键词匹配就能完成。但脱离Graph之后它在ReAct模型里是一个“工具”模型想用才用不想用就跳过——这就是不可控的根源。4.3 关键节点实现细节与难产点整个重构里最麻烦的节点是“分类节点”。之前我用ReAct的时候分类和回复是同一个模型调用天然带上下文。现在拆成独立节点后这个节点就只负责输出一个JSON{category: network, sla: P2}。为了让结果稳定我给它加了输出约束直接把期望的JSON结构写进System Prompt里并且用Spring AI Alibaba的JsonSchema方式定义输出结构。实测效果很好输出格式非常稳定但仍有极小概率输出非法JSON。我的兜底方案是分类节点后面加一个“解析检查”子节点解析失败就直接走默认分类不让整个流程卡死。“知识库检索节点”也算一个难产点。检索本身不难难的是检索结果的截断——知识库经常返回几万字的长文本如果全塞给最后一步生成节点输入长度会被撑爆。我的处理是在检索节点内部做了摘要用另一条轻量模型调用把长文本压缩到500字以内这样下游生成节点始终只面对规整、精简的上下文。4.4 上线后遇到的问题重试风暴和状态残留重构后第一次上线就遇到“重试风暴”。当时“查询节点”调工单接口超时Graph默认重试3次3次失败后走“降级分支”查询本地缓存。但我在写重试逻辑时没有加退避间隔相当于3次重试在几毫秒内全部打向一个已经过载的接口直接把对方打得更慢了。后来在重试配置里加上了“指数退避最大重试上限”问题才缓解。第二个问题是状态残留。当时为了图省事State用的全局单例Map结果A用户跑完流程之后留下的userInfo被B用户的下一次流程读到了——这在开发环境几乎不可能发现但在生产环境就是严重的数据泄露事故。解决方式是每次流程开始前new一个State对象所有节点共享这一个实例流程结束之后整个State随着请求一起销毁。这一点怎么强调都不为过Spring AI Alibaba Graph的State生命周期必须跟着请求走绝不能复用。5. 从工程视角看这套编排方案的成熟度5.1 开发体验上手成本不高但设计成本不低从开发者的体感来说Spring AI Alibaba Graph的上手成本不算高。基本Node/Edge/State的概念半天就能跑通第一个Demo。但真正难的是“设计”怎么拆节点拆得合适怎么设计State字段条件路由的粒度多大合适什么时候抽子图什么时候并行。这些能力不是啃文档啃出来的而是在真实业务里踩出来的。我的经验是拆节点要“按失败域拆”。一个节点包含两种可能失败的操作就应该拆成两个节点。比如“查询订单”和“计算订单金额”是两个失败域前者可能网络超时后者可能数据缺失拆开之后可以各自定义重试策略和降级方案——这是Graph编排较之于单体Agent最大的工程优势。5.2 可观测性Graph带来的运维红利用Graph之后监控那块儿轻松了很多。以前ReAct的日志是一长串模型调用记录很难判断流程走到哪了现在只要在每个节点入口打印一行日志就能完整还原那次请求的路径。我还给每个节点加了耗时统计和成功/失败计数用Micrometer暴露成指标接线到监控大盘之后哪个节点慢、哪个节点失败率高一目了然。这里有个小技巧在State里新增一个list字段每次节点执行完就把节点名追加进去这样日志里配合traceId随时能完整回放某次请求走过哪些节点、每个节点消耗多久。在做事故复盘的时候这份数据价值极高。5.3 编排引擎之外组件生态还需要补课坦率说Spring AI Alibaba Graph目前还谈不上成熟。跟LangGraph相比它的算子类型还不够丰富比如“Map-Reduce”“Send”这类高级节点还没有现成的抽象遇到复杂的扇出聚合逻辑只能自己手写并行和合并。另外它对“人工介入”的支持也比较原始企业流程里最常见的“挂起等待人工审批”这个操作官方没有开箱即用的实现需要自己用异步回调或轮询去模拟。但这恰恰意味着动手能力强的人可以发挥。我在实际使用里自己封装了“人工审批节点”的原型节点执行时生成审批任务ID把流程状态持久化到数据库同时挂起线程另一边审批接口回调后用审批结果唤醒挂起流程并继续执行后续节点。这个实现虽然粗糙但已经能支撑真实的联调测试而不再是被动地在ReAct里“让模型假装等了一下”。5.4 踩坑清单状态污染、循环引用、超时累积最后把这些坑梳理成一张速查表给准备上手的人排雷典型问题触发场景推荐解法状态污染State被复用或并行分支写同keyState按请求创建并行分支分key写汇合节点合并循环引用条件边的目标节点名写错节点名统一常量管理启动时做拓扑检测超时累积上游节点没事下游节点超时每个节点单独设置超时和重试加指数退避节点间数据格式不统一上游存的是对象下游按字符串读用POJO定义State而不是到处用Map裸转并行分支信息合并丢失两分支同时写同一字段合并阶段显式声明字段优先级杜绝覆盖LLM输出不可解析分类节点输出非法JSON输出用JsonSchema约束后端加解析兜底如果你正在构建类似的智能体系统尤其涉及输出不可控的内容生成时可以多参考输出解析与元数据提取的经验这类问题的通用解决思路是一致的在模型之外构建确定性的校验与容错层而不是把所有希望寄托在大模型的自我修正上。从我的实践里提炼出的最终建议如果一定让我给一条最实用建议那就是先画图再写代码。打开白板把业务流程的每一步、每个分支、每个异常处理路径画出来确认无误了再对照图去写Node和Edge。大多数人写Graph代码失控不是因为写代码的能力不行而是因为图的拓扑就没设计清楚。另外不要迷信“全流程AI化”。很多地方用规则、用状态机、用传统代码就够了把它们留在图里做确定性节点把真正需要语义理解的地方交给大模型这样才能既保住AI的灵活性又不牺牲企业流程的稳定性。这是我用了半年多Spring AI Alibaba Graph之后最大的心得。这个方向后续还有很大的扩展空间——比如多级子图联动、人机协同审批、基于图拓扑的自动回滚。技术演进不会停但把复杂流程拆成可控节点的思路在很长一段时间内都不会过时。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询