与任意界面集成
你可以使用 Midscene 的 Agent 来控制任意界面,比如 IoT 设备、内部应用、车载显示器等,只需要实现一个符合 AbstractInterface 定义的 UI 操作类。
在实现了 UI 操作类之后,你可以获得 Midscene Agent 的全部特性:
- TypeScript 的 GUI 自动化 Agent SDK,支持与任意界面集成
- 用于调试的 Playground
- 通过 yaml 脚本控制界面
- 通过 CLI 命令接入 Skills
演示和社区项目
我们已经为你准备了一个演示项目,帮助你学习如何定义自己的界面类。强烈建议你查看一下。
还有一些使用此功能的社区项目:
配置 AI 模型服务
将你的模型配置写入环境变量,可参考 模型策略 了解更多细节。
export MIDSCENE_MODEL_BASE_URL="https://替换为你的模型服务地址/v1"
export MIDSCENE_MODEL_API_KEY="替换为你的 API Key"
export MIDSCENE_MODEL_NAME="替换为你的模型名称"
export MIDSCENE_MODEL_FAMILY="替换为你的模型系列"
更多配置信息请参考 模型策略 和 模型配置。
实现你自己的界面类
关键概念
AbstractInterface 类:一个预定义的抽象类,可以连接到 Midscene 智能体
- 动作空间:描述可以在界面上执行的动作集合。这将影响 AI 模型如何规划和执行动作
步骤 1. 从 demo 项目开始
我们提供了一个演示项目,运行了本文档中的所有功能。这是最快的启动方式。
# 准备项目
git clone https://github.com/web-infra-dev/midscene-example.git
cd midscene-example/custom-interface
npm install
npm run build
# 运行演示
npm run demo
步骤 2. 实现你的界面类
定义一个继承 AbstractInterface 类的类,并实现所需的方法。
你可以从 ./src/sample-device.ts 文件中获取示例实现。让我们快速浏览一下。
import type { DeviceAction, Size } from '@midscene/core';
import { getMidsceneLocationSchema, z } from '@midscene/core';
import {
type AbstractInterface,
defineAction,
defineActionTap,
defineActionInput,
// ... 其他动作导入
} from '@midscene/core/device';
export interface SampleDeviceConfig {
deviceName?: string;
width?: number;
height?: number;
}
/**
* SampleDevice - AbstractInterface 的模板实现
*/
export class SampleDevice implements AbstractInterface {
interfaceType = 'sample-device';
private config: Required<SampleDeviceConfig>;
constructor(config: SampleDeviceConfig = {}) {
this.config = {
deviceName: config.deviceName || 'Sample Device',
width: config.width || 1920,
height: config.height || 1080,
};
}
/**
* 必需:截取屏幕截图并返回 base64 字符串
*/
async screenshotBase64(): Promise<string> {
// TODO:实现实际的屏幕截图捕获
console.log('📸 Taking screenshot...');
return 'data:image/png;base64,...'; // 你的屏幕截图实现
}
/**
* 必需:获取界面尺寸
* 这里的宽高是指界面的逻辑尺寸(logical size),不需要考虑设备像素比(dpr)。defineActionTap 等动作得到的坐标也是基于这个逻辑尺寸的坐标系。你可以在动作实现中根据需要将逻辑坐标转换为物理坐标。
*/
async size(): Promise<Size> {
return {
width: this.config.width,
height: this.config.height,
};
}
/**
* 必需:定义 AI 模型的可用动作
*/
actionSpace(): DeviceAction[] {
return [
// 基础点击动作
defineActionTap(async (param) => {
// TODO:实现在 param.locate.center 坐标的点击
await this.performTap(param.locate.center[0], param.locate.center[1]);
}),
// 文本输入动作
defineActionInput(async (param) => {
// TODO:实现文本输入
await this.performInput(param.locate.center[0], param.locate.center[1], param.value);
}),
// 自定义动作示例
defineAction({
name: 'CustomAction',
description: '你的自定义设备特定动作',
paramSchema: z.object({
locate: getMidsceneLocationSchema(),
// ... 自定义参数
}),
call: async (param) => {
// TODO:实现自定义动作
},
}),
];
}
async destroy(): Promise<void> {
// TODO:清理资源
}
// 私有实现方法
private async performTap(x: number, y: number): Promise<void> {
// TODO:你的实际点击实现
}
private async performInput(x: number, y: number, text: string): Promise<void> {
// TODO:你的实际输入实现
}
}
需要实现的关键方法有:
screenshotBase64()、size():帮助 AI 模型获取界面上下文
actionSpace():一个由 DeviceAction 组成的数组,定义了在界面上可以执行的动作。AI 模型将使用这些动作来执行操作。Midscene 已为常见界面与设备提供了预定义动作空间,同 时也支持定义任何自定义动作。
使用这些命令运行 Agent:
npm run build 重新编译 Agent 代码
npm run demo 使用 JavaScript 运行智能体
npm run demo:yaml 使用 yaml 脚本运行智能体
步骤 3. 使用 Playground 测试 Agent
为 Agent 附加一个 Playground 服务,即可在浏览器中测试你的 Agent。
import 'dotenv/config'; // 从 .env 文件里读取 Midscene 环境变量
import { playgroundForAgent } from '@midscene/playground';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// 实例化 device 和 agent
const device = new SampleDevice();
await device.launch();
const agent = new Agent(device);
// 启动 playground
const server = await playgroundForAgent(agent).launch();
// 关闭 Playground
await sleep(10 * 60 * 1000);
await server.close();
console.log('Playground 已关闭!');
步骤 4. 添加 Skill 支持(可选)
Agent Skills 让 AI 编程助手(如 Claude Code、Cline 等)可以通过 CLI 命令驱动你的自定义界面。了解更多请参考 Skills 文档。
在你的 npm 包中添加一个 CLI 入口文件(如 ./src/cli.ts):
#!/usr/bin/env node
import { runSkillCLI } from '@midscene/core/skill';
import { SampleDevice } from './sample-device';
runSkillCLI({
DeviceClass: SampleDevice,
scriptName: 'my-device',
});
然后在 package.json 中配置 bin 字段:
{
"bin": {
"my-device": "./dist/cli.js"
}
}
发布后,AI 编程助手即可通过 npx my-device 命令来控制你的自定义界面。
关于 Skill 的编写规范和更多示例,请参考 midscene-skills 仓库。
步骤 5. 发布 npm 包,让你的用户使用它
./index.ts 文件已经导出了你的 Agent 与界面类。现在可以发布到 npm。
在 package.json 文件中填 写 name 和 version,然后运行以下命令:
你的 npm 包的典型用法如下:
import 'dotenv/config'; // 从 .env 文件里读取 Midscene 环境变量
import { playgroundForAgent } from '@midscene/playground';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// 实例化 device 和 agent
const device = new SampleDevice();
await device.launch();
const agent = new Agent(device);
await agent.aiAct('click the button');
步骤 6. 在 Midscene CLI 和 YAML 脚本中调用你的类
编写一个包含 interface 字段的 yaml 脚本来调用你的类:
interface:
module: 'my-pkg-name'
# export: 'MyDeviceClass' # 如果是具名导出,使用该字段
config:
output: './data.json'
该配置等价于:
import MyDeviceClass from 'my-pkg-name';
const device = new MyDeviceClass();
const agent = new Agent(device, {
output: './data.json',
});
YAML 的其他字段与自动化脚本文档一致。
API 参考
AbstractInterface 类
import { AbstractInterface } from '@midscene/core';
AbstractInterface 是智能体控制界面的关键类。
以下是你需要实现的必需方法:
interfaceType: string:为界面定义一个名称,这不会提供给 AI 模型
screenshotBase64(): Promise<string>:截取界面的屏幕截图并返回带有 'data:image/ 前缀的 base64 字符串
size(): Promise<Size>:界面的大小,它是一个具有 width 和 height 属性的对象
actionSpace(): DeviceAction[] | Promise<DeviceAction[]>:界面的动作空间,它是一个 DeviceAction 对象数组。在这里你可以使用预定义动作,或是自定义交互操作。
类型签名:
import type { DeviceAction, Size, UIContext } from '@midscene/core';
import type { ElementNode } from '@midscene/shared/extractor';
abstract class AbstractInterface {
// 必选
abstract interfaceType: string;
abstract screenshotBase64(): Promise<string>;
abstract size(): Promise<Size>;
abstract actionSpace(): DeviceAction[] | Promise<DeviceAction[]>;
// 可选:生命周期/钩子
abstract destroy?(): Promise<void>;
abstract describe?(): string;
abstract beforeInvokeAction?(actionName: string, param: any): Promise<void>;
abstract afterInvokeAction?(actionName: string, param: any): Promise<void>;
}