跳到主要内容

执行模型

流程在运行时做的一切都来自一个小循环:取一个节点,运行它,看它返回什么,沿一条连线去下一个节点。一旦你能预测这个循环,你就能预测你的流程。

本页按运行时的真实实现来讲解这个循环。若流程的行为让你意外,答案几乎总在本页。

目的

理解执行模型让你能够:

  • 不运行流程就预测下一个执行的节点。
  • 区分"我的流程失败了"与"我的流程走了另一条分支"——日志里长得像,含义却相反。
  • 知道一次运行为何停止,以及是三道护栏中的哪一道停了它。
  • 有意识地连接错误路径,而不是靠试错。

何时需要本页

在构建任何带分支、循环或错误处理的东西之前读它。以下情形你需要它:

  • 某节点"成功"了,流程却去了意外的地方。
  • 运行立刻结束,没有明显错误。
  • 你在犹豫用重试、用失败连线,还是用 If 来处理一个问题。

搭一条纯 Click 加 Wait 的直线不需要本页。

一次一个节点

一个运行中的流程是一个会话(session)。每个会话同一时刻只执行一个节点,持有一个"当前节点"指针。

这比听上去重要:

  • 没有后台节点。某节点运行时,流程里没有任何别的东西在跑。
  • 慢节点阻塞整个会话。一个 30 秒超时的 Find Image 会把该会话卡住最多 30 秒。
  • 并行来自更多会话(更多实例),不是把一个流程劈开。
真正的并行从哪来

想同时做两件事,就在两个实例上跑流程,或用 Flow Queue 在实例池上运行多个流程。单个会话内,执行严格串行。

循环

这是运行时的主循环,按真实执行顺序:

两个值得背下来的推论:

  1. fatalcancelled 永不沿连线走。 接上 Fatal 端口不会在正常运行中创造救援路径——运行时一见该状态即终止。想恢复的一切都用 failure
  2. 没有连线的 route 会结束运行。 节点返回 failure 而 Failure 端口空着,运行就以 failureTRANSITION_NOT_FOUND 终止。它不会悄悄继续。

Status 与 route——最重要的区别

每个节点返回两个独立的东西:

它描述什么谁读它
status执行健康度——节点本身跑成了吗?运行时,用于护栏与重试
route流程从哪个端口离开连线解析,用于选下一个节点

它们不是同一个值,且检测节点上二者刻意不同。

五种 status

Status含义可重试沿连线走
success节点正常完成
failure可恢复的失败
timeout期望条件未按时达成
cancelled运行在节点中途被停止——终止
fatal不可恢复的系统或配置错误——终止

为什么"未找到"不是失败

这是最常见的困惑来源,值得直说。

Find Image 搜模板、超时未命中时:

  • statussuccess——检测正常运行了,只是没找到图;
  • routefailure——于是流程从 Failure 端口离开。

实际效果:一次"未找到"永不计入连续失败护栏,也永不触发节点重试。 轮询稀有事件一千次的流程不会因此被杀。这是刻意的——"按钮还没出来"是正常结果,不是错误。

同样的模式适用于 Check Regionpassed / 未通过)和 Read Text

当作提问读,别当作裁决读

对检测节点,把端口读成一个问题的答案——"它在吗?"——而不是"这节点跑成了吗?"。两种情况下节点都跑成了。

连线解析

节点返回 route R 时,运行时查 (该节点, R) 出发的连线:

  1. 候选在编译期按 (fromNodeId, fromPort) 建好索引。
  2. priority 升序、再按连线 id 升序排序。
  3. 第一个无条件的候选者胜出。
  4. 带条件的候选者求值;true 选中,false 跳过。
  5. 条件报错或返回非布尔值,该候选被跳过、继续下一个。
  6. 无人选中 → "没有连线"——运行以 failure 终止。

连线解析是纯函数:它从不抓帧、发输入、改变量或写文件。它只做选择。

节点执行管线

"运行节点"内部,阶段永远按此顺序:

容易踩坑的点:

  • 参数里的 {{变量}} 解析不了时,节点在任何重试之前就返回 fatalPARAM_EXPRESSION_INVALID。坏表达式是配置缺陷,重试只会一模一样地失败。
  • 输出映射恰好运行一次,作用于最终尝试。重试三次的节点不会写三次变量。

护栏

三个上限保护你免于永不结束的流程。三者都是流程设置,由运行时亲自检查。

护栏检查时机超限效果
maxNodeExecutions每个节点前终止 fatalMAX_NODE_EXECUTIONS_EXCEEDED
maxRuntimeDurationMs每个节点前终止 fatalMAX_RUNTIME_DURATION_EXCEEDED
maxConsecutiveFailures每个节点后终止 fatalMAX_CONSECUTIVE_FAILURES_EXCEEDED

连续失败计数的行为:

  • failuretimeout 递增它。
  • success 清零
  • fatalcancelled 在计数被使用前就终止运行。

由于检测的"未找到"带 success 状态,它清零计数而不是递增。

长运行流程需要余量

maxNodeExecutions 数每个节点,包括 StartStop。一个 200 轮、12 节点的循环要执行约 2400 个节点。本文档的农场式流程把 maxNodeExecutions 设为 5000 正是此因。长流程死于 MAX_NODE_EXECUTIONS_EXCEEDED 时,去提高这个值,而不是砍短循环。

重试的 backoff 时间不计入单次尝试的超时,但计入 maxRuntimeDurationMs

一次运行如何结束

运行恰以下列方式之一终止:

原因最终状态
到达 Stop 节点按 Stop 的声明(success / failure / cancelled
到达 Return 节点按 Return 的声明
某节点返回 fatalfatal
某节点返回 cancelled,或用户停止运行cancelled
route 无匹配连线failureTRANSITION_NOT_FOUND
越过护栏fatal

"跑着跑着就没了"的流程不存在——每条路径要么抵达终止节点,要么以 TRANSITION_NOT_FOUND 终止。

逐步示例

看一个小的登录步骤:

按钮晚出现时的轨迹:

节点statusroute下一个
1StartsuccesssuccessFind Image
2Find ImagesuccessfailureWait 2s
3Wait 2ssuccesssuccessFind Image(第 2 个)
4Find Image(第 2 个)successsuccessClick
5ClicksuccesssuccessStop
6Stop终止 success

注意第 2 步虽然路由到 Failure,status 仍是 success。连续失败计数纹丝未动,没有任何重试参与。流程只是走了它的另一条分支。

最佳实践

  • 连上每个检测节点的 Failure 端口。 空着的 Failure 端口把"未找到"变成被终止的运行。
  • failure 做恢复,别用 fatal fatal 立刻终止且接不住。
  • 对"它出来了吗?"优先分支而非重试。 重试属于真正失败的操作;分支属于处在不同状态的屏幕。
  • 给长流程慷慨的 maxNodeExecutions 它是安全网,不是预算。
  • success 清零你的失败计数。 在危险步骤之间穿插成功步骤,自然使 maxConsecutiveFailures 不被引爆。

性能笔记

  • 会话一次跑一个节点,所以流程总时长是节点时长之和。给流程提速最快的办法,是缩短那些通常瞬间成功的节点的超时,而不是重排图。
  • 轮询节点的 timeoutMs 覆盖整个轮询循环——抓帧、裁剪、匹配、发事件。2 秒超时里放 1 秒轮询间隔,得到的约是两次尝试,不是两秒匹配。
  • 连线解析是纯函数且已建索引,分支本身近乎零成本。别为速度压平一张可读的图。

内存笔记

  • 每个会话持有自己的执行上下文:变量、nodeResultslastResult。同一流程跑在 10 个实例上就是 10 份独立上下文。
  • nodeResults 随每个执行过的节点增长——每个节点的结果都被记录以供后续引用(If 能查上游节点结果靠的正是它)。maxNodeExecutions 极高、迭代数千次的流程会在运行全程积累结果数据。
  • 超长时间的自动化,优先拆成被反复调用的 Sub Flow,或拆成多次计划运行,而不是一个执行数小时的会话。

常见错误

错误后果修复
Failure 端口空着运行以 TRANSITION_NOT_FOUND 结束连上,哪怕只连到一个 Stop
指望 Fatal 当兜底永不被走;运行终止可恢复的问题放在 failure 上处理
Find Image 加重试想"找得更狠"重试不触发——未找到带 success 状态提高 timeoutMs,或在 Failure 上分支
读日志时把"未找到"当错误把健康流程误读成坏的route,别只看 status
超长流程用默认 maxNodeExecutions中途死于 MAX_NODE_EXECUTIONS_EXCEEDED在流程设置里调高

故障排查

运行立刻结束,日志写着 TRANSITION_NOT_FOUND 某节点返回了没有连线的 route。找日志里最后的节点,看它走了哪个端口。

运行以 MAX_CONSECUTIVE_FAILURES_EXCEEDED 结束,节点却个个看着正常。 有东西在反复返回 failuretimeout,中间没有任何成功。找持续失败的操作节点(不是检测节点)。

带重试策略的节点从不重试。 要么它返回了 fatal(永不重试),要么返回了 success 状态加 failure 路由(检测未找到)——那不是可重试的结果。

流程走了预期之外的分支。 查节点的 route,不是 status。检测节点上两者设计上就不同。

FAQ

fatal 会回滚什么吗? 不会。fatal 终止运行。已发往设备的输入已经发生。

一个流程里两个节点能同时跑吗? 不能。一个会话一次执行一个节点。要并发就用多个实例。

节点重试 3 次,输出映射跑 3 次吗? 不——恰好一次,应用于最终尝试的结果。

cancelled 连线有被走过的时候吗? 没有。画布上可以存在 cancelled 端口以备未来兼容,但运行时在 cancelled 上直接终止,不走连线。

相关页面