变量、输入与记忆
Microbe Studio 给你四个存值的地方,它们只在一件事上不同:值活多久、谁看得见。 选错正是"我的计数器被重置了"和"我的两个实例在抢同一个值"的头号成因。
本页把四个都画在地图上。
目的
对任何一条数据,用本页决定:
- 它该是变量还是记忆条目?
- 它要活过运行吗?活过应用重启吗?别的实例要看见吗?
- 操作员要在运行前手动输入它吗?
四个家,一览
| 家 | 存活期 | 可见者 | 典型用途 |
|---|---|---|---|
流程变量(scope: flow) | 一次运行 | 仅该运行 | 计数器、循环状态、OCR 结果 |
实例变量(scope: global) | 永久,跨重启 | 一个实例上的所有流程 | "这个实例登录的是哪个账号" |
| Runtime Memory | 你选的按条目 TTL | 取决于记忆作用域(下文) | "15 分钟内别再碰这个目标" |
| Flow Input 参数 | 一次运行,启动时提供 | 该运行 | "这趟处理多少个" |
流程变量
流程变量在流程的 Variables 对话框声明:路径(vars.gem_farmed_trip)、类型、可选默认值。
类型
可声明的类型:
string、number、boolean、point、rect、object、array、null
除 null 外每种类型都有带尾缀 ? 的可空变体——number?、string? 等。可空变量允许持有 null;不可空的不允许,违反声明的值会以 TYPE_MISMATCH 拒绝启动运行。
如何初始化
每次运行开始时,严格按此顺序:
- 所有已声明变量置
null。 - 有
defaultValue的应用之。 - 应用流程的
initialValues。 - 每个值对照声明类型检查。
- 任何值违反声明,运行不启动。
这个顺序意味着默认值总胜过 null,初始值总胜过默认值。
读与写
在几乎所有文本框里用两种等价语法引用变量:
{{gem_farmed_trip}}
${gem_farmed_trip}
用 Variable 节点写变量,它持有一列赋值行。每行有目标和操作:
| 操作 | 效果 |
|---|---|
set | 求值表达式并存结果 |
clear | 把变量重置为声明的默认值 |
delete | 从上下文移除变量 |
log | 把当前值写进运行日志(不改动) |
set 行还可带 expire after(ttlSeconds,至多一年)。到期后值回到声明默认值,而非消失。
产品里最有用的双节点惯用法:
- Variable 节点:
vars.items_done = {{items_done}} + 1 - If 节点:
{{items_done}} >= {{max_per_trip}}
这一对给你"做 N 次、N 按次提供",不需要循环节点,而且经得起分支——循环计数器可经不起。
实例变量
在声明上设 scope: global——UI 里标为 Instance——彻底改变存储:
- 值跨运行、跨应用重启持久。
- 由该实例上的每个流程共享。
- 实例之间隔离:A 与 B 各有一份。
用于设备的事实,不是运行的事实。"登录了哪个账号"、"这实例做过首次设置没"、"这实例在哪个服务器"。
因为跨重启存活,一个过期的实例变量会在多天后悄悄影响运行。给它们一条刻意的重置路径——设置流程里的一行 clear——别假设每次都是干净开局。
Flow Input 参数
给声明打上 input 标记,它就成为 Flow Input 参数:在流程内仍是普通变量,但会在运行前被拿出来供输入。
- 操作员在 Dashboard 的 Run Condition 表单填写。
- 标记 required 则留空时阻止运行。
- Sub Flow 节点能把父表达式绑定到子输入——真正的传参。
这就是把流程变成可复用函数的东西:
子声明它需要什么;父在调用点提供。子对调用者一无所知,因此同一个子能从队列条目、计划或三个不同的父运行。
Runtime Memory
变量回答"此次运行此刻什么为真"。Runtime Memory 回答另一个问题:"这东西我处理过了吗?多久之前?"
它是带按条目 TTL 的多条目键值仓库,由 Runtime Memory 节点写、Memory Match 节点查。
四种记忆作用域
值得精确记住的部分:
| 作用域 | 活过运行结束 | 活过应用重启 | 跨实例共享 |
|---|---|---|---|
run | 否 | 否 | 否 |
session | 是 | 否——一次应用启动 | 否——每实例私有 |
persist | 是 | 是 | 否——每实例私有 |
shared | 是 | 是 | 是——唯一跨实例作用域 |
run活在运行自己的执行上下文上,随运行消失。session意为"本次应 用启动"。新启动从空开始。persist由 SQLite 支撑,重启安全。shared是跨实例协调的唯一刻意开关。其余作用域按实例做命名空间,实例 A 永远读不到、盖不掉实例 B 的记忆。
shared 是真共享十个实例往 shared 写同一个键就会互相覆盖。只在跨实例协调正是目的时用它——例如全局"该目标已被认领"清单。凡按设备的,用 persist。
冷却守卫模式
Runtime Memory 的经典用法:别再处理刚处理过的东西。
Memory Match 用可引用受测条目 {{item.<字段>}} 的表达式对每条存活条目比较候选者:
(abs({{current_x}} - {{item.x_coordinate}}) <= 1) && (abs({{current_y}} - {{item.y_coordinate}}) <= 1)
读作"有没有已记住的条目在我现在位置一格之内?"——容差匹配,不是严格相等,这正是真实屏幕数据需要的。
条目带 900 秒 TTL,守卫 15 分钟后自动过期。什么都不用清理。
记忆操作
| 操作 | 做什么 | 需要 key |
|---|---|---|
remember | 追加条目(key 空则自动生成) | 否 |
forget | 按 key 移除一条 | 是 |
clear | 清空整个记忆 | 否 |
count | 把存活条目数写入变量 | 否 |
怎么选:决策表
| 你想…… | 用 |
|---|---|
| 在一次运行里数迭代 | 流程变量 |
| 运行前传入一个数 | Flow Input 参数 |
| 记住这台模拟器用哪个账号 | 实例变量 |
| 15 分钟内避免重访某目标 | Runtime Memory,persist,TTL |
| 仅本次运行内避免重访 | Runtime Memory,run |
| 让所有实例共避一张目标单 | Runtime Memory,shared |
最佳实践
- 声明你用的每个变量。 未声明的引用在启动时失败而不是运行中——那是你想要的失败方式。
- 计数器默认
0,不是null。null + 1不是数。 - OCR 写的一律
number?。 失败的读取不产生值,不可空声明会拒绝它。 - TTL 优于手动清理。 自动过期的条目不会泄漏。
- 输入按它驱动的决策命名——
max_per_trip,不是n。 - 在自然边界重置行程计数器,不是循环顶部。参考自动化在流程回基地时重置——"一趟"真正结束的点。
性能笔记
- 变量读取与表达式求值在内存中,几乎免费。别为性能回避变量。
persist与shared的操作走 SQLite。快,但是 I/O——别把remember放进每秒跑几百次的紧循环。count扫描存活条目。记忆很大时,自己维护一个计数变量更好。
内存笔记
- 每个会话携带全套流程变量的独立副本。一个流程跑 10 个实例即 10 套独立变量。
run作用域的 Runtime Memory 挂在执行上下文上,运行终止即释放。session作用域存于共享数据库、按启动令牌命名空间;应用启动时回收上一次启动的行,不会无限增长。- 记忆按绝对时间过期,写入 900 秒 TTL 的条目重启后照样过期——存的是到期时刻,不是内存里的倒计时。
常见错误
| 错误 | 症状 | 修复 |
|---|---|---|
| 计数器声明无默认值 | 算术啥也不产出 | 默认 0 |
| 不可空变量绑 OCR 结果 | 运行被 TYPE_MISMATCH 拒绝 | 声明 number? / string? |
拿 shared 存按设备状态 | 实例互相覆盖 | 用 persist |
指望 session 活过重启 | 重启后空 | 用 persist |
| 循环顶部重置行程计数 | 永远到不了上限 | 在行程边界重置 |
| 假设实例变量开局干净 | 好几天前的旧值 | 在设置流程里显式清 |
故障排查
我的计数器永远是 1。
Variable 节点在重置而不是递增。确认表达式是 {{x}} + 1 而不是 1。
运行拒绝启动,报 TYPE_MISMATCH。
某个默认或初始值不符合声明类型。最常见:该 可空的 OCR 目标被声明成不可空。
Memory Match 从不命中。
查两个节点的作用域——run 作用域的 remember 对读 persist 的 match 不可见。再确认表达式里的字段名与你存的字段一致。
两个实例互相跳过对方的工作。
它们共享着 shared 作用域的记忆。换成 persist 按设备隔离。
FAQ
{{name}} 与 ${name} 有区别吗?
没有。都接受、行为一致。选一种保持统一。
子流程能改父流程的变量吗? 子把绑定输入收为自己的局部变量。结果通过子的终止状态传回,或用共享记忆作用域。
变量 TTL 到期后变成什么?
回到声明的默认值——不会变 null,除非默认值就是。
实例变量在机器之间同步吗? 不。它只属于该安装上的那个实例。
相关页面
- 执行模型——这些值所居的上下文
- 流程解剖——声明存放在哪
- Variable 节点——写它们的节点
- Runtime Memory 节点——记忆写入者
- Memory Match 节点——记忆读取者
- Sub Flow 节点——传参