记得我第一次接触MaaFramework,是在被一款手游的日常任务折磨到快崩溃的时候。那套操作说简单也简单:登录、领奖、做任务、下线,但每天重复三四十分钟,纯粹是体力活。我前后试过按键精灵、PyAutoGUI、甚至自己拿OpenCV写过模板匹配脚本,结果要么遇到分辨率一变就全部失效,要么为了处理各种弹窗和异常状态,代码越写越像一个"状态机灾难现场"。后来在技术社区里看到有人在聊MaaFramework这个自动化工具,说是"以图像识别为核心、用JSON编排流程",我抱着试一试的心态入了坑,结果一发不可收拾。这篇文章就是我从零开始用MaaFramework搭自动化工具的完整记录,适合任何手头有重复性GUI操作想固化下来的朋友参考,不管是游戏日常、App回归测试,还是桌面软件的定时操作,思路都是通用的。
1. MaaFramework 解决的核心问题:为什么普通脚本一上复杂场景就翻车
在讲MaaFramework之前,我想先聊清楚一个很多人没意识到的问题:自动化脚本翻车,通常不是"不会写",而是"没有反馈"。
1.1 简单脚本和成熟框架的本质区别
用PyAutoGUI或按键精灵写自动化,本质上是"盲人操作":你告诉鼠标从A点移动到B点,点击,等两秒,再点击。如果软件界面稍微变一下、弹窗提前或延迟出现、网络卡了半秒,脚本就完全不知道当前处于什么状态,只会机械地继续往下走——最终结果要么是点错位置,要么是卡在某个界面上空转。
MaaFramework的思路完全不同。它的核心循环是这样的:截图 → 识别当前画面状态 → 根据状态决定下一步动作 → 执行动作 → 再次截图确认。这四个步骤会把整个操作过程变成一个有反馈、可判断的闭环,每一步都在"看着屏幕做决定",而不是蒙着眼执行预定坐标。
这也是为什么MaaFramework在游戏日常、App测试这类界面状态多、元素变化频繁的场景里特别好用。传统方案里你需要维护一套复杂的坐标表,界面上多一根公告栏整个脚本就废了;而在MaaFramework里,你只需要保证"要识别的那一小块画面"稳定即可,周围怎么变化都不影响。
1.2 和主流自动化方案的横向对比
我简单整理了一下自己在选型时对比过的方案,大家可以根据场景选:
| 方案 | 核心思路 | 优势 | 致命短板 |
|---|---|---|---|
| 按键精灵 / AutoHotkey | 坐标 + 模拟键鼠 | 上手极快,轻量 | 无状态反馈,分辨率一变就废 |
| PyAutoGUI + OpenCV | 截图 + 模板匹配 | 灵活,可控性强 | 所有轮子都要自己造,状态管理很痛苦 |
| Selenium / Playwright | 接管浏览器DOM | 稳定,元素定位准 | 只能Web端,桌面和游戏完全碰不了 |
| Appium / UI Automator | 读取原生控件树 | 定位稳定 | 依赖系统层面控件信息,游戏/自绘界面无效 |
| MaaFramework | 图像识别 + 流程编排 | 跨平台、多语言、内置识别算法 | 初期需要理解"节点"和"流程"思维 |
实际体验下来,MaaFramework最打动我的是它把"识别"和"编排"这两件最麻烦的事做成了开箱即用的能力。它内置了模板匹配、特征匹配、OCR、颜色识别等好几种图像识别算法,你在JSON里写"用模板A找屏幕上这个位置,找到了就点击,找不到就走另一条分支"就行,所有底层图像处理全由框架搞定。
注意,MaaFramework不是万能的。需要读取软件内部数据、绕过验证或破解加密逻辑的场景,它做不了,也不该用这类工具去做。它擅长的是"模拟人眼看屏幕、用手点鼠标"这一层的自动化。
2. 环境搭建:跑通第一个最小 Demo 之前别急着写任务
很多新手一上来就写复杂任务,结果环境没通,遇到报错都不知道是框架问题还是自己配置的问题。我的建议是先老老实实跑一个最小Demo,再逐步往上搭。
2.1 下载和目录结构
去MaaFramework的GitHub Releases页面下载对应系统的发行包。Windows版本解压后你会看到这样几个关键目录:
bin/:框架核心二进制,包括MaaCore的运行时docs/:官方文档,建议把pipeline相关的文档先过一遍resource/:默认的资源和模板目录Python/:官方Python绑定库
实际使用中,resource/目录的结构值得多说两句。MaaFramework的项目通常把自定义的识别模板和pipeline配置放在这个目录里,你可以根据自己的项目建子目录来区分不同任务模块。整个框架的目录设计思路是"二进制和资源分离",升级框架版本时直接替换bin/,自己的业务配置不受影响。
2.2 从官方Demo验证链路
解压之后先别写自己的代码,把官方Demo跑起来验证整条链路。最简单的做法是用Python绑定连上一个窗口,让框架截一张图回来,确认截图和识别通路是通的。
在我的环境里,跑最小Demo的步骤大致是:
- 安装Python绑定:进入解压后的目录,把对应平台的
MaaFramework库pip安装进去,或者直接把Python/目录加入PYTHONPATH。 - 准备一个目标应用窗口:可以是浏览器窗口、游戏窗口或任何有固定界面的程序。
- 写一个最小脚本:连接该窗口、初始化资源目录、触发一个最简单的空任务、读取日志确认执行成功。
from MaaFramework.maa import Maa, AdbController # 连接Android模拟器的例子 controller = AdbController( adb_path="adb路径", address="127.0.0.1:5555", screenshot_method="Screencap", touch_method="AdbTap", ) maa = Maa(controller, resource_dir="resource/项目名")如果你是在PC端做桌面软件自动化,连接方式稍有不同,但核心逻辑一致:框架需要能截图到目标画面,并且能注入鼠标或键盘操作。只要这两点通了,剩下的都是配置层面的问题。
2.3 我在环境阶段踩过的坑
环境阶段最容易踩的坑是截图黑屏或截图分辨率过高。尤其是夜神、MuMu这类Android模拟器,默认渲染分辨率可能是720p,但MaaFramework的识别算法对缩放过后的图像比较敏感。建议统一把模拟器分辨率设成一个固定值,比如1280×720,然后在框架配置里把缩放比例关掉。分辨率不统一的话,后面所有模板匹配的阈值调试都会变得玄学。
另外,Windows桌面端的自动化一定要以管理员身份运行,否则部分窗口的前台激活和鼠标注入会静默失败——日志里看着一切正常,就是画面不动,这种情况我排查了大半天才反应过来。
3. 三个核心概念吃透:节点、识别算法和动作
接下来是整个MaaFramework最核心的部分。理解这三个概念,你就理解了框架的整个设计哲学。
3.1 节点(Node):流程的基本单位
MaaFramework里一切流程都由"节点"组成。你可以把节点想象成流程图里的一个方框:每个节点完成一件小事,做完之后通过next字段告诉框架下一步去哪。
一个最简节点长这样:
{ "节点名": { "识别": "TemplateMatch", "模板": "签到处.png", "动作": "Click", "点击位置": [640, 400], "next": ["签到完成"] } }这个节点做的事情是:在屏幕里找签到处.png这张模板图片,找到了就在坐标(640,400)点一下,然后跳转到签到完成节点。整个流程就是这样一个一个节点串起来的链条。
实际项目中,节点名建议用语义清晰的名字,比如点击签到入口、确认弹窗、等待加载完成。别用task1、task2这种名字,一个完整的日常任务流程往往有几十个节点,命名混乱会让后续维护生不如死。
3.2 识别算法:框架的"眼睛"
MaaFramework内置了多种识别算法,每种算法的适用场景不同。我把常用的几种整理成了表格:
| 算法 | 原理 | 推荐场景 | 注意事项 |
|---|---|---|---|
| TemplateMatch | 模板图在原图中滑动匹配 | 图标、按钮、固定UI元素 | 对缩放和翻转敏感,匹配速度快 |
| FeatureMatch | 提取特征点进行匹配 | UI换了皮肤、光照变化 | 需要素材足够清晰有纹理 |
| OCR(文字识别) | 识别区域内文字 | 文字按钮、状态文案 | 中英文识别效果有差异,需测试 |
| ColorMatch | 统计区域内颜色分布 | 一片固定颜色的区域 | 抗干扰弱,背景复杂时慎用 |
实际选择时可以按这样的思路来:有固定图标的用TemplateMatch;需要判断文字内容的用OCR;同一界面存在多种主题皮肤、图标会变的,考虑FeatureMatch或多模板组合。
3.3 动作:不只是点击和滑动
点击和滑动是自动化的常态,但MaaFramework的动作类型比这丰富得多。除了Click、Swipe之外,还有等待、按键、惯性滑动等。我自己最常用的其实是Wait——很多流程问题不在识别不到,而在动作之后界面有动画过渡,立刻做下一个识别容易截到中间帧。
举个例子,点完"领取"按钮之后,界面通常会弹出一个获得奖励的动画,持续一两秒。这时候如果立刻去识别"奖励弹窗关闭按钮",大概率失败。正确做法是在点击之后加一个0.8秒到1.2秒的等待节点,等画面稳定了再继续。
3.4 流程控制:节点怎么"串"起来
一个节点的next字段支持多种写法,最简单的就是填一个节点名。但MaaFramework还支持条件跳转:根据识别成功与否,走不同的分支。
{ "识别签到状态": { "识别": "TemplateMatch", "模板": "已签到.png", "next": { "success": ["今日签到已完成"], "failed": ["点击签到入口"] } } }这段配置的意思是:如果识别到了"已签到"的图标,说明今天已经签到过了,流程直接结束;如果没有识别到,才去执行签到操作。这是MaaFramework最有价值的能力——流程会根据屏幕真实状态自我调整,而不是死板地走固定路线。
理解了节点、识别算法、动作和流程控制,你就理解了MaaFramework的全部核心哲学。接下来我用一个完整案例串一遍。
4. 动手写一个签到任务:从截图到 pipeline 全流程
纸上得来终觉浅,我拿一个"每日签到"的典型场景完整走一遍。假设目标App每天有签到得奖励的功能,我们做一个自动打开App、点击签到入口、完成签到、关闭App的小工具。
4.1 第一步:拆解操作流程和状态
写自动化之前,先别碰代码,把人工操作的过程拆成状态步骤。一次签到流程是这样的:
- 打开App,进入首页(可能需要等待加载)
- 首页右下角有"签到"图标,点击
- 弹出签到面板,点击"签到"按钮
- 出现"签到成功"文字提示
- 关闭面板,退出App
对应到MaaFramework,就是五个节点。每个节点要思考的问题是:这一步靠什么判断完成状态?等待加载靠"首页出现某个特征图标"来判断;签到面板是否弹出靠"签到按钮的模板图"来判断;签到是否成功靠"成功两个字的OCR"来判断。
4.2 准备模板素材
这是我强烈建议认真对待的环节。模板图直接决定识别成功率,有几点经验:
- 截图时保证图像清晰,不要有阴影遮挡
- 模板尺寸不要太大,只截取要识别的那个元素本身,周围留少量边距即可
- 文件命名规范,例如中文场景用
sign_entry.png、sign_btn.png、signed_done.png - 模板图不要从网上随便找,必须用同一设备同一分辨率下的实际截图裁剪
截图的工具不限,Windows下用Win+Shift+S区域截图,模拟器直接用自带截图即可。关键是截完检查一下尺寸——有些截图工具会引入缩放,模板图和实际截图里的大小不一致会导致匹配率暴跌。
4.3 编写pipeline配置JSON
模板准备齐全后,开始写JSON。下面是一个简化但逻辑完整的配置:
{ "打开App并等待首页": { "动作": "Click", "点击位置": [100, 100], "next": ["识别首页签到入口"] }, "识别首页签到入口": { "识别": "TemplateMatch", "模板": "sign_entry.png", "动作": "Click", "点击偏移": [0, 0], "next": { "success": ["识别签到按钮"], "failed": ["识别首页签到入口"] } }, "识别签到按钮": { "识别": "TemplateMatch", "模板": "sign_btn.png", "动作": "Click", "next": { "success": ["识别签到成功提示"], "failed": ["识别签到按钮"] } }, "识别签到成功提示": { "识别": "OCR", "文字": ["签到成功"], "next": { "success": ["关闭App"], "failed": ["识别签到成功提示"] } }, "关闭App": { "动作": "Key", "按键": "Home", "next": [] } }你发现没有,识别首页签到入口这个节点在识别失败时会跳回自己,形成循环重试。这样做的好处是:如果App加载慢,首页还没出来,节点会一直重试直到成功,不需要手动设置等待时间。用"识别重试"代替"固定等待",是MaaFramework配置提升稳定性的关键手法。
4.4 用Python调用跑起来
JSON配置完成后,Python这边的代码反而非常简单:
from MaaFramework.maa import Maa, AdbController controller = AdbController( adb_path="adb路径", address="127.0.0.1:5555", ) maa = Maa(controller, resource_dir="resource/daily_sign") maa.start_task("打开App并等待首页")跑起来后,框架会在终端输出每一步日志:识别到了什么、点击了什么、跳转到哪里。第一次跑的时候盯着日志看,确认每个步骤是否符合预期。
4.5 我第一次跑这个任务的翻车现场
这套流程我第一次跑的时候,在"识别签到成功提示"这个节点上翻车了。现象是:签到按钮已经点完了,弹窗也出现了"签到成功"字样,但节点一直识别失败,循环重试了十几次,最后把弹窗给点没了。
排查过程是这样的:我先把日志级别调到最详细,让框架把每次识别时的截图都保存下来。拉出截图一看,签到成功的文字是"签到成功(+100金币)",而我的OCR节点只写了"签到成功"四个字,按道理应该能匹配上。问题出在弹窗出现时界面上有动画光效,文字区域有半透明光斑盖住了字形边缘,OCR直接把"成"识别成了"戚"。
解决方式有两种:一是把OCR识别区域缩小到文字中间不被光效影响的位置;二是退一步用TemplateMatch识别"签到成功"那句话旁边的对勾图标。我最后用了第二种,一次通过。
这个案例说明:识别稳定性的关键往往不是算法多高级,而是你怎么界定"识别区域"。一个干净、稳定、不受特效干扰的判断区域,比任何参数调优都有效。
5. 图像识别不稳的排错思路:阈值、区域和日志三板斧
在使用MaaFramework的过程中,90%的调试时间都花在"识别不到"或"错误识别"上。把典型的排错经验整理出来,希望能帮你少走弯路。
5.1 识别不到的三类原因
我把识别失败的原因归纳为三类,排查时按顺序来:
第一类是模板与场景不匹配。模板截图的分辨率、颜色、背景和实际识别有出入。比如同一个按钮在浅色模式下是白底,在深色模式下是黑底,模板只有白底版本,深色模式下识别率会暴跌。这类问题靠换多套模板或用FeatureMatch解决。
第二类是阈值设置不合理。MaaFramework的TemplateMatch默认阈值是0.8,但不同场景最优值差异很大。图标简单、轮廓明显,阈值可以调到0.95以上防误触;如果界面按钮带了渐变、光效,0.8都可能识别不到。关于阈值有个原则:能区分目标和非目标的最小值,而不是越高越好。
第三类是识别区域太大。很多人用全屏找一个小图标,结果背景里相似的纹理干扰了匹配。正确做法是手动设置ROI(识别区域),告诉框架"只在屏幕右下角这块区域找"。
5.2 提升识别稳定性的几个手段
除了上面提到的缩小ROI和合理阈值,下面几个手段也很有效:
- 多模板组合:同一个节点可以配置多张模板图,用"或"的逻辑,任一模板匹配成功即为识别成功。适用于按钮有多种状态的场景。
- 多算法并联:在JSON中给同一个节点同时配置
TemplateMatch和OCR,哪个先命中用哪个。我在判断"活动入口"时经常这样用,入口图标有时是纯图标、有时带文字,两种算法互补。 - 前置等待:识别之前先等一下,让动画特效消停。
- 分阶段识别:与其做一次大范围识别,不如把流程拆细,每一步只判断一个简单的元素是否出现。
5.3 日志是排错第一生产力
MaaFramework的日志系统比很多自动化工具做得好,建议从一开始就养成看日志的习惯。尤其是打开详细日志后,框架会把每次识别命中的分数、耗时、截图都存下来。这些截图是你判断"框架眼中的屏幕"的直接证据——很多问题你看自己的屏幕觉得一切正常,但框架看到的画面可能因为缩放、黑边、窗口遮挡而完全不同。
我的习惯是:任务出问题后,第一件事去翻日志目录里的截图,看框架当时截到的实际画面是什么样的。九成问题在看到截图的那一瞬间就明白了。
5.4 顺带说一句加载与调度
还有一个容易被忽略的点:MaaFramework任务本身有加载和调度机制。多个节点并行处理时,框架内部会管理它们的执行顺序。在复杂任务里,建议一个节点只做一件事,不要试图在同一个节点里又识别又点击又判定又跳转。职责单一,Debug的时候才能快速定位是哪个环节出了问题。
6. 再深入一步:分支逻辑、异常兜底与多分辨率适配
基础任务能稳定运行之后,你就能体会到自动化工具的真正威力了。这一节分享一下让工具在更长的时间里、更复杂的场景中保持稳定的进阶技巧。
6.1 用分支处理复杂业务状态
真实的业务场景很少是"一条直线走到头"的,中间充斥着各种分支。比如签到任务,你不仅需要处理"未签到"和"已签到"两种状态,还可能要处理"签到面板弹出失败""今天活动还没开始""网络超时弹窗"等各种状态。
MaaFramework的节点跳转可以做成一张状态表——每个节点定义一个状态,next定义状态转移条件。把节点看成状态机里的状态,节点间的跳转就是状态转移,整张图就是完整的业务流程。一旦你用这种思维去组织任务,再复杂的业务都能拆得井井有条。
6.2 异常兜底:让任务永远有路可走
自动化脚本最怕什么?怕执行到一半失去控制。比如点击了签到按钮,结果弹出了个"网络异常"的弹窗。如果流程里没有兜底,当前节点会一直重试点击,永远点不出结果,任务卡死。
MaaFramework的做法是设计"兜底节点"。比如所有签到流程的根节点,在你识别到任何未知弹窗时,都能跳到一个"关闭弹窗"节点去统一处理。换句话说,每个分支节点在识别失败时都要有一条"逃逸路线",要么回到上一个状态,要么进入异常处理流程。这样即使出现完全没见过的界面,任务也不会卡死,而是会退回到可控状态重试。
结合Python层的异常处理,可以在任务启动后定期检查任务状态,超过一定重试次数就主动中止任务并告警。我第一次做无人值守长时间跑任务的时候,就是因为没做这层保护,某个异常弹窗导致任务循环了半宿,第二天起来才发现。
6.3 多分辨率和多机适配
如果只是自己一台机器用,分辨率问题可以无视。但如果你想写一个能分享出去的工具,就得考虑不同用户的分辨率差异。
MaaFramework提供了分辨率适配机制,简单说就是在启动任务时设置一个标准分辨率,框架会把截图统一缩放到这个基准上再做识别。这种思路和前端做响应式设计的逻辑很像,识别逻辑写一套,适配交给框架处理。
实际测试中,我拿1280×720的基准截图写的流程,在1920×1080的设备上跑的时候,大部分节点直接跑通,少数按钮位置有偏移的会识别失败。这类识别失败通常是因为截图缩放导致的模板畸变,解决方式是把模板图重新截取一遍,尽可能使用大尺寸高清晰的素材,或者宽容度更高的FeatureMatch。
6.4 多任务串联的编排思路
当一个工具逐步长大,它会从"一个签到任务"变成"每天自动签到+领邮件+刷体力+清日常"的全家桶。这时候合理的做法是:每个功能模块独立成一个子流程,用总入口节点串联。
{ "开始今日日常": { "识别": "OCR", "文字": ["欢迎回来"], "next": [ "执行签到", "领取邮件奖励", "清理日常任务", "结束流程" ] } }多个子任务逐个执行,失败的任务不阻塞其他任务。这里有一层更深的经验是:子任务之间尽量解耦,每个子任务都要能在任意时间点独立启动。如果一个签到任务假设"必须先打开App",另一个刷副本任务也假设"必须先打开App",那这两个子任务组合时就要做两次打开判断,逻辑就会混乱。正确的做法是每个子任务开头都自带一个"确认环境状态"的节点,确保自己处于可执行的前置状态再继续。
最后分享一点个人体会
从接触到深入使用MaaFramework,我最深的感受是:这个工具把"自动化"这件事的抽象层次拉高了一个台阶。以前写脚本,你关注的是坐标、延时、异常处理这些底层细节;用MaaFramework,你关注的是"这个业务有哪几个状态""每个状态之间怎么跳转""如何用图像特征表达一个状态"——这其实是在逼你用软件工程的思路去设计自动化流程。
如果你看完这篇文章准备上手,我给的建议是:先拿一个最小的任务走通全链路,再去完善细节。环境通了、基本流程能跑了,你才会有信心去处理那些复杂的分支和异常。另外,模板素材的规范真的值得花时间做好,一套命名清晰、裁剪规范的素材库,会在多任务扩展时省下大把调试时间。我现在的主力工具已经稳定运行了好几个月,每天只需要偶尔看一眼日志确认任务正常,那种把重复劳动交给机器、自己彻底解放出来的感觉,正是最初让我决定认真研究MaaFramework的全部理由。