--- name: maafw-pipeline description: 编写、修改与审查 MaaFramework(MaaFW)Pipeline——从零新建任务与子流程(大任务逐级拆分为嵌套复用的子流程、需求翻译成状态、资源文件与节点命名、识别/动作节点编写与 ROI 圈定、next 链分支/循环/[JumpBack]/[Anchor] 中断与失败路径设计、可达性自查)与存量重构(摸底可复用节点、改名前全库搜引用、坐标卫生与反模式审查)。Use when authoring, editing, or reviewing any pipeline JSON in a MaaFW project — adding tasks, sub-flows, or nodes, breaking large tasks into nested reusable sub-flows, choosing TemplateMatch/OCR/ColorMatch or action types, sizing ROIs, designing next chains with branches, loops, interrupts, and timeout fallbacks, naming or renaming pipeline files, folders, nodes, or template images, or refactoring existing pipelines for coordinate hygiene and anti-patterns. --- # MaaFW Pipeline 开发 编写全流程分两条动线,动手前先分轨: - **新建轨**:从零新建流程(任务或子流程)——需求 → 拆分与状态清单(含探索取证)→ 文件结构与命名 → 素材准备 → 节点编写 → 流程组合 → 自查与交接。任务可大可小:大任务逐级拆成子流程,嵌套组合,每一层走同样的步骤。 - **修改轨**:在既有项目上改动、重构、审查——摸底 → 搜引用 → 修改 → 自查(见下),不按新建顺序硬套。 字段精确语义(字段名、参数、默认值、版本支持)**不在本文复制**,一律路由 [MaaLLMWiki](https://github.com/Windsland52/MaaLLMWiki) 的 3.1-任务流水线协议:版本锁定、引用回源、不可达时标记"未验证"(已安装 `maallmwiki` skill 时由其承载完整路由流程)。执行模型是下文一切推理的基础,在此给出最小版: ## 执行模型(最小心智模型) 节点是三段式:**识别 → 动作 → next**。引擎在节点间如此运转: 1. 任务由入口节点名启动(`post_task`):引擎初始化为"当前节点 = 入口节点、轮询列表 = [入口节点]"——入口作为唯一候选被识别(通常 DirectHit 恒命中);若识别超时,用**它自己的** `timeout`,未命中也走**它自己的** on_error。 2. 当前节点动作完成后,对它的 **next 列表按顺序轮询识别**:每轮依次尝试各候选;本轮全未命中,等 `rate_limit` 后再截图重试(本轮候选全为 DirectHit 时跳过截图,零画面开销)。 3. **第一个命中的候选立即胜出**(后续候选不再检测),**进入该节点**、执行其动作——动作成功则轮询它的 next 列表(回到第 2 步);动作失败走它的 on_error。 4. 轮询超过**当前节点**的 `timeout`(缺省 20s)仍无命中 → 走**当前节点**的 on_error——想调整某节点**被识别**的等待时长或超时去向,改的是**它上一节点**的 `timeout` / `on_error`,不是它自己的。 5. **异常状态**: - **进入(三种)**:动作失败;轮询超时;被轮询列表中没有任何存在且启用的节点(不等 `timeout`、立即未命中——不存在的引用过不了加载校验,走到这一步的多为 `enabled:false` 节点或未设置的锚点)。 - **进入后**:on_error 列表接替 next 列表被同样轮询——on_error 为空时 next 立即为空、循环直接终止,任务以失败收场。 - **解除**:只由识别命中解除;命中后若动作失败,立即重新进入——因此结果上走出异常 = 命中且动作成功。 - **异常期间收紧**:再次未命中(on_error 轮询超时,或 on_error 列表没有任何存在且启用的节点——如恢复节点名拼错,不等超时、立即失败)**不进 on_error**、任务直接失败;执行链走空时若仍处于异常状态,已登记的 `[JumpBack]` 回跳不生效,任务以失败终止。 6. **终止与成败**: - 循环在 next 为空或外部 stop / StopTask 时终止;**任务成败 = 终止时是否处于异常状态**(正常态走空即成功)。 - **唯一例外 `[JumpBack]`**(仅正常态):子链走空不终止,**重新轮询父节点的 next 列表**(从头按序识别;父节点不经识别恢复为当前节点,超时与 on_error 归属随之回到它,动作不重跑)。例:`A.next=["B","[JumpBack]C"]`、`C.next=["D"]`、`D.next=[]`——C 命中执行后走 D,D 走空,接着轮询的就是 `A.next`。 **on_error 归属**:看当前处于哪个节点——候选被识别不算进入节点,**命中后才算进入**;进入后的执行动作与轮询 next 列表都发生在本节点内,失败就走本节点的 on_error:动作失败走动作所在节点的 on_error,轮询 next 列表超时走 next 列表所属节点的 on_error。 ## 硬护栏(全程生效) 1. **探索优先**:要写的每个状态必须有真实观测证据(截图、OCR 文本、色值、box 等);凭文档、节点名或记忆推断的状态一律视为 guessed,**guessed 状态不得落笔**。细则: - **按转移推进**:探索推进到哪个界面(以当前设备画面为准),就观测该界面的识别标志与点击目标;点击后实际观测到下一界面的标志出现(即 next 里的"出口"候选),这条转移即可落笔。弹窗处理节点同样只在见过该弹窗后写。写到哪、观测到哪,不必整个流程探索完再动笔;整段贯通与可重入不在写作门槛内,轮到自查与端到端验收去跑。 - **点击最小化副作用**:非必要的提交/领取类按钮不点;能靠关键帧确认的状态不主动触发。 - **不可复现的状态**(一次性奖励、首通引导、不可逆操作后的界面)无法主动到达,用**留存的关键帧**代替——工具暂存的重要变化帧同样算观测证据,写前逐帧核对;连关键帧都没有的仍按缺口处理。 - **复用已有节点**时,其入口前提同样是必须观测的状态。 - **无法探索**(无设备、无授权、会触发禁止的副作用)就停下报告缺口,不要硬写计划。 - **实时性场景不适用**:抢购、QTE 等毫秒级反应无法靠"观察→决策"的探索模式,也超出轮询模型的反应速度——直接说明做不到,不硬写。 2. **坐标卫生**:点击目标由识别结果推导。`target` 缺省 `true`(继承本节点识别 box),绝大多数节点不需要写它;要偏移用 `target_offset` 并注明依据;确需另指目标优先 string 引用(前置节点名 / `[Anchor]锚点名`,引用结果为空即动作失败);固定坐标数组视同硬编码,仅特殊场景(如负坐标从边缘计算)使用并注明理由——**严禁 `DirectHit` + 硬编码 target 坐标**。 3. **不编造字段**:字段名、参数、版本支持拿不准就路由,宁可少写用默认值;`$` 开头 root 字段不解析、`.` 开头文件不读取。pipeline 文件按 **jsonc** 解析——行内与整行 `//` 注释直接可用;需要结构化注释时用 `desc` / `_desc`,占位提示用 `code` / `_code`(如 `template_code` 表示该参数在 interface.json/Custom 代码里统一配置),`doc` / `_doc` 亦同(尽量不用)——均为**非协议字段**(3.1 协议零记载),引擎对未知键静默忽略,作结构化注释属约定用法,纯说明优先 jsonc `//` 注释。 4. **基准分辨率中立**:运行基准从项目 controller 配置动态获取(缺省 720 短边,兼容横竖屏);量 ROI 的截图必须走框架截图链路,不用外部图片。 5. **循环必须有终止论证**:凡执行会回到已执行节点候选列表的写法(`[JumpBack]` 回跳、next 回指成环),终止论证三选一并在注释说明。细则: - **限次**:`max_hit` / `timeout` + `on_error` 上报; - **识别门控**:候选带真实识别、动作以消除该识别为目标,动作可能失效时仍配兜底限次; - **挂机意图**:显式声明的无限产出循环,退出靠外部停止或条件节点,环内每轮有真实进展,重置计数必须显式; - **`timeout` 只管"目标界面一直没出现"**:管不了"一直命中、停不下来"——只要每轮都有候选命中,超时永远不会触发; - **DirectHit 禁入循环**:恒命中且无识别门控,禁止作为 `[JumpBack]` 自回环或循环体候选——全 DirectHit 候选列表还会跳过截图,连画面变化都观测不到。 ## 新建轨 步骤 1–5 按**段**滚动推进:拆出的每个子流程各自走 1→5,探索一段、写一段,不必整个任务探索完再动笔;全部段落完成并自查后进第 6 步。 滚动推进必须伴随**进度档案**(跨会话的 working memory,外置成文件,放资源包外不被框架加载的位置):记录需求与拆分结论(子流程清单)、每段实现状态(**计划中 / 已编写 / 已测试**)、每个已观测状态的证据帧编号、当前断点(设备停在哪个界面)。**关键帧与状态绑定归档**——它同时是 observed 证据、复测识别的素材(节点测试模式:截图 + hits/box 期望)、模板裁剪的来源。 ### 1. 需求 → 拆分与状态清单(含探索取证) - **先拆再翻(滚动拆分)**:需求可大可小,大任务先拆成子任务/子流程——每个子流程有明确的入口/出口边界(约定 `Start`/`End` 后缀),可跨任务复用的收进资源包内 `pipeline/` 顶层共享文件(`general.json`),复用边界由文件归属与跨模块引用表达,不靠节点名前缀(见 [references/project-structure.md](references/project-structure.md));子流程内部再同法拆分,嵌套任意层。拆分边界:一段可独立观测与独立验证的界面段/业务段。拆分不必一次完成——先定子流程边界的大框架,屏内的状态与弹窗随推进滚动补进清单。 - **屏内动线(先同屏、后出口)**:进入一个界面后,先写并即测该界面的异常处理节点——同屏内容不离开画面,写完就能测(屏内广度);出口最后写——写完测完人已在下一界面,顺接继续(屏间深度)。反序会先离开本屏,未适配的弹窗要整条流程重跑才能回来补测。 - 把每个(子)流程翻译成**状态**而非动作清单:起始态、每个中间态(含可能的弹窗)、成功态、失败态。每个状态回答两个问题: - **用什么识别**:固定图 → TemplateMatch;文本 → OCR(`expected` 用完整稳定文本);色块 → ColorMatch;无视觉变化的流程节点 → DirectHit;目标尺寸 / 缩放会变(模板匹配失配)→ FeatureMatch;模板 / 文本 / 颜色都不可行的复杂目标 → NeuralNetworkClassify / NeuralNetworkDetect(自训 ONNX 模型);多条件同屏 → And / Or;都表达不了 → Custom(交接 `maafw-custom`)。完整名单与精确参数路由 3.1-任务流水线协议核对。 - **证据是 observed 还是 guessed**:哪段要写,哪段的状态必须全部 observed(硬护栏 1);尚未探索的段留到写到时再探索。 ### 2. 文件结构与命名 先定落位再写内容: - 新功能按模块拆文件/目录;文件组织两派选一固定:**模块前缀式**(`AutoCollect/AutoCollectRoute1.json`——文件名带模块前缀、模块多时配目录,排序聚簇)或**功能平铺式**(`awards.json` 平铺根目录,功能数适中时更轻),均有大项目实证;**新建默认模块前缀式 + PascalCase 文件名**(与节点 / 模板图大小写一套,目录从第一天起、无增长天花板),存量平铺项目照旧、增量新模块可开目录;细则见 [references/naming.md](references/naming.md)。 - pipeline JSON 有 **v1 扁平**与 **v2 嵌套**(`recognition` / `action` 收敛为 `{type, param}`,v4.4.0+)两种等价写法,字段语义通用;**新建默认 v2**(识别/动作/流程参数命名空间分开,错位字段在结构上不可发生),存量跟随既有主流风格、不必整仓转换,项目内统一(依据见 [references/flow-patterns.md](references/flow-patterns.md) 示例注)。 - 节点名跨文件全局解析、**大小写敏感**;同资源包内重名即错误(跨包重名是渠道覆盖机制,见 references/naming.md);不以 `$` 开头,方括号(`[` `]`)是 next / on_error 引用位的属性语法(`[` 开头条目按属性前缀解析),节点名约定不使用;入口/收尾节点用 `Start`/`End` 后缀(约定);节点名语义按词位公式组织(`任务_界面_对象_角色_变体`,下划线分隔槽位,见 references/naming.md);复用边界不用节点名前缀表达。 - image 按模块分目录,素材路径即引用键,改名必须同步改 pipeline 引用与 custom 代码内的读图调用(自定义识别 / 动作可自行加载图片)。 - 仓库布局、多资源包见 [references/project-structure.md](references/project-structure.md);资源目录下文件夹名禁 `_` 开头(Android 打包事故)。 ### 3. 素材准备 从状态清单推导素材缺口,补齐再写节点: - 模板从**基准分辨率的框架截图**(无损原图缩放后)裁剪,裁剪源与识别输入保持同一图像空间。外部图片 / 游戏内截屏不一律禁用,判定标准是**像素空间一致**:模拟器自带截图在分辨率与控制器基准一致(或长宽比一致、可无损缩放到基准)、且无遮挡污染(通知栏 / 工具水印 / 有损压缩)时与框架截图同源同空间,可用;相机翻拍、比例不符、带污染的不可用。无论来源,一律过裁后自匹配复核兜底。 - 素材获取按工作流选工具,硬要求不随工具变(像素空间一致 + 裁后自匹配复核,见上条):**agent 自主工作流优先实时观测底座**(如 [maafw-live](https://github.com/Windsland52/maafw-live))——帧流常驻、裁剪与取色全程程序化,人不必盯屏:模板从留存的原始分辨率帧裁(`crop`,识别空间一致)、探色(`color`)、无模板时候选区域编号选点(`annotate`)、不可复现状态从关键帧库留存帧裁剪(含事后**离线** `crop --from-kf`,不需要设备;产物带 L2 出处)。该工具的动线与判据读它自带的 skill(随包发布,本集合不重复):`npx skills add https://github.com/Windsland52/maafw-live --skill maafw-live --global`。无观测底座时也可自写脚本走框架截图链路截图后程序化裁剪;人工在场调试可用官方工具链(VSCode maa-support 插件、MFA 工具箱、MPE 等)交互式截取。 - **裁后自匹配复核**:新模板在其来源帧的来源位置应命中(位置正确优先于得分,纯色模板得分是噪声)——裁完即验,不等流程联调才发现素材问题。 - 裁剪区域选稳定特征,避开动态数字/动效——动态内容使同一状态的每次捕获像素不同,模板命中不稳。 ### 4. 节点编写 每态一个节点,先识别后动作: - 识别按状态清单的选型落字段;`roi` 收紧到目标实际出现区域(提速且防误命中),起点可取识别 box 外扩约 20px——过紧易失配、过松易误中,不稳时扫描几档。 - 动作按意图选型,完整名单与精确参数路由 3.1-任务流水线协议核对:点击 `Click` / `LongPress`,滑动 `Swipe` / `MultiSwipe` / `Scroll`,按键 `ClickKey` / `LongPressKey` / `KeyDown` / `KeyUp`,输入 `InputText`,应用启停 `StartApp` / `StopApp`,流程 `StopTask` / `DoNothing`,系统能力 `Command` / `Shell` / `Screencap`,扩展 `Custom`。点击落点遵守坐标卫生(硬护栏 2)。 - 要向用户展示节点消息(运行日志 / 轻提示 / 系统通知 / 弹窗)配节点 `focus`:按消息类型给模板(`{name}` 等占位替换、支持 `$` i18n),`display` 选展示渠道;`trace` 上报遥测需项目 interface.json 配置了 `telemetry` 才生效——字段定义路由 3.1,模板与渠道机制路由 3.3 协议「节点通知处理」节。 - 参数起点:`threshold` 以实测为准(得分裕量规则仅 TemplateMatch 套用;OCR / ColorMatch 语义不同;FeatureMatch 无 threshold,严格度由 `count` 最少特征点数与 `ratio` KNN 距离比控制)——起点取目标位实测得分减约 0.1(换场景会掉),核对仍高于该屏误配最高分;两侧贴得太近是模板 / roi 问题,调阈值救不了;能用 `*_wait_freezes` 等待稳定的就不加 delay;拿不准的参数少写、用默认值(硬护栏 3)。 - 写入纪律:增量插入而非整文件改写——JSONC 注释承载"为什么这么写",整写会毁掉它;缩进与字段顺序跟项目既有约定(常有格式化插件);坐标一律用框架缩放后坐标系的实测值。 ### 5. 流程组合 - **主干**:最短可成功路径排成线性 next 链。细则: - **等待目标出现**:界面切换用"等待目标出现"节点表达,不用长 delay 凑; - **参数保持缺省**:`pre_delay` / `post_delay` / 节点级 `timeout`(next 轮询超时)尽量保持缺省; - **wait_freezes 的 `timeout` 例外**:只在必须等画面稳定时用 `pre_wait_freezes` / `post_wait_freezes`,其自身 `timeout` 不属于"保持缺省"之列——默认 20s,等待区域有持续动画时等满才继续,常配 1~2s 小值兜底。 - **分支**:next 列表顺序即优先级,第一个命中执行。排序两原则: - **遮蔽约束优先**:某候选出现时另一候选也必然命中,前者必须排在前;排反了后者永远轮不到(功能错乱),若被遮蔽的恰是出口、遮蔽者的动作又消不掉自己的触发条件,就构成"一直命中"的死循环,`timeout` 救不了(硬护栏 5 的预防端在此); - **命中频次降序**:其余候选按频次降序排,每轮按序识别,高频在前平均每轮少做识别,全局资源消耗最优; - **表达手段**:互斥分支用顺序表达;按位置/得分排序用 `order_by`,组合条件用 `And`/`Or`。 - **循环与中断**。细则: - **循环**:循环体 + 出口节点放同一列表,`max_hit` 限次防死磕(终止论证三选一见硬护栏 5); - **弹窗 / 加载处理器**:随时可能弹的界面(公告、奖励、断线)做独立处理节点,以 `[JumpBack]` 挂在关键等待点(回跳只在正常态兑现,异常态下链走空即任务失败); - **跨分支汇合用 `anchor`**——引用未设置的锚点会被静默跳过。 - **等待与重试分层**。细则: - **等待用参数表达,不写结构**:`next: ["目标"]` 本身已在等待,节奏由 `timeout` / `*_wait_freezes` / `rate_limit` 调,自指 next 对"等下一屏出现"零增益; - **重试需证据**:动作重试只在遥测显示真实失效率后添加; - **自指重试优先**:线性流程用单节点自指 + 自身 `max_hit` 即可(预算含首次进入、跨引用点全程累计),不建克隆节点; - **克隆的门槛与纪律**:确需独立重试预算或策略升级(重试前等待、长按替单击)才克隆;`Retry` / `Fallback` 后缀与源同前缀保持可搜;desc 注明克隆自哪个节点、与源刻意差异的字段(差异面)——差异面之外的字段与源不一致即漂移 bug,改源节点时差异面之外的字段有同步义务; - **共享处理器例外**:被多处引用的节点在 `max_hit` 累计语义下不能用小值,防线是严格识别门控;`max_hit` 用尽的候选**静默退场**(识别时跳过、不报错),列表照常轮询——再无可命中候选时空转至超时、走宿主节点的 on_error,耗尽本身不是失败信号; - **审查基线**:无证据的自指 / 自回环在审查时视为待删项。 - **失败路径**:每个 next 列表问一遍"全没命中怎么办"。细则: - **兜底结构**:`timeout`(`-1` 无限慎用,v5.5+)+ `on_error` 兜底(回退/重试/上报),重试配 `max_hit` 限次; - **on_error 链自身必须能稳定命中**——异常状态里再失败没有二次兜底,任务直接终止; - **on_error 只做异常恢复**:不当正常流程的控制流(比如拿来实现 if-else 效果); - **重试与 `max_hit` 是安全带不是修复**:反复失败先修根因(识别不稳/逻辑缺陷); - **禁无识别门控的盲目重试**:"没反应就原地点第二次"(第二次点击可能已落到下一界面的其他元素上);重新识别到按钮仍在才再点的有限重试是合法恢复手段; - **动作失败走收敛**:动作失败(框架报错)不以自指或自回环重试——持续的动作失败是缺陷不是抖动,让 on_error 收敛(上报 / 回退 / 重入子流程入口重跑识别),瞬态恢复须显式有界。 模式示例 JSON 与稳定性法则(识别→操作→再识别、少用硬延迟)见 [references/flow-patterns.md](references/flow-patterns.md)。 ### 6. 自查与交接 - 可达性:无孤立节点、无 anchor 断链、无未解析引用;完备性四问——主线能跑?弹窗能处理?加载能等过去?**不在目标场景时的行为已显式定义**(自动跳过还是失败上报,是项目的产品决策,不能默认漏掉)? - 有节点测试基建的项目(如带 `tests/` 目录与截图期望的)为新增识别节点补用例。 - 交接:单节点验证与端到端验收 → `maafw-debug`;task 入口与选项接线 → `maafw-interface`;JSON 表达不了的逻辑 → `maafw-custom`。 ## 修改轨(存量项目入口) 改动、重构、审查既有 pipeline 时走此轨: 1. **摸底**(规模随改动伸缩,走索引不通读)。细则: - **小修**(bug、调字段、ROI 微调):定位目标节点及其定义与直接上下游即可; - **结构性改动**(新增功能、重构、动复用面)才摸复用面——先看地图(目录树 + interface.json 任务清单)→ 节点名索引用脚本提取(逐文件列 root key,勿逐文件通读)→ 按能力关键词检索、命中文件才读全文(词位命名的语义名即检索索引,共享件先查 `general.json`); - **素材**:按 `image/` 目录结构按需检索; - **无论规模**:先明确改动边界(哪些节点动、哪些只引用)。 2. **搜引用**:改节点名或素材路径前全库搜索引用点——漏改的后果按引用位置分档,并非都是静默:next / on_error 漏改**加载期即失败**(非锚点引用的存在性校验,能抓住;interface 的 pipeline_override 注入的悬空引用同样被拦);roi / target 引用、custom 的 run_task、template 素材路径漏改**运行期失败**(目标解析空即动作失败 / 任务不存在 / 模板懒加载缺图);`[Anchor]` 条目(校验跳过锚点名)与 option 的 pipeline_override(override 落空)才是**静默失联**。引用点清单: - **节点引用**:next / on_error 的三种写法(裸字符串、`[JumpBack]X` 前缀、`{name:"X"}` 对象)、roi / target 的 string 引用、`[Anchor]`、option 的 pipeline_override、custom 的 run_task / override_next; - **素材路径**:pipeline 的 template 与 custom 代码内的读图调用(自定义识别 / 动作可自行加载 `image/` 下的图); - **同前缀克隆**:改识别 / 动作参数前另搜 `Retry` / `Fallback` 后缀——克隆与源静默漂移,同样不报错; - **跨语言引用**:custom / agent 代码内写节点名(语言不限——C++ / Python / Node.js / Go 等)按入口分两类——`run_task` / `run_recognition` / `run_action` 的 entry 按**原始字符串**查节点,传 `"[Anchor]X"` 只会去找字面叫这个名字的节点并失败,锚点跳转先 `get_anchor` 取实名再传入;`override_next` 的 next 列表与 pipeline 引用位同解析,`[JumpBack]` / `[Anchor]` 属性语法可用。锚点本身可经 context API 读写(`set_anchor` / `get_anchor`,C API / agent RPC / 各绑定均暴露);写实名 + 改名前全库搜引用仍是基线防线。 3. **修改**:按需对齐新建轨 2–5 的同等标准写新内容。 4. **自查**:可达性检查 + 反模式清单逐项核对([references/anti-patterns.md](references/anti-patterns.md)——坐标卫生、timeout/-1 滥用、循环限次、版本兼容)。 ## 深入阅读(按需) - [references/naming.md](references/naming.md) — 文件/目录/节点/图片命名规范全文(协议层 + 两派实证) - [references/project-structure.md](references/project-structure.md) — 仓库布局、多资源包、任务入口存放、agent 组织 - [references/flow-patterns.md](references/flow-patterns.md) — 流程设计展开、模式示例 JSON、稳定性法则 - [references/anti-patterns.md](references/anti-patterns.md) — 坐标卫生细则、反模式清单、审查清单