从业务需求到工作流

判断业务是否需要工作流

先问业务是否需要回答以下问题:

  • 每个业务实例目前处于哪个阶段,接下来会发生什么?
  • 顺序和条件路径是否需要独立于某个函数被审阅?
  • 操作人员是否需要根据持久化记录诊断、重试或补偿?
  • 后台可靠执行、版本历史、事件去重或流程超时是否影响正确性?

如果答案大多是否,普通类型化代码通常更简单。使用队列、写日志、调用多个函数或包含条件分支,本身都不是采用工作流的理由。

识别触发业务事件

先定义“什么事情发生时,产生一次新的业务处理”:

业务触发事件同一事件的稳定身份示例
订单履约一版订单支付成功order-paid:<orderId>:<paymentId>
库存补货一次库存变更已提交stock-changed:<productId>:<revision>
报价评估一版报价已提交quotation-submitted:<id>:<revision>

稳定身份用于识别同一事件的重试。新的库存变更或新一版报价应有新的身份;网络重试不应创造新身份。

拆分流程阶段和原子业务动作

把需要被运营人员单独识别、观察或恢复的阶段做成节点,把阶段内部的查询和写入留在 Service 中。

过度拆分:

查询一行 → 读取字段 → 相减 → 判断大于零 → 拼接对象 → 插入一行

合理拆分:

计算补货缺口 → 判断是否需要补货 → 创建补货记录 / 记录库存充足

拆分不足则是把整个多阶段流程塞进一个巨大 Run 模块,导致界面只能看到一个节点,失去路径观测价值。

区分每次输入和管理员参数

输入随每次业务事件变化,例如商品 ID、库存变更版本或报价 ID。管理员参数是部署后可调整的少量业务配置,例如目标库存或风险阈值。

不要把凭证放入任何一类。API 密钥应由应用配置或专用秘密管理能力提供。输入和参数在运行开始时会形成快照,后续修改不应改变已经开始的运行。

详细声明方式见工作流定义 DSL。

设计副作用、幂等、重试与补偿

工作流的 eventKey 防止同一业务事件产生多个运行,但不能替代节点内部的业务幂等。例如,创建补货记录时还应使用库存变更 ID 建立唯一约束或先检查已有记录。

让 Agent 在方案中逐项说明:

  1. 哪些节点只读,哪些节点会写库、发消息或调用外部系统;
  2. 同一节点再次执行时,如何识别已经完成的副作用;
  3. 暂时性错误是否可以复用同一业务身份重试;
  4. 已经完成一部分后失败,是否需要显式补偿;
  5. 哪些动作不可逆,必须在运行前额外确认。

工作流不会自动回滚外部副作用,也不应通过删除运行历史假装它没有发生。

确认可用的节点能力

默认插件提供 Run、Condition 和 Terminate。它们能表达执行、布尔分支和提前结束。

人工审批、等待外部回调、循环和子流程需要专门的 Instruction。设计包含这些语义时,先让 Agent 检查已安装插件是否在检查器、构建器和运行时注册了对应能力。不要让一个 Run 模块长时间占用 Worker 来模拟等待。

设计示例:报价评估

需求:报价提交后计算风险;高于管理员阈值时标记为人工处理,否则自动通过;运营人员需要查看判断过程。

设计结果:

  • 事件:一版报价提交成功;
  • 输入:quotationId 和用于本次判断的 amount;
  • 参数:approvalLimit,由管理员设置;
  • 节点:计算风险 → 判断是否超限 → 记录人工处理或自动通过;
  • 普通代码:加载报价、计算规则、持久化决定;
  • 幂等:以报价 ID 和修订号唯一记录一次决定;
  • 恢复:计算失败可在修复后重试;外部通知已发送后需避免重复或显式补偿。

如果“人工处理”还要求人在数天后点击同意并从原处恢复,默认节点能力不足,需要审批或等待类 Instruction。

审核设计结果

确认 Agent 的方案回答了本页列出的选型、事件身份、节点边界、输入与参数、副作用以及节点能力问题。节点的具体约束见节点概览,数据库事务和 Run 模块边界见Run 节点。如果 Agent 只展示流程图而没有解释这些决定,方案尚不足以进入实现。