资讯动态

Spring AI Alibaba Graph 实战:从 ReAct Agent 到企业级 Workflow 编排

发布时间:2026/10/19 11:33:05 来源:尧图企业网站定制
1. 从 ReAct Agent 到 Workflow 的编排困局1.1 为什么单 Agent 模式在企业场景里走不远ReAct 模式刚出来那阵子几乎所有人都在拿它做 Demo。一个 Agent 配上几个 Tool用户问一句它调一次工具看起来确实像那么回事。但真把它丢进企业业务流里跑问题很快就暴露了。我最早接触 ReAct Agent 是在一个内部知识问答的场景里当时的需求听起来很简单用户提问Agent 判断要不要查知识库查到之后组织语言回答。单轮对话下确实没问题但只要业务稍微复杂一点比如“先查订单状态如果已发货就查物流如果未发货就查库存库存不足就触发补货提醒”ReAct 就开始力不从心了。原因很直接ReAct 的本质是“边想边做”它没有一个全局的流程视图每一步都依赖上一步的推理结果来决定下一步。这种模式在步骤少、分支少的时候很灵活但一旦步骤超过五六个、分支超过三四个Agent 的推理链路就会变得极不稳定。更麻烦的是可观测性。ReAct Agent 的执行过程是一个黑盒你只能看到它最终调了哪些工具、返回了什么结果但中间为什么这么调、有没有走弯路、哪一步耗时最长这些信息很难拿到。企业场景对可观测性的要求是很高的出了故障要能定位、要能复现、要能优化ReAct 这种“自由发挥”的模式天然就不满足这些条件。还有一个容易被忽略的点是成本。ReAct 每一步都要调用大模型做推理步骤越多Token 消耗越大。一个五步的流程如果每步都让模型重新推理一遍Token 消耗可能是固定编排流程的三到五倍。在内部测试阶段可能感觉不明显但上了生产环境、QPS 一上来账单会教你做人。所以结论很明确ReAct Agent 适合做探索性的、步骤不固定的任务比如开放式问答、信息检索、简单的工具调用。但企业里的复杂流程需要的是确定性、可观测、可复现的编排能力这就得靠 Workflow。1.2 Workflow 编排到底在编排什么很多人一听到 Workflow 就觉得是“把几个节点连起来”这个理解太浅了。Workflow 编排的核心不是连线而是对状态、控制流、数据流三件事的统一管理。状态管理解决的是“当前流程走到哪了、每个节点的输出是什么、后续节点需要哪些前置数据”这些问题。控制流解决的是“下一步走哪个分支、什么条件下循环、什么条件下终止”。数据流解决的是“节点之间的数据怎么传递、格式怎么转换、上下文怎么维护”。这三件事在 ReAct 里是隐式的、由模型推理决定的在 Workflow 里是显式的、由开发者定义的。显式定义带来的好处是流程可预测、可测试、可优化。你可以针对每个节点单独写单元测试可以给每个节点设置超时和重试策略可以在节点之间插入人工审核环节这些都是 ReAct 做不到的。Spring AI Alibaba Graph 这套东西本质上就是给 Java 生态提供了一套声明式的 Workflow 编排能力。它把流程抽象成图Graph节点Node是执行单元边Edge是流转规则状态State在节点之间传递。你定义好图的结构运行时引擎负责调度执行。这个思路和 LangGraph 是一脉相承的但它是 Java 原生的对国内用 Spring 技术栈的团队来说接入成本低很多。1.3 企业 AI 复杂流程的典型特征在动手写代码之前得先搞清楚“企业 AI 复杂流程”到底复杂在哪。我总结下来主要是四个维度多分支条件。业务流程里到处都是 if-else比如审批流里金额不同走不同审批人客服场景里问题类型不同走不同处理链路。这些分支条件有些是确定性的规则有些需要模型来判断。多角色协作。一个流程里可能涉及多个 Agent有的负责信息提取有的负责决策有的负责生成回复。这些 Agent 之间怎么交接、怎么共享上下文是需要设计的。人工介入。企业流程不可能全自动总有些环节需要人来确认或补充。Workflow 要支持在任意节点暂停、等待人工输入、然后继续执行。长时运行与容错。一个流程可能跑几分钟甚至几小时中间某个节点失败了要能重试整个流程挂了要能从断点恢复这些在 ReAct 里基本没法做。理解了这些特征才能理解为什么需要 Graph 这种编排方式以及为什么 Spring AI Alibaba Graph 要设计成现在这个样子。2. Spring AI Alibaba Graph 核心概念拆解2.1 Graph、Node、Edge、State 四件套Spring AI Alibaba Graph 的核心抽象就四个Graph、Node、Edge、State。把这四个搞明白剩下的都是细节。Graph是整个流程的容器你可以把它理解成一张流程图。它定义了有哪些节点、节点之间怎么连、从哪个节点开始、到哪个节点结束。Graph 本身不执行任何业务逻辑它只负责描述结构。Node是执行单元每个节点做一件具体的事。节点可以是调用大模型、调用外部 API、执行一段 Java 代码、甚至是嵌套另一个 Graph。节点的输入是当前 State输出是对 State 的更新。Edge是流转规则定义了从一个节点到另一个节点的跳转条件。Edge 分两种普通 Edge 和条件 Edge。普通 Edge 就是无条件跳转A 执行完直接到 B。条件 Edge 会根据 State 里的某个值来决定走哪条路这是实现分支的关键。State是贯穿整个流程的上下文所有节点共享同一个 State 对象。每个节点可以从 State 里读数据也可以往 State 里写数据。State 的设计直接决定了流程的灵活性和可维护性。这四个概念组合起来就能表达任意复杂的流程结构。分支用条件 Edge 表达循环用 Edge 指回前面的节点并行用多个 Edge 从同一节点出发人工介入用特殊的 Node 来暂停流程。2.2 State 设计整个编排的地基State 设计是整个 Workflow 里最容易翻车的地方。我见过太多项目流程逻辑写得没问题但因为 State 设计得乱七八糟后期维护成本极高。State 设计的第一个原则是类型安全。Spring AI Alibaba Graph 支持用 POJO 或者 Map 来作为 State。用 Map 的好处是灵活什么都能往里塞坏处是没有任何类型检查运行时才发现 key 写错了是常有的事。我的建议是能用 POJO 就用 POJO把流程里需要用到的字段都定义成类的属性编译期就能发现大部分问题。第二个原则是职责单一。State 里只放流程流转需要的数据不要把业务实体的所有字段都塞进去。比如一个订单处理流程State 里放 orderId、status、currentStep 就够了不需要把订单的所有详情都放进去。需要详情的时候节点自己去查。第三个原则是不可变优先。如果框架支持尽量让 State 是不可变的每次节点执行返回一个新的 State 对象。这样做的好处是每个节点的输入输出都很清晰调试的时候可以清楚地看到每一步 State 的变化。如果性能有要求用可变 State 也行但要做好并发控制。第四个原则是版本兼容。State 的结构可能会随着业务变化而调整如果流程需要长时间运行或者需要从持久化存储恢复State 的序列化兼容性就很重要。建议给 State 加一个 version 字段反序列化的时候根据版本做兼容处理。public class OrderFlowState { private String orderId; private String status; private String currentStep; private ListString history; private int version 1; // getters and setters }2.3 条件边与循环让流程真正“活”起来条件边是 Workflow 实现分支的核心机制。它的工作原理是节点执行完之后引擎会遍历从该节点出发的所有条件边依次判断每个边的条件是否满足第一个满足条件的边会被选中流程沿着这条边走到下一个节点。条件边的条件可以用 Java 的 Predicate 来表达也可以用 SpEL 表达式。用 Predicate 的好处是类型安全、可以写复杂逻辑坏处是条件逻辑散落在代码里不够直观。用 SpEL 的好处是条件可以配置化改条件不用重新编译坏处是复杂逻辑写起来比较绕。我的经验是简单的条件用 SpEL复杂的条件用 Predicate。循环的实现方式是把边指回前面的节点。但这里有个坑如果没有终止条件循环会无限执行下去。所以循环一定要配一个计数器或者状态标记达到阈值就跳出循环。另外循环体内的节点要保证幂等因为循环可能会重试。graph.addConditionalEdges(checkInventory, Map.of( inStock, createOrder, outOfStock, notifyProcurement ), state - state.getStatus().equals(IN_STOCK) ? inStock : outOfStock );2.4 与 LangGraph 的对比Java 生态的差异化选择LangGraph 是 Python 生态里做 Agent 编排最成熟的方案之一Spring AI Alibaba Graph 在设计上借鉴了很多 LangGraph 的思路但针对 Java 生态做了不少调整。最大的差异在并发模型上。LangGraph 基于 Python 的 asyncio天然适合 IO 密集型的场景。Spring AI Alibaba Graph 基于 Java 的线程池和 CompletableFuture在高并发场景下更稳定但编程模型相对复杂一些。第二个差异在状态管理上。LangGraph 的 State 是基于 TypedDict 的灵活但类型检查弱。Spring AI Alibaba Graph 支持强类型 State对大型项目更友好。第三个差异在生态集成上。Spring AI Alibaba Graph 天然能和 Spring 生态集成依赖注入、事务管理、监控埋点这些都能复用现有的基础设施。LangGraph 在这方面需要自己搭。选哪个不是非此即彼的问题。如果团队主要是 Java 技术栈Spring AI Alibaba Graph 是更自然的选择。如果团队是 Python 为主或者需要用到 Python 生态里的一些特定库LangGraph 更合适。3. 从零搭建一个可落地的编排流程3.1 环境准备与依赖引入先把依赖引进来。Spring AI Alibaba Graph 的依赖坐标会随着版本变化建议直接去官方仓库查最新的版本号。核心依赖就一个dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-graph-core/artifactId version${latest.version}/version /dependency如果要用到模型能力还需要引入对应的模型 Starter。这里有个坑Spring AI Alibaba Graph 的版本和 Spring AI 的版本有对应关系版本不匹配会出现各种奇怪的 NoSuchMethodError。建议在引入之前先确认版本兼容矩阵。配置方面主要是模型相关的配置。如果用的是 DashScope 的模型配置大概长这样spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus temperature: 0.7API Key 不要硬编码在配置文件里用环境变量或者配置中心来管理。这个是最基本的安全要求但我在代码审查里还是经常看到有人直接写在 yaml 里。3.2 定义 State流程的数据契约State 的定义决定了整个流程的数据契约。我以一个“智能客服工单处理”流程为例来演示 State 该怎么设计。这个流程的需求是用户提交工单系统先做意图识别根据意图分到不同的处理链路。如果是咨询类直接由模型生成回复如果是投诉类先做情绪分析情绪激烈就转人工情绪平稳就由模型生成安抚回复并记录如果是技术问题先查知识库查到就返回答案查不到就转人工。State 需要承载的数据包括工单 ID、用户输入、意图分类结果、情绪分析结果、知识库查询结果、最终回复、当前步骤、处理历史。public class TicketFlowState { private String ticketId; private String userInput; private String intent; private String sentiment; private String knowledgeResult; private String finalReply; private String currentStep; private ListString history new ArrayList(); private int retryCount 0; private int version 1; public void addHistory(String step) { this.history.add(step); this.currentStep step; } // getters and setters }这里有几个设计决策值得说明。history字段记录每一步的执行轨迹方便调试和审计。retryCount用于控制重试次数防止无限循环。version用于后续的序列化兼容。currentStep是冗余字段但它在条件边判断和日志打印时非常有用所以保留。3.3 节点实现每个节点只做一件事节点实现的核心原则是单一职责。一个节点只做一件事做完了把结果写进 State然后交给下一个节点。不要把多个逻辑塞进一个节点那样会让流程变得难以测试和复用。意图识别节点public class IntentRecognitionNode implements NodeTicketFlowState { private final ChatClient chatClient; public IntentRecognitionNode(ChatClient chatClient) { this.chatClient chatClient; } Override public TicketFlowState apply(TicketFlowState state) { String prompt 请判断以下用户输入的意图只返回意图类别不要有其他内容。 可选类别咨询、投诉、技术问题。 用户输入%s .formatted(state.getUserInput()); String intent chatClient.prompt(prompt).call().content(); state.setIntent(intent.trim()); state.addHistory(intent_recognition); return state; } }这个节点看起来简单但有几个细节要注意。Prompt 里明确要求“只返回意图类别”是为了避免模型返回一堆解释性文字导致后续判断失败。trim()是为了去掉可能的空白字符。实际生产环境里还需要对模型返回做校验如果返回的不是预期类别要有兜底逻辑。情绪分析节点类似也是调模型但 Prompt 和输出格式不一样。知识库查询节点则是调外部 API不涉及模型调用。回复生成节点又是调模型但 Prompt 里要带上意图、情绪、知识库结果等上下文。这里有个经验节点之间的数据传递尽量通过 State不要通过节点之间的直接引用。我见过有人为了图方便在节点 A 里把结果存到一个静态变量里节点 B 直接读静态变量。这种做法在单线程下没问题一旦并发上来就会出各种诡异的数据串扰问题。3.4 构建 Graph把节点连成流程节点定义好了接下来就是构建 Graph。构建过程分三步创建 Graph 实例、添加节点、添加边。StateGraphTicketFlowState graph new StateGraph(TicketFlowState.class); graph.addNode(intent, new IntentRecognitionNode(chatClient)); graph.addNode(sentiment, new SentimentAnalysisNode(chatClient)); graph.addNode(knowledge, new KnowledgeQueryNode(knowledgeService)); graph.addNode(reply, new ReplyGenerationNode(chatClient)); graph.addNode(transfer, new TransferToHumanNode()); graph.addNode(end, new EndNode()); graph.setEntryPoint(intent); graph.addConditionalEdges(intent, Map.of( 咨询, reply, 投诉, sentiment, 技术问题, knowledge ), state - state.getIntent() ); graph.addConditionalEdges(sentiment, Map.of( 激烈, transfer, 平稳, reply ), state - state.getSentiment() ); graph.addConditionalEdges(knowledge, Map.of( found, reply, notFound, transfer ), state - state.getKnowledgeResult() ! null ? found : notFound ); graph.addEdge(reply, end); graph.addEdge(transfer, end); CompiledGraphTicketFlowState compiledGraph graph.compile();这段代码里有几个关键点。setEntryPoint指定了流程的入口节点。addConditionalEdges的第三个参数是一个函数它从 State 里提取出用于判断的值然后根据这个值去 Map 里找对应的目标节点。compile()会把 Graph 编译成一个可执行的 CompiledGraph编译过程中会做校验比如检查是否有节点没有出边、是否有环没有终止条件等。3.5 执行与状态持久化编译完之后就可以执行了TicketFlowState initialState new TicketFlowState(); initialState.setTicketId(T20240101001); initialState.setUserInput(我买的商品到现在还没发货太慢了); TicketFlowState result compiledGraph.invoke(initialState); System.out.println(result.getFinalReply());invoke是同步执行还有stream方法可以流式获取每个节点的执行结果适合做实时进度展示。状态持久化是企业场景的刚需。Spring AI Alibaba Graph 支持通过 Checkpoint 机制来持久化 State每个节点执行完之后会把 State 存下来。这样如果流程中断了可以从最后一个 Checkpoint 恢复不用从头跑。Checkpoint 的存储可以用内存、文件、数据库。生产环境建议用数据库并且要考虑并发写入的问题。如果同一个流程实例可能被多个线程同时恢复需要加锁或者用乐观锁来保证一致性。4. 复杂场景下的编排进阶技巧4.1 多 Agent 协作让专业的人做专业的事单 Agent 处理复杂任务时Prompt 会变得非常长模型容易“精神分裂”。更好的做法是把任务拆给多个 Agent每个 Agent 有自己的角色和职责通过 Workflow 来协调。比如一个合同审核流程可以拆成三个 Agent条款提取 Agent 负责从合同文本里提取关键条款风险识别 Agent 负责判断条款是否有风险建议生成 Agent 负责给出修改建议。三个 Agent 串行执行前一个的输出是后一个的输入。多 Agent 协作的关键是上下文传递。每个 Agent 只需要自己关心的那部分上下文不要把整个 State 都塞给模型。比如条款提取 Agent 只需要合同原文风险识别 Agent 只需要提取出来的条款建议生成 Agent 需要条款和风险点。按需传递上下文既能降低 Token 消耗又能提高模型输出的准确性。4.2 人工介入在流程中插入 Human-in-the-Loop企业流程不可能全自动总有些环节需要人来确认。Spring AI Alibaba Graph 支持在节点执行前或执行后暂停流程等待外部输入。实现方式有两种。一种是中断式流程执行到某个节点时抛出中断信号引擎把当前 State 持久化然后流程挂起。外部系统处理完之后带着 State 重新恢复执行。另一种是轮询式流程执行到某个节点时节点内部去查一个外部状态如果人工还没处理完就等待处理完了就继续。中断式更适合长时等待的场景因为流程完全挂起不占用线程资源。轮询式更适合短时等待的场景实现起来简单一些但会占用线程。人工介入的节点设计要注意State 里要记录人工输入的结果并且要能区分“人工同意”和“人工修改”两种情况。人工同意就直接走下一步人工修改则要把修改后的内容写回 State后续节点基于修改后的内容继续执行。4.3 错误处理与重试让流程经得起折腾生产环境的流程必须考虑失败。模型调用可能超时外部 API 可能返回错误网络可能抖动。这些都要有处理策略。Spring AI Alibaba Graph 支持在节点级别配置重试策略。重试策略包括最大重试次数、重试间隔、退避策略。对于模型调用这种可能因为限流而失败的场景建议用指数退避第一次等 1 秒第二次等 2 秒第三次等 4 秒。但重试不是万能的。有些错误重试多少次都没用比如参数错误、权限不足。所以重试策略要配合错误分类可重试的错误才重试不可重试的错误直接走失败分支。失败分支的处理方式取决于业务。有些场景可以降级比如模型调用失败就返回一个默认回复。有些场景必须人工介入比如支付失败。这些都要在 Graph 里定义清楚。graph.addNode(reply, new ReplyGenerationNode(chatClient) .withRetry(RetryPolicy.builder() .maxAttempts(3) .backoff(Backoff.exponential(Duration.ofSeconds(1), 2.0)) .retryOn(TimeoutException.class, RateLimitException.class) .build()));4.4 可观测性让流程不再是黑盒可观测性是企业级编排和 Demo 级编排的分水岭。没有可观测性出了问题只能靠猜。Spring AI Alibaba Graph 提供了几个层面的可观测性。节点级别每个节点的执行时间、输入输出、成功失败状态都可以记录。流程级别整个流程的执行轨迹、总耗时、经过的节点路径可以记录。State 级别State 在每个节点前后的变化可以记录。这些数据可以输出到日志、Metrics、Tracing 三个地方。日志用于事后排查Metrics 用于监控告警Tracing 用于分析性能瓶颈。我一般会在每个节点里加一段统一的埋点代码记录节点名称、开始时间、结束时间、State 的关键字段。这些数据打到日志里配合 ELK 或者类似的分析工具排查问题的时候非常方便。public TicketFlowState apply(TicketFlowState state) { long start System.currentTimeMillis(); try { // 业务逻辑 return state; } finally { long cost System.currentTimeMillis() - start; log.info(node{}, ticketId{}, cost{}ms, step{}, intent, state.getTicketId(), cost, state.getCurrentStep()); } }5. 常见问题与排查技巧实录5.1 流程不按预期走分支怎么办这是最常见的问题。明明条件边配好了但流程就是不走预期的分支。排查思路分三步。第一步确认 State 里的判断字段值是否正确。在条件边的判断函数里打日志把实际的值打出来。很多时候是上游节点没有正确设置这个字段或者设置的值和条件边里写的值不一致比如大小写、空格。第二步确认条件边的 Map 是否覆盖了所有可能的值。如果 State 里的值不在 Map 的 key 里流程会走默认分支或者直接报错。建议在条件边的判断函数里加一个兜底逻辑遇到未知值走一个默认分支。第三步确认条件边的顺序。如果从同一个节点出发有多条条件边引擎会按顺序判断第一个满足条件的边会被选中。如果条件之间有重叠可能会走错分支。建议条件之间互斥不要有重叠。5.2 State 序列化失败怎么处理State 需要序列化的时候比如持久化到数据库、通过网络传输如果 State 里有不可序列化的字段就会报错。常见的不可序列化字段包括Lambda 表达式、ThreadLocal、某些第三方库的对象。解决办法有两个。一是给不可序列化的字段加transient关键字序列化的时候跳过。二是自定义序列化器把这些字段转换成可序列化的形式。比如一个字段是Function类型可以序列化成类名反序列化的时候再反射创建。还有一个坑是版本兼容。State 的类结构变了之后反序列化旧数据可能会失败。解决办法是给 State 加serialVersionUID并且在反序列化的时候做版本判断旧版本的数据用兼容逻辑处理。5.3 流程执行超时怎么排查流程执行超时可能有很多原因排查的时候要分段定位。先看是单个节点慢还是整体流程慢。如果单个节点慢看这个节点在做什么。如果是模型调用可能是 Prompt 太长、模型负载高、网络延迟。如果是外部 API 调用可能是对方服务慢。如果是数据库查询可能是索引没建好。如果整体流程慢但每个节点都不慢那可能是节点太多或者流程走了弯路。看执行轨迹确认流程有没有走不必要的分支。有时候是因为条件边判断错误导致流程走了远路。还有一种情况是死循环。流程在几个节点之间来回跳永远出不来。这种一般是循环的终止条件没写对。排查的时候看执行轨迹如果发现某几个节点反复出现基本就是死循环了。5.4 并发场景下的状态污染问题这个问题很隐蔽单线程测试的时候完全正常一上并发就出各种诡异问题。根本原因是多个线程共享了同一个 State 对象。Spring AI Alibaba Graph 的invoke方法每次调用都会创建一个新的执行上下文正常情况下不会有状态污染。但如果节点里用了静态变量、ThreadLocal、或者单例对象来存状态就会出问题。排查方法在节点里打印 State 对象的 hashCode看不同请求的 hashCode 是否一样。如果一样说明共享了同一个对象。解决办法是确保每个请求都有独立的 State 实例不要在节点里用静态变量存请求级别的数据。5.5 常见问题速查表问题现象可能原因排查方法解决方案流程不走预期分支State 字段值不对在条件边打日志检查上游节点是否正确设置字段流程不走预期分支条件 Map 未覆盖检查 Map 的 key加兜底分支序列化失败字段不可序列化看异常堆栈加 transient 或自定义序列化器流程超时单节点慢看节点耗时日志优化节点逻辑或加超时流程超时死循环看执行轨迹修正循环终止条件并发状态污染共享 State 对象打印 hashCode确保每请求独立 State流程无法恢复Checkpoint 未存检查持久化配置配置 Checkpoint 存储模型输出不稳定Prompt 不明确看模型原始输出优化 Prompt 加格式约束5.6 几个踩过的坑坑一条件边的判断函数里做了耗时操作。条件边会被频繁调用如果判断函数里查数据库或者调 API会严重影响性能。判断函数里只做内存里的简单判断需要查数据的提前查好放到 State 里。坑二节点里抛异常没有捕获。节点里抛出的异常如果没有被捕获会导致整个流程失败。建议在节点里做好异常处理可恢复的异常走重试或降级不可恢复的异常包装成业务异常往上抛。坑三State 里的集合类型用了不可变集合。有些节点会往 State 的集合字段里 add 元素如果这个集合是Collections.unmodifiableList或者List.of创建的会抛UnsupportedOperationException。State 里的集合字段用ArrayList这种可变集合。坑四忘记设置入口节点。Graph 编译的时候如果不设置入口节点会报错。这个错误很明显但新手容易忘。坑五节点名称重复。Graph 里节点名称必须唯一重复了会覆盖。建议用有意义的名称不要用node1、node2这种。6. 从能跑到好用生产化改造清单6.1 配置外置化流程里的很多参数应该外置到配置文件而不是硬编码在代码里。比如模型名称、超时时间、重试次数、分支阈值。外置化的好处是改参数不用重新编译部署运维成本低。配置的粒度要把握好。太粗了不够灵活太细了配置项爆炸。我的经验是变化频率高的参数外置变化频率低的参数硬编码。比如模型名称可能经常换就外置流程的节点结构基本不变就硬编码。6.2 监控告警接入生产环境必须有监控。核心监控指标包括流程执行成功率、平均耗时、P99 耗时、各节点耗时分布、模型调用成功率、Token 消耗量。这些指标要接入现有的监控体系设置合理的告警阈值。比如成功率低于 95% 告警P99 耗时超过 10 秒告警。告警要能定位到具体是哪个节点、哪个流程实例出了问题。6.3 灰度与回滚流程变更是有风险的新版本可能引入 bug。所以要有灰度机制新版本先跑一小部分流量观察没问题再全量。出问题了要能快速回滚到旧版本。灰度可以按流量比例灰度也可以按用户维度灰度。回滚要保证 State 的兼容性新版本产生的 State 旧版本要能读否则回滚之后流程会挂。6.4 压测与容量规划上线前要做压测搞清楚系统的容量上限。压测的时候要模拟真实流量包括正常流程、异常流程、并发场景。容量规划要考虑峰值流量。比如平时 QPS 是 10大促的时候可能到 100系统要能扛住 100 的峰值。如果扛不住要么限流要么扩容。限流是在入口处限制请求量超过阈值的请求直接拒绝或者排队。扩容是增加实例数提高处理能力。两者要配合使用限流保底扩容提上限。6.5 文档与交接流程编排的代码往往比较复杂没有文档的话接手的人很难看懂。文档要包括流程的整体结构图、每个节点的职责、State 的字段说明、条件边的判断逻辑、异常处理策略。文档要和代码同步更新。我见过太多项目文档还是半年前的版本和代码完全对不上。建议把文档放在代码仓库里改代码的时候一起改文档Code Review 的时候一起 Review。7. 一些个人体会Spring AI Alibaba Graph 这套东西上手不难用好不容易。我最大的体会是编排的本质是管理复杂度。ReAct 把复杂度交给了模型Workflow 把复杂度交给了开发者。前者灵活但不可控后者可控但不灵活。企业场景下可控性比灵活性重要得多。另一个体会是不要为了编排而编排。有些流程其实很简单两三个步骤用 ReAct 就够了硬要套 Workflow 反而增加复杂度。判断标准是如果流程的步骤和分支是固定的、可预测的用 Workflow如果流程是探索性的、步骤不固定的用 ReAct。两者也可以混合使用Workflow 里嵌套 ReAct Agent兼顾确定性和灵活性。最后一个体会是可观测性要一开始就做。不要等到出了问题才想起来加日志。每个节点、每条边、每次 State 变化都应该有记录。这些记录在排查问题的时候能救命。我现在的习惯是写节点的时候顺手把埋点加上不然后面补起来很痛苦。这个内容后续还可以这样扩展把 Checkpoint 存储换成 Redis 或者数据库支持分布式部署把节点执行改成异步提高吞吐量接入更完善的 Tracing 体系做全链路追踪。这些都是在生产环境里会遇到的真实需求有机会再展开聊。

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

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

免费获取报价 →
↑