Maestro AI 断言源码解析:3 条命令把截图缺陷检测与文本提取接入你的测试流
【免费下载链接】MaestroPainless E2E Automation for Mobile and Web项目地址: https://gitcode.com/GitHub_Trending/ma/Maestro
本文从源码层面拆解 Maestro AI 的截图断言能力:assertNoDefectsWithAI、assertWithAI、extractTextWithAI 三条命令如何截图并调用云端模型,以及如何在 5 分钟内接入你的 flow。
功能全景:Maestro AI 的 4 个能力点
Maestro 的 AI 能力集中在maestro-ai模块,核心是一个只有三个方法的接口,外加一个离线评测工具。四条能力对应关系如下:
| 能力 | 对应源码入口 | 一句话说明 |
|---|---|---|
| 通用 UI 缺陷检测 | maestro-ai/src/main/java/maestro/ai/IAPredictionEngine.kt 中findDefects | 把整屏截图发给云端模型,返回category+reasoning的缺陷列表 |
| 自然语言断言 | 同接口performAssertion | 把一句人类语言断言转成"是否成立",不成立时 flow 失败并附上模型理由 |
| 截图文本提取 | 同接口extractText | 用自然语言 query 从截图里抠出文本,写入 flow 变量供后续assert使用 |
| 截图集批量评测 | maestro-ai/src/main/java/maestro/ai/DemoApp.kt | 离线跑一批*_good.png/*_bad.png,统计模型误报率与漏报率 |
flow 层面的接线则发生在 maestro-orchestra-models/src/main/java/maestro/orchestra/MaestroCommand.kt,它把 YAML 里的三条 AI 命令映射到执行管线。
最快上手路径:不接设备,先跑离线评测
想不接真机就体验,先编译出自带的评测程序maestro-ai-demo。它读本地截图文件,对每张图调用云端缺陷检测并打印 PASS/FAIL:
export MAESTRO_CLOUD_API_KEY=your-cloud-key export MAESTRO_CLI_AI_KEY=sk-... ./gradlew :maestro-ai:installDist ./maestro-ai/build/install/maestro-ai-demo/bin/maestro-ai-demo foo_1_bad.png截图文件名必须遵循{应用名}_{序号}_{good|bad}.png约定(bad表示"这张图应当被检出缺陷"),否则 DemoApp 会直接报参数错误。两个环境变量都要设:MAESTRO_CLI_AI_KEY用于本地模型客户端,MAESTRO_CLOUD_API_KEY用于 Maestro Cloud 推理端点。跑通后再接设备即可在正式 flow 里使用。
功能深挖
先看三个能力共同依赖的接口签名,这是整个 AI 模块的最小契约:
interface AIPredictionEngine { suspend fun findDefects(screen: ByteArray): List<Defect> suspend fun performAssertion(screen: ByteArray, assertion: String): Defect? suspend fun extractText(screen: ByteArray, query: String): String }通用 UI 缺陷检测(assertNoDefectsWithAI)
痛点场景:视觉回归靠人肉对比截图,布局错位、元素截断、文案重叠这类问题在自动化断言里很难表达——view tree 看起来"没问题",但画面上已经错了。
源码实现路径:YAML 中的assertNoDefectsWithAI被解析为AssertNoDefectsWithAICommand(定义在 maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt),执行入口是 maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt 的assertNoDefectsWithAICommand:先maestro.takeScreenshot(imageData, compressed = false)抓无压缩 PNG,再交给CloudAIPredictionEngine.findDefects(maestro-ai/src/main/java/maestro/ai/CloudPredictionAIEngine.kt),最终由 maestro-ai/src/main/java/maestro/ai/cloud/ApiClient.kt POST 到$baseUrl/v2/find-defects,baseUrl默认是https://api.copilot.mobile.dev,可用MAESTRO_CLOUD_API_URL覆盖。响应体反序列化为Defect(category, reasoning)列表,只要列表非空,flow 就以"Found N possible defects"失败,且每个缺陷都带模型的推理文本。
最小可运行示例(flow YAML 里的一行):
- assertNoDefectsWithAI自然语言断言(assertWithAI)
痛点场景:"订单卡片上应当显示配送地址"这种断言,你写不出稳定的 selector,正则和文本匹配又太脆。
源码实现路径:AssertWithAICommand多带一个assertion: String字段(同样在 Commands.kt)。Orchestra 的assertWithAICommand复用同一条截图链路,但调用performAssertion——它在 maestro-ai/src/main/java/maestro/ai/Prediction.kt 里走的是同一个/v2/find-defects端点,只是把断言文本放进FindDefectsRequest.assertion字段,然后取response.defects.firstOrNull():返回null即断言通过,返回缺陷则失败信息直接引用defect.reasoning。可以把它理解为"带题目判卷的缺陷检测",题目就是你这句自然语言。
最小可运行示例:
- assertWithAI: "订单卡片上显示了配送地址"截图文本提取(extractTextWithAI)
痛点场景:WebView、canvas、游戏引擎渲染的文本不在 view tree 里,assert的文本选择器拿不到,而你又不想引入第三方 OCR。
源码实现路径:ExtractTextWithAICommand携带query和outputVariable两个字段。执行函数extractTextWithAICommand截图后调用AIPredictionEngine.extractText,落到 ApiClient 的extractTextWithAi,POST$baseUrl/v2/extract-text,请求体是ExtractTextWithAiRequest(query, screen)。关键的一步在最后:jsEngine.putEnv(command.outputVariable, text)把结果注入 flow 的 JS 变量环境,后续步骤就能用{{变量名}}引用提取结果——这是它与两条断言命令的本质区别,它是"产出"而不是"判定"。
最小可运行示例:
- extractTextWithAI: query: "提取当前页面显示的用户余额" outputVariable: balance一键跑通:用截图集评估缺陷模型(maestro-ai-demo)
痛点场景:接入 AI 断言之前,你想先知道模型对你家 App 的截图到底误报多少、漏报多少,避免把不稳定的判定塞进 CI。
源码实现路径:DemoApp 按文件名解析出appName、序号和good/bad标签,构造TestCase(shouldPass = status == "good");对每张图读取字节后走Prediction.findDefects(无 prompt 文件时)或Performance performAssertion(同名.txtprompt 存在时),verify()按"bad 图没检出 = false-negative,good 图报缺陷 = false-positive"打印 FAIL,并把category: reasoning逐条列出。--parallel可并发跑,注释里直接提醒"May get rate limited";--show-prompts/--show-raw-response帮你调试模型行为。
最小可运行示例:
maestro-ai-demo --model gpt-4o --show-raw-response test-ai-fixtures/uber_*_bad.png关键源码地图
- maestro-ai/src/main/java/maestro/ai/IAPredictionEngine.kt — 能力契约:
findDefects/performAssertion/extractText - maestro-ai/src/main/java/maestro/ai/CloudPredictionAIEngine.kt — 唯一实现,持
apiKey转发到Prediction - maestro-ai/src/main/java/maestro/ai/Prediction.kt — 三个静态入口,负责 DTO 到领域结果的裁剪
- maestro-ai/src/main/java/maestro/ai/cloud/ApiClient.kt — Ktor HTTP 客户端、超时配置、
Defect等请求/响应 DTO - maestro-ai/src/main/java/maestro/ai/AI.kt — 直连 OpenAI/Claude 的抽象基类,定义
MAESTRO_CLI_AI_KEY等环境变量名 - maestro-ai/src/main/java/maestro/ai/DemoApp.kt — 离线批量评测 CLI
- maestro-orchestra-models/src/main/java/maestro/orchestra/MaestroCommand.kt 与 maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt — YAML 命令的数据模型
- maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt — 运行期:截图、调引擎、失败判定、把提取文本注入变量环境
- maestro-ai/README.md — 构建与使用文档
组合实战:一个 flow 里串起断言与提取
下面的 flow 演示了"先泛检、再定向断言、最后提取变量并做正则校验"的组合方式:
appId: com.example.app --- - launchApp - waitForAnimationToEnd - assertNoDefectsWithAI - assertWithAI: "页面展示了登录按钮,且没有广告弹窗遮挡" - extractTextWithAI: query: "提取当前页面显示的用户余额" outputVariable: balance - assert: text: "{{balance}}" matchesRegex: "\\d+\\.\\d{2}"前两条命令守住"画面没有明显破损"和"关键状态成立",后两条命令把截图里的动态数字变成可校验的变量——整条链路不需要任何额外依赖,前提是MAESTRO_CLOUD_API_KEY已导出。
调优与避坑
- 别混淆两套 Key:flow 里的三条 AI 命令只读
MAESTRO_CLOUD_API_KEY(Orchestra 报错信息也会提示这一点);MAESTRO_CLI_AI_KEY只服务于 maestro-ai 模块内直连 OpenAI/Claude 的路径,离线评测程序则两个都要。 - 超时是写死的:ApiClient 固定为连接 10 秒、socket/request 各 60 秒,源码里没有重试逻辑。网络慢时单个 AI 步骤可能吃掉半分钟,一个 flow 里别堆太多 AI 步骤,CI 超时时间要相应放大。
- 截图是全量无压缩 PNG:
takeScreenshot(..., compressed = false)意味着高分辨率设备上每次断言都上传原图,流量和耗时都成正比,没必要时用默认分辨率即可。 - 失败可降级:三条命令的
optional字段默认为true,但默认行为仍是失败即停;想在灰度期只告警不阻断,可显式配optional: true,或把 AI 步骤包进retry:块兜底网络抖动。 - 评测注意命名与限流:DemoApp 只接受
{app}_{n}_{good|bad}.png三段命名,--parallel并发跑同一 key 时容易被限流,批量评测建议串行或降低并发。
结语
Maestro AI 这三条命令全部依赖 Maestro Cloud 的/v2/find-defects与/v2/extract-text端点,没有纯本地的模型路径,MAESTRO_CLOUD_API_URL只改地址不改协议;断言的可靠性取决于云端模型对截图的判定,建议先用离线评测程序在自己的截图集上验证。更多构建与用法细节见 maestro-ai/README.md。
【免费下载链接】MaestroPainless E2E Automation for Mobile and Web项目地址: https://gitcode.com/GitHub_Trending/ma/Maestro
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考