写过 UI 自动化的人,大概率见过这样的代码:
await page.locator('#search-input').fill('Midscene.js');await page.locator('.search-button').click();await expect(page.locator('.result-list')).toBeVisible();它很精确,但也很依赖页面结构。只要前端把 id、类名或者 DOM 层级改掉,测试脚本就可能失效。
Midscene.js 提供了另一种思路:让多模态模型观察页面截图,再根据自然语言理解界面、定位元素并执行操作。
同一段操作可以写成:
await agent.aiAct('在搜索框输入 Midscene.js,然后点击搜索按钮');await agent.aiAssert('页面已经展示与 Midscene.js 相关的搜索结果');这篇文章从零开始介绍 Midscene.js。读完后,你将能够:
- 理解它和 Playwright 的关系;
- 配置一个支持视觉理解的模型;
- 编写第一段 AI 网页自动化脚本;
- 使用自然语言完成操作、等待、取数和断言;
- 将它接入 Playwright 测试;
- 理解它的成本、稳定性和适用边界。
1. Midscene.js 是什么
Midscene.js 是一个开源、视觉驱动的 UI 自动化框架。它让我们用自然语言描述目标,再由 AI 根据当前界面规划并执行操作。
它不只支持网页,还可以用于 Android、iOS、HarmonyOS、桌面应用和自定义界面。本文先从最容易上手的网页自动化开始。
传统 Playwright 通常这样工作:
测试代码 ↓CSS / XPath / role 定位器 ↓直接操作 DOM 元素Midscene 的典型链路是:
自然语言目标 ↓获取当前页面截图和界面信息 ↓多模态模型理解页面 ↓规划点击、输入、滚动等步骤 ↓通过 Playwright 执行操作 ↓继续观察,直到目标完成因此,Midscene 不是 Playwright 的替代品。Playwright 负责真正控制浏览器,Midscene 在它上面增加了视觉理解和自然语言规划能力。
2. 它适合解决什么问题
Midscene 比较适合:
- 页面结构经常变化,选择器维护成本很高;
- 快速编写业务流程演示或自动化原型;
- 对后台系统进行语义化 UI 测试;
- 从视觉布局复杂的页面提取结构化数据;
- 页面元素缺少稳定的
id或data-testid; - 希望用接近测试用例的语言描述操作。
例如:
登录商城后台搜索名称包含“手机”的商品打开库存最低的商品确认页面显示库存预警如果完全使用传统定位器,这条流程可能涉及大量选择器。Midscene 可以直接理解“库存最低”“库存预警”这些语义。
但它不适合替代所有自动化代码。支付提交、删除数据、权限校验等高风险操作,仍然应该使用确定性定位器、后端接口校验和严格断言。
3. 两种入门方式
官方提供两条快速体验路径:
3.1 Chrome 扩展
如果只是想体验,可以先安装 Midscene Chrome 扩展。打开任意网页后,在扩展中输入自然语言指令,不需要创建 Node.js 项目。
它适合:
- 验证模型配置是否正确;
- 尝试一句提示词能否完成目标;
- 观察 AI 如何理解页面;
- 在写代码前快速调试操作描述。
扩展更像一个 Playground。确认提示词有效后,再把它迁移到 SDK 脚本中。
3.2 JavaScript SDK
SDK 适合需要重复执行、加入版本管理、接入 CI 或 Playwright 测试的场景。
本文主要讲 SDK 的使用方法。
4. 准备开发环境
需要准备:
Node.js 18 或更高版本npm / pnpm一个支持视觉理解的多模态模型创建项目:
mkdir midscene-democd midscene-demonpm init -y安装依赖:
npm install --save-dev @midscene/web playwright tsx dotenvnpx playwright install chromium其中:
@midscene/web:Midscene 网页自动化 SDK;playwright:控制浏览器;tsx:直接运行 TypeScript;dotenv:从.env文件加载模型配置。
在 package.json 中增加命令:
{ "scripts": { "demo": "tsx demo.ts" }}5. 配置多模态模型
Midscene 依赖模型理解页面截图,所以普通纯文本模型不能完成全部工作。
当前版本推荐使用四个环境变量:
MIDSCENE_MODEL_BASE_URL=https://你的模型服务地址/v1MIDSCENE_MODEL_API_KEY=你的API密钥MIDSCENE_MODEL_NAME=你的模型名称MIDSCENE_MODEL_FAMILY=对应的模型家族四个变量分别表示:
| 变量 | 作用 |
|---|---|
MIDSCENE_MODEL_BASE_URL | OpenAI 兼容接口地址,不要手动追加 /chat/completions |
MIDSCENE_MODEL_API_KEY | 模型服务密钥 |
MIDSCENE_MODEL_NAME | 实际调用的模型名称 |
MIDSCENE_MODEL_FAMILY | 告诉 Midscene 如何适配模型的视觉定位格式 |
例如,使用官方快速开始中展示的 OpenRouter 配置形式:
MIDSCENE_MODEL_BASE_URL=https://openrouter.ai/api/v1MIDSCENE_MODEL_API_KEY=替换成你自己的密钥MIDSCENE_MODEL_NAME=qwen/qwen3.7-plusMIDSCENE_MODEL_FAMILY=qwen3模型和 MODEL_FAMILY 必须匹配,不要根据模型名称自己猜。不同模型的最新配置应查看官方的模型支持文档。
早期文章中可能会看到 OPENAI_API_KEY、OPENAI_BASE_URL。当前版本仍然兼容这些变量,但官方已经推荐使用 MIDSCENE_MODEL_*。
将密钥文件加入 .gitignore:
.envmidscene_run/不要把真实 API Key 提交到 Git。
6. 编写第一段自动化脚本
新建 demo.ts:
import { chromium } from 'playwright';import { PlaywrightPageAgent } from '@midscene/web/playwright';import 'dotenv/config';
const browser = await chromium.launch({ headless: false,});
const page = await browser.newPage({ viewport: { width: 1280, height: 800 },});
await page.goto('https://www.bing.com');
const agent = new PlaywrightPageAgent(page);
await agent.aiAct( '在搜索框输入 Midscene.js,点击搜索,并等待搜索结果出现',);
await agent.aiAssert( '页面中已经出现与 Midscene.js 相关的搜索结果',);
await browser.close();运行:
npm run demo第一次建议设置:
headless: false这样可以直接看到浏览器如何输入和点击。流程稳定后,再改成无头模式运行。
PlaywrightPageAgent 表示这个 Agent 绑定到一个 Playwright Page。旧示例中的 PlaywrightAgent 目前仍然可用,但它是为了兼容保留的别名,新代码优先使用 PlaywrightPageAgent。
7. 最常用的四类 API
Midscene 的常用能力可以归纳为:
Act:操作页面Wait:等待页面进入某种状态Query:理解并提取页面数据Assert:判断页面是否满足条件7.1 aiAct:完成一系列操作
await agent.aiAct( '点击右上角登录,在用户名中输入 demo,密码中输入 123456,然后点击登录',);aiAct() 会让模型持续观察、规划和执行,直到认为目标已经完成。
一个指令里可以包含多个步骤,但不要一次塞入过长的业务流程。建议按业务阶段拆分:
await agent.aiAct('打开登录窗口');await agent.aiAct('输入测试账号和密码并登录');await agent.aiWaitFor('用户头像已经出现在页面右上角');这样失败时更容易定位是哪一步出了问题。
7.2 aiWaitFor:等待语义状态
await agent.aiWaitFor('商品列表加载完成,并且至少显示一件商品', { timeoutMs: 10_000,});它适合等待“列表出现”“弹窗关闭”“提交成功”等视觉状态,比固定 sleep(5000) 更贴近真实目标。
如果页面有确定的网络响应或 DOM 状态,优先使用 Playwright 的确定性等待:
await page.waitForResponse('/api/products');视觉等待和确定性等待可以混合使用。
7.3 aiQuery:提取结构化数据
const products = await agent.aiQuery< Array<{ title: string; description: string }>>( '{ title: string, description: string }[], 提取前三条 Midscene.js 相关搜索结果',);
console.log(products);相比“告诉我页面上有什么”,明确写出数据结构更稳定:
{ title: string, price: number }[]Midscene 还提供几个方便的类型化查询:
const title = await agent.aiString('第一条结果的标题');const count = await agent.aiNumber('当前页面显示多少条搜索结果');const hasLogin = await agent.aiBoolean('页面上是否存在登录按钮');const location = await agent.aiLocate('第一条搜索结果的位置');需要参与程序判断的数据,建议使用类型化方法或 aiQuery,不要从一大段自然语言回答中手动截取。
7.4 aiAssert:进行自然语言断言
await agent.aiAssert('搜索结果区域至少包含一条与 Midscene.js 相关的结果');断言失败时,Midscene 会抛出异常,并在报告中给出相关信息。
但是,金额、订单状态、权限等关键事实最好提取后用普通代码断言:
const stock = await agent.aiNumber('第一件商品的库存');
if (stock < 0) { throw new Error(`库存不能小于 0,当前值为 ${stock}`);}AI 断言适合判断视觉语义,JavaScript 断言适合判断精确数据。
8. 精细操作 API
如果不需要模型规划多个步骤,可以使用更直接的操作方法:
await agent.aiInput('Midscene.js', '页面顶部的搜索框');await agent.aiTap('搜索按钮');await agent.aiScroll( { scrollType: 'untilBottom' }, '搜索结果区域',);await agent.aiRightClick('第一条搜索结果');它们仍然使用 AI 定位目标元素,但执行动作更加明确。
经验上可以这样选择:
多个连续步骤、不确定下一步页面 → aiAct明确输入某个字段 → aiInput明确点击某个语义元素 → aiTap等待视觉状态 → aiWaitFor提取页面内容 → aiQuery验证视觉结果 → aiAssert9. 把 Midscene 接入 Playwright Test
一次性脚本适合体验,项目中的 UI 回归测试更适合使用 @playwright/test。
安装:
npm install --save-dev @playwright/test创建 playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({ testDir: './e2e', timeout: 120_000, reporter: [ ['list'], ['@midscene/web/playwright-reporter'], ], use: { headless: true, viewport: { width: 1280, height: 800 }, },});创建 e2e/fixture.ts:
import { test as base } from '@playwright/test';import { PlaywrightAiFixture, type PlayWrightAiFixtureType,} from '@midscene/web/playwright';
export const test = base.extend<PlayWrightAiFixtureType>( PlaywrightAiFixture({ waitForNetworkIdleTimeout: 1000, }),);
export { expect } from '@playwright/test';创建 e2e/search.spec.ts:
import { test, expect } from './fixture';
test('搜索 Midscene.js', async ({ page, aiInput, aiTap, aiWaitFor, aiQuery, aiAssert, recordToReport,}) => { await page.goto('https://www.bing.com');
await aiInput('Midscene.js', '搜索框'); await aiTap('搜索按钮'); await aiWaitFor('搜索结果已经加载完成');
const titles = await aiQuery<string[]>( 'string[], 提取前三条搜索结果的标题', );
expect(titles.length).toBeGreaterThan(0); await aiAssert('结果中至少有一条与 Midscene.js 相关');
await recordToReport('搜索结果', { content: JSON.stringify(titles), });});运行:
npx playwright test这样可以继续使用 Playwright 的测试组织、超时、重试和断言,同时使用 Midscene 处理视觉理解和语义定位。
10. 如何写出更稳定的提示词
AI 自动化的稳定性很大程度上取决于指令是否清楚。
10.1 描述可观察目标
不推荐:
处理一下商品推荐:
在商品列表中找到名称包含“耳机”的第一件商品,打开详情页10.2 提供界面线索
点击页面右上角、购物车图标左侧的登录按钮位置、文字、颜色和相邻元素都能帮助模型定位。
10.3 一个指令只承担一个业务阶段
不要把登录、搜索、下单、付款、退出全部放进一次 aiAct。步骤越长,调试和重试越困难。
10.4 明确结束状态
点击保存,并等待页面右上角出现“保存成功”提示只有操作而没有结束条件,模型可能过早判断任务完成。
10.5 取数时给出结构
{ name: string, price: number, stock: number }[],提取当前页全部商品结构越明确,后续代码越容易处理。
11. 推荐使用“AI + 确定性代码”混合模式
全部交给 AI 看起来代码最少,但不一定最可靠。
推荐分工:
Playwright:打开固定 URL、注入登录态、等待接口、精确提交Midscene:理解复杂页面、寻找语义元素、提取视觉数据普通断言:校验金额、数量、状态码和权限结果例如:
await page.goto('https://example.com/admin/products');
await agent.aiAct('打开库存最低的商品详情');
const product = await agent.aiQuery<{ name: string; stock: number;}>('{ name: string, stock: number },提取当前商品名称和库存');
expect(product.stock).toBeGreaterThanOrEqual(0);这类混合方式兼顾了灵活性和确定性。
12. 调试与运行报告
Midscene 会生成运行报告,用来查看:
- AI 看到了什么页面;
- 模型生成了什么执行计划;
- 定位到了哪个元素;
- 实际执行了哪些动作;
- 哪一步出现错误。
当脚本失败时,不要只增加 sleep,应该先看报告并判断:
页面是否还没加载完成模型是否选错了元素指令是否存在歧义弹窗是否遮挡目标页面是否打开了新标签页模型配置是否支持视觉定位对于固定出现的 Cookie 弹窗,可以在创建 Agent 时补充上下文:
const agent = new PlaywrightPageAgent(page, { actionContext: '如果出现 Cookie 同意弹窗,先点击接受,再执行任务。',});13. 常见问题
13.1 提示缺少 API Key
检查是否加载了:
import 'dotenv/config';并确认 .env 位于运行命令所在目录。
13.2 模型能回答文字,但不会点击
通常是模型不支持视觉定位,或者 MIDSCENE_MODEL_FAMILY 配置不匹配。以官方模型支持表为准。
13.3 页面输入时丢字符
受控输入框可能无法处理过快输入,可以配置:
const agent = new PlaywrightPageAgent(page, { keyboardTypeDelay: 80,});13.4 点击后打开新标签页
页面级 Agent 默认更偏向保持在当前页。如果业务确实需要管理多个页面,可以关闭同页限制,并为新页面创建 Agent,或者使用 PlaywrightBrowserAgent。
13.5 测试偶尔成功、偶尔失败
依次检查:
- 是否使用了固定等待而页面加载速度不稳定;
- 提示词是否同时包含太多步骤;
- 页面是否有随机弹窗;
- 浏览器窗口大小是否固定;
- 模型和模型家族是否匹配;
- 是否应该将关键步骤改为 Playwright 定位器。
13.6 运行速度比普通 Playwright 慢
这是正常现象。Midscene 需要截图、调用模型、规划和验证,延迟与 Token 成本都高于确定性脚本。
不要为了“更 AI”而把所有点击都改成模型调用。稳定选择器能够完成的动作,继续使用 Playwright 通常更便宜、更快。
14. 使用时需要注意的安全问题
Midscene 可以点击、输入、上传文件,因此它拥有真实操作界面的能力。
在测试环境中使用时应注意:
- 不要将生产管理员账号交给自动化脚本;
- 不要让不可信网页内容改变高风险操作目标;
- 删除、支付、发布等操作保留人工确认;
- 上传文件时限制允许访问的目录;
- 模型 API Key 只通过环境变量或密钥服务提供;
- CI 中使用独立测试账号和隔离数据;
- 对 AI 操作保留报告和审计记录。
自然语言指令可以提高开发效率,但不能替代服务端权限校验。
15. 一个合理的学习顺序
新手可以按以下路线学习:
Chrome 扩展体验自然语言操作 ↓PlaywrightPageAgent 编写单文件脚本 ↓掌握 aiAct / aiWaitFor / aiQuery / aiAssert ↓使用 aiInput / aiTap 完成精细操作 ↓接入 Playwright Test ↓查看报告并优化提示词 ↓采用 AI + Playwright 混合模式 ↓接入 CI 和专用测试数据不要一开始就用它完成几十步的下单流程。先选择一个页面、一项操作和一个断言,稳定后再逐步扩展。
16. 本文总结
Midscene.js 为 UI 自动化增加了一层视觉理解和自然语言规划能力:
Playwright 控制浏览器+ 多模态模型理解页面+ 自然语言描述操作目标+ aiQuery 提取结构化数据+ aiAssert 验证视觉语义+ 运行报告辅助调试它最大的价值不是让我们彻底告别选择器,而是让过去难以维护的视觉定位和复杂页面理解变得更简单。
真正稳定的使用方式通常是:
用 Midscene 处理不确定、视觉化、语义化的部分,用 Playwright 和普通代码处理确定、精确、高风险的部分。
如果只是刚开始学习,先完成本文的搜索脚本,再把目标页面替换成自己的商城后台。尝试让它搜索商品、打开详情、提取库存并完成语义断言,就能很快理解 Midscene 的工作方式。
参考资料
If this article helped you, please share it with others!
Some information may be outdated






