Midscene.js:基于AI视觉的跨平台UI自动化测试与RPA实践

发布时间:2026/7/6 9:40:30
Midscene.js:基于AI视觉的跨平台UI自动化测试与RPA实践 1. 项目概述当AI“看见”你的屏幕最近在折腾自动化测试和RPA机器人流程自动化时我遇到了一个老生常谈的痛点UI结构一变脚本就崩。无论是基于CSS选择器的Playwright还是依赖Accessibility Tree的Appium都绕不开对页面底层结构的强依赖。改个类名、换个布局甚至只是图标按钮没有文本标签自动化流程就可能直接卡住。直到我发现了Midscene.js这个项目的理念让我眼前一亮——它彻底抛弃了传统的DOM或视图树解析转而让AI直接“看”屏幕截图然后用自然语言指挥它操作。这就像你给一个坐在电脑前的新手同事发指令“点击那个红色的登录按钮”、“在搜索框里输入‘最新款手机’并回车”他不需要懂HTML也不需要知道按钮的ID只要眼睛能看到手就能点到。Midscene.js的核心价值在于它将AI的多模态视觉理解能力变成了一个通用的、跨平台的UI操作员。你不再需要为Web、安卓、iOS、桌面应用甚至游戏界面编写和维护复杂的定位器。它的工作流程极其直观截取当前屏幕 - 将截图和你的自然语言指令如“点击登录”一起喂给视觉大模型 - 模型识别出目标元素的位置并返回坐标 - SDK驱动鼠标或触控点击该坐标。整个过程开发者只需要关心“做什么”而把“怎么做”和“在哪里”完全交给AI。这个思路解决了几个关键问题首先是维护成本UI怎么改只要人眼能认出来AI就能操作其次是覆盖范围Canvas渲染的图表、无语义的图标按钮、跨域iframe、乃至原生桌面应用凡是能截图的地方它都能介入最后是验证维度你可以让AI断言“这个按钮变成了灰色不可用状态”或“弹窗出现在了屏幕中央”这是基于视觉效果的验证比检查某个DOM属性是否存在要可靠得多。接下来我就结合实战带你从零开始把这个“AI操作员”用起来。2. 核心设计思路为什么是“纯视觉”驱动在深入代码之前有必要先理解Midscene.js选择“纯视觉”路线的深层考量。这决定了它的能力边界和最佳适用场景。2.1 传统自动化工具的“阿喀琉斯之踵”传统的UI自动化无论是Web端的Selenium/Playwright还是移动端的Appium其核心都是与应用程序的结构化描述进行交互。对于Web这是DOM文档对象模型对于移动应用这是视图树View Hierarchy或可访问性树。工具通过这些结构化的节点信息使用ID、XPath、CSS选择器等定位元素。这套方法的脆弱性体现在强耦合于实现细节一个按钮的定位可能依赖于#submit-btn .primary这样的选择器。前端重构时开发人员为了优化样式或组件结构完全可能修改类名或DOM结构导致选择器失效。对非语义元素“失明”一个纯图标按钮buttonsvg.../svg/button如果缺乏aria-label等可访问性属性在视图树中可能只是一个没有文本内容的节点传统工具很难稳定定位。完全由Canvas或WebGL渲染的复杂应用如一些设计工具、游戏其内部元素对DOM来说是完全不可见的。跨平台与跨上下文壁垒原生桌面应用、安卓/iOS原生控件、以及Web页面中的跨域iframe它们往往处于不同的进程或安全沙箱中传统工具很难获取其内部完整的结构信息或者需要极其复杂的配置才能打通。这些痛点导致了自动化脚本的“脆弱性”需要投入大量精力进行维护其投资回报率ROI常常令人沮丧。2.2 Midscene.js的“第一性原理”解法Midscene.js的思路回归到了最本质的人机交互方式视觉。用户操作图形界面时依赖的是屏幕上像素所呈现的信息而不是背后的代码结构。因此它提出了一个大胆的假设如果AI能像人一样理解屏幕内容那么它就能像人一样操作任何界面。其技术栈核心是“截图 多模态大模型MLLM”输入当前屏幕的截图或指定区域的截图 一条自然语言指令如“找到并点击那个写着‘确认支付’的蓝色按钮”。处理将图文对输入给一个具备强大视觉定位Grounding能力的多模态模型。这个模型经过海量UI截图和指令的对齐训练能够理解指令所指的视觉元素并精确地输出该元素在截图中的边界框Bounding Box坐标。输出与执行SDK获得坐标后将其换算为当前屏幕上的绝对坐标然后通过底层驱动如Playwright的鼠标API、ADB的tap命令、Windows的SendInput等执行点击、输入等操作。这个流程的优势是降维打击式的去耦合完全解除了与应用程序内部结构的耦合。只要UI的视觉表现不变无论底层代码如何重构自动化脚本都能继续工作。全平台通用理论上任何能输出图像截图并能接收模拟输入键鼠、触控的设备都可以被自动化。Midscene.js目前已经支持了浏览器、Android、iOS、鸿蒙、Windows/macOS/Linux桌面应用正是基于这一原理。验证更贴近用户感知你可以进行视觉断言例如aiAssert(错误提示框应该显示在屏幕顶部)。这测试的是用户实际看到的效果而非内部状态对于视觉回归测试尤其有价值。注意纯视觉驱动并非没有代价。它对多模态模型的推理速度和准确性有较高要求且执行速度通常比基于DOM的直接操作稍慢。但对于复杂、动态或跨平台的场景其带来的稳定性和开发效率提升往往是决定性的。3. 环境搭建与快速上手理论讲完我们动手实操。我将以最常见的Web浏览器自动化为例带你快速跑通第一个Midscene.js脚本。3.1 项目初始化与依赖安装首先创建一个新的Node.js项目。我更推荐使用pnpm因为Midscene.js的官方示例多基于此。mkdir midscene-demo cd midscene-demo pnpm init -y接下来安装核心依赖。你需要midsceneSDK本身以及一个浏览器自动化驱动。Midscene.js与Playwright集成得非常好我们选择它。pnpm add midscene playwright # 安装Playwright的浏览器内核Chromium, Firefox, WebKit pnpm exec playwright install实操心得虽然Midscene.js理论上可以对接任何能截图的驱动但Playwright是目前在Web端最成熟、功能最全面的选择。它提供了稳定的截图API和精准的输入模拟与Midscene的集成是“官配”。如果你主要针对Chrome也可以只安装Chromiumpnpm exec playwright install chromium。3.2 获取并配置AI模型API密钥Midscene.js本身不提供AI模型它是一个“调度器”需要你连接一个后端的多模态大模型服务。官方推荐并深度优化了若干模型例如DeepSeek-V3、GLM-4.6V、Qwen3.2-VL等。你需要选择一个并获取其API密钥。这里以DeepSeek-V3为例因其在视觉定位任务上表现优异且性价比高访问DeepSeek官网注册并登录。在控制台创建API Key。在项目根目录创建一个.env文件来存储密钥确保该文件在.gitignore中避免泄露。# .env DEEPSEEK_API_KEY你的实际API密钥3.3 编写第一个AI操作员脚本现在我们来写一个简单的脚本让AI帮我们打开GitHub并搜索“Midscene.js”。创建一个文件first-ai-op.jsimport { Midscene } from midscene; import { chromium } from playwright; import dotenv from dotenv; // 1. 加载环境变量 dotenv.config(); async function main() { // 2. 启动浏览器 const browser await chromium.launch({ headless: false }); // 设置为 true 则无头运行 const context await browser.newContext(); const page await context.newPage(); // 3. 创建Midscene实例连接到Playwright的page对象 const midscene new Midscene({ page, // 传入Playwright的page对象 apiKey: process.env.DEEPSEEK_API_KEY, // 你的模型API密钥 provider: deepseek, // 指定模型提供商 }); try { // 4. 导航到GitHub await page.goto(https://github.com); console.log(已打开GitHub首页); // 5. 让AI“看到”屏幕并点击搜索框 // aiAct 是核心方法截图 发送指令给模型 执行操作 await midscene.aiAct(点击页面顶部的搜索输入框); console.log(AI已点击搜索框); // 6. 人工或AI输入搜索词 // 这里我们先手动输入展示混合操作 await page.keyboard.type(Midscene.js); await page.keyboard.press(Enter); console.log(已输入关键词并搜索); // 等待一下结果加载 await page.waitForTimeout(2000); // 7. 让AI在搜索结果中点击第一个仓库链接 // 指令可以更精确 await midscene.aiAct(点击第一个搜索结果它应该是一个仓库链接标题包含“web-infra-dev/midscene”); console.log(AI已点击目标仓库); // 等待页面跳转 await page.waitForURL(/web-infra-dev\/midscene/); console.log(成功进入Midscene.js仓库页面); // 8. 让AI进行一个视觉断言验证页面上有“Star”按钮 const starButtonExists await midscene.aiQuery(页面上是否存在一个可以“Star”这个仓库的按钮); console.log(Star按钮是否存在, starButtonExists); } catch (error) { console.error(自动化过程出错, error); } finally { // 暂停一下方便观察结果 await page.waitForTimeout(5000); await browser.close(); } } main();3.4 运行与解析在终端运行这个脚本node first-ai-op.js如果你的环境配置正确你会看到浏览器自动打开导航到GitHub搜索框被点击、输入关键词然后自动点击了第一个仓库结果最终停在了Midscene.js的GitHub页面。这个简单脚本揭示了几个关键点aiAct(instruction)这是最常用的指令。你只需要用自然语言告诉AI要做什么。Midscene.js会处理截图、模型调用、坐标计算和动作执行的所有细节。混合操作模式脚本中我们让AI点击搜索框但自己用page.keyboard.type输入文本。这展示了Midscene.js可以无缝嵌入到你现有的Playwright/Puppeteer脚本中只在需要“智能识别”的地方调用AI其他常规操作沿用原有稳定方法非常灵活。aiQuery(question)这个方法用于向AI提问关于当前屏幕内容的问题并返回一个文本答案。它不执行操作只进行“视觉问答”VQA。在上面的例子里我们用它来做一个简单的存在性检查。指令的精确性最初的指令点击页面顶部的搜索输入框比较宽泛但通常足够。如果页面有多个搜索框你可以描述得更精确比如点击那个placeholder是“Search GitHub”的搜索框。AI模型能理解这些描述。注意事项首次运行可能会较慢因为需要下载模型相关的资源如果有本地模型或建立网络连接。aiAct的执行时间取决于模型响应速度和网络延迟通常需要1-3秒不适合对延迟有极端要求的超高速操作循环。4. 核心API深度解析与实战技巧掌握了基础操作后我们来深入Midscene.js的核心API了解如何用它应对更复杂的场景。4.1 核心方法不止于aiAct除了aiActSDK提供了其他几个强大的方法共同构成完整的自动化能力矩阵。方法名核心作用典型应用场景返回值aiAct(instruction)执行动作。让AI理解指令并在屏幕上执行相应操作点击、输入、滚动等。点击按钮、链接、输入文本、拖拽元素。PromisevoidaiQuery(question)视觉问答。向AI提问关于当前屏幕内容的问题。获取页面标题、读取特定位置的文本、确认某个元素的状态如是否禁用。Promisestring(AI的回答文本)aiAssert(condition)视觉断言。让AI判断一个视觉条件是否成立不成立则抛出错误。在测试中验证UI状态“登录成功后用户头像应该显示在右上角”。PromisevoidaiLocate(description)元素定位。仅让AI找到并返回元素的位置信息但不执行操作。当你需要先获取元素坐标再进行一些自定义操作时如计算相对偏移量后点击。PromiseBoundingBox(包含x, y, width, height的对象)aiExtract(schema)结构化数据提取。让AI根据你定义的JSON Schema从屏幕中提取结构化信息。从商品列表页提取名称、价格、评分从仪表盘中读取图表数值。Promise符合Schema的对象4.2 实战技巧编写健壮的AI指令指令的质量直接决定AI操作的准确性。以下是一些经过验证的指令编写技巧1. 使用明确的参照物和相对位置不佳点击那个按钮。哪个按钮较佳点击“提交”按钮。更佳点击登录表单下方的蓝色“提交”按钮。最佳在“用户名”输入框右侧找到并点击那个写着“忘记密码”的链接。2. 利用UI本身的文本内容AI对文本的识别能力极强。尽可能在指令中嵌入你希望它找到的UI文本。在顶部的导航栏中点击“文档”菜单。找到显示为“余额1234.56”的文本区域。点击弹窗中红色的“删除”按钮。3. 处理动态内容和等待UI不是静态的。在操作后需要给页面加载或状态变化留出时间。// 示例登录操作 await midscene.aiAct(在“用户名”输入框中输入“testuser”); await midscene.aiAct(在“密码”输入框中输入密码); await midscene.aiAct(点击“登录”按钮); // 重要等待登录后的页面变化 await page.waitForTimeout(3000); // 或等待某个特定元素出现 // 然后继续后续操作 await midscene.aiAssert(页面右上角应该显示用户昵称“testuser”);4. 组合使用aiQuery进行条件判断实现有逻辑分支的自动化流。// 检查是否有更新弹窗 const hasUpdateDialog await midscene.aiQuery(屏幕上是否有一个提示软件更新的弹窗); if (hasUpdateDialog.toLowerCase().includes(yes)) { await midscene.aiAct(点击更新弹窗上的“稍后提醒我”按钮); } else { console.log(没有更新弹窗继续流程。); }4.3 实战案例自动化一个复杂的Web表单填写假设我们要自动化一个电商网站的收货地址填写页面这个页面有多个字段且有依赖关系如选择省份后动态加载城市。import { Midscene } from midscene; import { chromium } from playwright; async function fillShippingAddress() { const browser await chromium.launch({ headless: false }); const page await browser.newPage(); const midscene new Midscene({ page, apiKey: process.env.DEEPSEEK_API_KEY, provider: deepseek }); await page.goto(https://example-store.com/checkout/address); try { // 1. 填写姓名和电话相对简单的字段 await midscene.aiAct(在“收货人”输入框中输入“张三”); await midscene.aiAct(在“手机号码”输入框中输入“13800138000”); // 2. 选择省份这是一个下拉选择框 await midscene.aiAct(点击“所在省份”选择框); await page.waitForTimeout(500); // 等待下拉列表展开 await midscene.aiAct(在下拉列表中找到并选择“广东省”); // 3. 等待城市选择框更新后选择城市 await page.waitForTimeout(1000); // 等待前端根据省份加载城市数据 await midscene.aiAct(点击“所在城市”选择框); await page.waitForTimeout(500); await midscene.aiAct(在下拉列表中找到并选择“深圳市”); // 4. 填写详细地址 await midscene.aiAct(在“详细地址”输入框中输入“南山区科技园科苑路100号”); // 5. 使用aiExtract验证邮政编码字段的格式提示 const zipCodeHint await midscene.aiQuery(“邮政编码”输入框旁边或下面有什么格式提示文字吗); console.log(邮政编码提示, zipCodeHint); // 根据提示决定如何输入 if (zipCodeHint.includes(6位)) { await midscene.aiAct(在“邮政编码”输入框中输入“518057”); } // 6. 最后点击保存按钮 await midscene.aiAct(滚动到页面底部点击“保存收货地址”按钮); await page.waitForTimeout(2000); // 7. 视觉断言确认保存成功提示出现 await midscene.aiAssert(页面上应该出现一个绿色的“保存成功”提示消息); console.log(收货地址填写并保存成功); } catch (error) { console.error(填写地址失败, error); // 可以在这里截图保存错误现场 await page.screenshot({ path: error-filling-address.png }); } finally { await page.waitForTimeout(3000); await browser.close(); } }这个案例展示了如何处理动态加载的下拉框、基于前一步结果的等待、以及利用aiQuery获取上下文信息来指导后续操作。aiAct指令的精确描述是成功的关键。5. 跨平台自动化从浏览器到移动端Midscene.js的威力在于其跨平台性。一旦你掌握了Web端的模式将其迁移到Android或iOS上概念是相通的只是底层驱动变了。5.1 Android设备自动化配置对于AndroidMidscene.js通常通过ADBAndroid Debug Bridge与设备通信并利用scrcpy之类的工具来获取屏幕截图。前置条件一台已开启“开发者选项”和“USB调试”的Android设备或模拟器。电脑上安装好ADB工具。通过USB连接设备或在同一网络下配置无线ADB连接。安装额外依赖pnpm add midscene/android-driverAndroid自动化脚本示例import { Midscene } from midscene; import { AndroidDriver } from midscene/android-driver; async function automateAndroidApp() { // 1. 初始化Android驱动 const driver new AndroidDriver({ deviceId: 你的设备序列号, // 通过 adb devices 获取默认连接第一个设备可省略 }); // 2. 连接设备并启动Midscene await driver.connect(); const midscene new Midscene({ driver, // 传入Android驱动实例而非Playwright的page apiKey: process.env.DEEPSEEK_API_KEY, provider: deepseek, }); try { // 3. 解锁屏幕假设设备已连接 await driver.adb.shell(input keyevent KEYCODE_WAKEUP); await midscene.aiAct(滑动屏幕以解锁); // 让AI执行滑动解锁操作 // 4. 打开微信假设微信在桌面 await midscene.aiAct(点击屏幕上的“微信”应用图标); await driver.delay(3000); // 等待应用启动 // 5. 在微信中操作 await midscene.aiAct(点击底部的“通讯录”选项卡); await driver.delay(1000); await midscene.aiAct(点击顶部的“搜索”框); await midscene.aiAct(在搜索框输入“文件传输助手”); // ... 更多操作 } finally { await driver.disconnect(); } }5.2 iOS设备自动化配置简述iOS自动化需要更复杂的设置通常需要macOS系统。安装了WebDriverAgent的iOS设备或模拟器。配置相关的证书和授权。Midscene.js社区提供了midscene-ios等驱动包。其代码结构与Android类似核心是切换不同的driver。import { IOSDriver } from midscene-ios-driver; // 假设的社区包 const driver new IOSDriver({ deviceName: iPhone Simulator }); const midscene new Midscene({ driver, apiKey, provider });重要提示移动端自动化尤其是iOS对环境配置要求较高且涉及系统权限。初次搭建可能会遇到较多障碍建议从Android模拟器开始尝试并详细阅读官方或社区驱动的文档。6. 高级应用集成测试与AI AgentMidscene.js不仅用于单次脚本更能融入现代开发工作流。6.1 与Vitest/Jest集成进行视觉回归测试你可以将aiAssert无缝集成到你的单元测试框架中创建强大的视觉断言测试。// 示例使用 Vitest import { describe, it, expect, beforeAll, afterAll } from vitest; import { Midscene } from midscene; import { chromium } from playwright; describe(购物车页面视觉测试, () { let browser, page, midscene; beforeAll(async () { browser await chromium.launch(); page await browser.newPage(); midscene new Midscene({ page, apiKey: process.env.API_KEY, provider: deepseek }); await page.goto(http://localhost:3000/cart); }); afterAll(async () { await browser.close(); }); it(应该正确显示空的购物车状态, async () { // 传统断言检查DOM元素 const emptyMessage await page.textContent(.cart-empty); expect(emptyMessage).toContain(购物车是空的); // 视觉断言检查用户实际看到的效果 await expect(async () { await midscene.aiAssert(页面中央应该显示一个明显的图标和“购物车是空的”文字); }).not.toThrow(); // 如果断言失败aiAssert会抛出错误 }); it(添加商品后总价应正确计算并显示, async () { // 模拟添加商品操作... await page.click(.add-to-cart-btn); await page.waitForTimeout(1000); // 视觉断言验证总价区域视觉呈现正确 await midscene.aiAssert(页面底部“总计”区域应该高亮显示并且金额为“299.00”); // 也可以结合aiQuery进行更灵活的验证 const totalPriceText await midscene.aiQuery(“总计”标签旁边显示的价格数字是多少); expect(totalPriceText).toMatch(/299\.00/); }); });这种测试方式特别适合验证样式、布局、动态效果等纯功能测试难以覆盖的部分。6.2 使用Midscene Skills构建自主AI AgentMidscene Skills是官方提供的一个高阶功能它允许你创建更复杂的、可复用的“技能”甚至让AI Agent自主规划任务。其核心思想是你定义一组可用的“技能”本质上是封装好的Midscene操作或逻辑然后给AI Agent一个高级目标如“帮我预订下周一早上9点从北京飞往上海的机票”Agent会自行分解任务、调用合适的技能、处理中间状态最终达成目标。这需要更复杂的配置通常涉及YAML文件定义技能和Agent设置。这代表了自动化从“脚本录制/编写”向“目标驱动”的范式转变是未来发展的方向。对于初学者可以先从基础的SDK用起逐步探索Skills。7. 常见问题、性能优化与避坑指南在实际使用中你肯定会遇到各种问题。以下是我踩过坑后总结的经验。7.1 常见问题排查表问题现象可能原因解决方案aiAct点击位置不准1. 屏幕缩放比例非100%。2. 模型识别误差。3. 指令描述模糊。1. 确保测试设备显示缩放为100%。2. 尝试更精确的指令如包含相邻元素文本。3. 使用aiLocate先获取坐标检查是否准确。操作执行失败报超时或网络错误1. API密钥无效或额度不足。2. 网络连接问题。3. 模型服务暂时不可用。1. 检查API密钥和环境变量。2. 检查网络尝试重试。3. 查看模型服务商状态页或切换备用模型。移动端截图失败或黑屏1. 设备未正确连接或授权。2. 屏幕锁屏。3. 某些安全应用禁止截图。1. 运行adb devices确认设备在线。2. 确保设备屏幕已解锁并亮起。3. 关闭某些安全软件的屏幕录制限制如游戏模式。脚本在CI/CD中运行失败1. 无头模式下的分辨率/字体与本地不同。2. 缺少浏览器或驱动。3. 环境变量未设置。1. 在CI配置中固定浏览器窗口大小。2. 在CI脚本中显式安装Playwright浏览器npx playwright install chromium。3. 在CI的Secrets中正确设置API_KEY。AI无法识别动态加载的元素元素尚未加载完成AI已截图。在aiAct或aiQuery前使用page.waitForSelector(传统) 或midscene.waitFor(等待AI可见) 确保元素出现。7.2 性能优化与成本控制减少不必要的AI调用AI调用有延迟和成本。对于稳定的、有可靠选择器的元素优先使用Playwright的原生方法如page.click(‘button#submit’)。只在必要时元素难以定位、跨平台、视觉验证使用aiAct/aiQuery。使用更快的模型对于速度要求高的场景可以尝试响应更快的模型如gemini-3.5-flash。在Midscene初始化时指定provider: ‘google’, model: ‘gemini-3.5-flash’。需要在性能和精度间权衡。设置合理的超时和重试const midscene new Midscene({ page, apiKey, provider: deepseek, timeout: 30000, // 全局超时30秒 maxRetries: 2, // 失败后重试2次 });利用本地或私有化模型如果对数据安全或调用成本有极高要求可以自部署开源的UI多模态模型如UI-TARS、Qwen-VL并通过Midscene的配置指向本地API端点。这需要较强的运维能力但能彻底控制成本和数据。7.3 我的独家避坑心得从“混合模式”开始不要试图用Midscene.js重写所有自动化脚本。最佳实践是“混合模式”用传统方法处理80%的稳定交互用Midscene.js解决剩下20%的“疑难杂症”如Canvas操作、验证码识别辅助、动态验证等。这样性价比最高。指令要“教”AI而不是“考”AI想象你在教一个完全不懂你网站的新人操作。指令要具体、包含上下文。例如与其说点击按钮不如说在登录表单里点击那个蓝色的“登录”按钮。截图区域优化默认是全屏截图。如果操作区域固定可以传递viewport参数给aiAct只截图特定区域能提升识别速度和精度。await midscene.aiAct(点击保存按钮, { viewport: { x: 100, y: 200, width: 400, height: 300 } // 只截取页面的一部分 });做好错误处理和日志AI并非100%可靠。一定要用try...catch包裹关键操作并在失败时保存截图和页面HTML这是后期调试的唯一依据。try { await midscene.aiAct(点击一个可能不存在的按钮); } catch (error) { console.error(AI操作失败, error.message); const timestamp Date.now(); await page.screenshot({ path: error-${timestamp}.png }); await page.content().then(html require(fs).writeFileSync(page-${timestamp}.html, html)); // 可能的降级方案 await page.click(button:has-text(Fallback)); }Midscene.js代表了一种新的自动化范式它用AI的视觉能力弥补了传统工具的固有缺陷。虽然它不能完全替代基于结构的自动化后者在速度和确定性上仍有优势但在处理复杂、动态、跨平台的UI交互时它提供了前所未有的灵活性和鲁棒性。对于前端开发者、测试工程师和RPA开发者来说将其纳入工具箱意味着你能应对的自动化场景边界被极大地拓宽了。开始尝试吧从一个小脚本开始感受让AI成为你浏览器操作员的魔力。