Sub Flow(子流程)
像函数一样调用另一个已保存的流程。
这是把一堆流程变成可维护系统的节点。关于构建大型自动化的一切,都取决于把它用好。
用途
在共享的执行上下文上内联运行一个已保存流程,可选地传参,然后按子流程返回的状态分支。
何时用
- 一段步骤被不止一个流程使用。
- 一个流程长到一屏读不完。
- 你想单独测试一块逻辑。
- 一个例程需要按调用方不同而表现不同,通过参数实现。
何时不用
- 只用一次的两个节点。 间接的代价超过它节省的。
- 调用它自己。 递归在运行时经活调用栈被拒绝。
- 子流程需要写回很多值时——子流程通过状态沟通,不通过返回负载。
前提
一个可调用的已保存流程。子流程应把它需要的值声明为 Flow Input 参数。
如何运行
子流程运行到它自己的 Stop 或 Return。该节点声明的状态就是子流程的返回值,它决定父流程从哪个端口离开。
自引用与循环调用在运行时被活调用栈拒绝——schema 无法知道流程自己的 id,所以这是运行时守卫加 UI 排除。
设置
| 设置 | 类型 | 范围 | 备注 |
|---|---|---|---|
| Flow | 流程引用 | — | 必填。 要调用的已保存流程 |
| Flow name | string | ≤ 200 字符 | 仅显示缓存——调用绑定流程 id |
| Inputs | 列表 | — | 参数映射:子流程输入的 name,与 value 模板 |
传参
每条输入映射把子流程声明的一个 Flow Input 绑到一个在父流程作用域里求值的变量感知模板:
| 子流程输入 | Value(在父流程里求值) |
|---|---|
max_per_trip | {{max_per_trip}} |
slot_index | 5 |
target_name | {{current_target}} |
求值后的值作为子流程的局部变量播种——真正的函数传参。子流程对调用方一无所知,所以同一个子流程可从队列条目、计划,或三个不同父流程运行。
空或缺省的列表意味着无映射,这是合法的;子流程则用它自己的声明默认值。
重命名是安全的
调用绑定流程 id。重命名子流程永不弄断调用方——flowName 只是显示缓存。
端口
| 端口 | 走它的时机 | 必接 |
|---|---|---|
| Success | 子流程以 success 终止 | 是 |
| Failure | 子流程以 failure 终止 | 否 |
| Cancelled | 子流程以 cancelled 终止 | 否——永不被走 |
| Fatal | 无法运行子流程,或检测到递归 | 否——永不被走 |
基础示例
抽出一个可复用例程:
- Flow:Return to base
- Inputs:无
接 Success → 继续,Failure → 恢复。
实际示例——薄编排器
最干净的大流程形态,是一个顶层小流程,除了带参数调用一个 worker 什么都不做:
编排器声明操作员填写的 Flow Input,然后往下传。改策略就改 worker;改参数则什么都不用改。
高级示例——小函数库
把大型自动化分解成单一目的的子流程,每个两个结局:
| 子流程 | Inputs | Success 意味 | Failure 意味 |
|---|---|---|---|
Return To Base | — | 我们在基地 | 到不了那里 |
Open Target List | — | 列表已打开 | 打不开 |
Process One Target | target_index | 已处理 | 跳过或够不着 |
Check Capacity | — | 有空位 | 已满 |
父流程于是一眼可读:
每个子流程可独立测试,父流程读起来像一份流程描述。
最佳实践
- 给每个子流程恰好是调用方需要的结局——通常一个成功一个失败,各带消息。
- 在子流程上声明输入,在调用点绑定。 永不把调用方可能想改的值硬编码进子流程。
- 保持子流程只做一件事,一句话能命名。
- 接进父流程前单独测试子流程。
- 留意多个分支对同一子流程的重复调用。 若五条路都调用"回到基地",考虑重构让调用只发生一次。
- 把子流程命名为动作——
Return To Base、Process One Target。 - 记住
maxNodeExecutions也数子流程节点。 父流程调用 20 节点子流程 50 次就执行 1000+ 节点。
性能笔记
- 子流程在同一会话里内联运行——没有进程边界、没有并行。总时长是父流程加所有子流程。
- 在循环里调用子流程按轮次成倍放大它的全部成本。检测密集的 子流程尤甚。
- Sub Flow 自身开销可忽略;成本全在子流程主体。
- 因为一切都在一个会话里,慢子流程阻塞整个运行——分解是为可维护性,不是速度。
内存笔记
- 子流程共享运行的执行上下文。绑定的输入作为子流程局部变量播种。
- 子流程执行的每个节点都记入运行的
nodeResults,所以长循环里的深调用树让上下文稳步增长。 run作用域的 Runtime Memory 属于运行,所以子流程见到与父流程相同的 run 记忆。
常见错误
| 错误 | 后果 | 修复 |
|---|---|---|
| 指望并行 | 子流程内联、依次运行 | 用多实例实现并发 |
| 子流程没有失败结局 | 父流程无法察觉问题 | 给它带消息的 failure Stop |
| 子流程里硬编码值 | 不可复用 | 声明输入、在调用点绑定 |
| 调用一个会回调的流程 | 运行时被拒 | 打破环 |
忽视 maxNodeExecutions | 中途死 | 调高护栏 |
| 把两个节点抽成子流程 | 间接多于价值 | 保持内联 |
故障排查
父流程总走 Success,尽管子流程明显失败。
子流程的终止节点声明了 success。查它到达了哪个 Stop/Return。
子流程看不到我的参数。 值绑到了子流程未声明为 Flow Input 的名字上。名字必须精确匹配。
节点走 Fatal。 无法运行该流程,或检测到递归。
运行死于 MAX_NODE_EXECUTIONS_EXCEEDED。
子流程节点算数。把子流程大小乘以调用次数。
重命名子流程没弄坏任何东西——对吗? 对。调用绑定流程 id。
FAQ
子流程与父流程并行运行吗? 不——在同一会话里内联、依次运行。
子流程能改父流程的变量吗? 它把绑定输入收为自己的局部变量。结果通过终止状态或共享记忆作用域传回。
流程能调用自己吗? 不能——递归在运行时被拒。
调用能嵌多深?
实际受 maxNodeExecutions 与运行时调用栈守卫限制。
Sub Flow 的 Failure 端口必接吗? 只有 Success 必接,但强烈建议接 Failure。