使用 @ui-tars/cli 驱动 ADB 操作 Android 设备:UI-TARS 多模态 GUI Agent 的命令行实战指南
@ui-tars/cli 是 UI-TARS 开源多模态 Agent 技术栈(packages/ui-tars/cli)中面向命令行场景的轻量入口。它把「视觉大模型推理 + 界面操作执行器(Operator)」封装成一条命令,让你无需打开桌面应用,就能在终端里下达自然语言指令,由 Agent 自动完成点击、输入、滑动等 GUI 操作。读完本文,你将掌握 npx @ui-tars/cli start 的完整参数用法、模型 Presets/本地配置两种接入方式,并理解其底层如何通过 ADB 与 Android 真机交互、如何支持中文输入等关键原理。
快速开始:一条命令让 Agent 帮你操作手机
@ui-tars/cli 官方的使用方式极其简洁。在电脑上连接好 Android 设备(开启 USB 调试)后,直接运行 README 给出的命令即可:
npx @ui-tars/cli start -p 'your config' -t adb -q "Help me add Tom to my contacts. His phone number is 12345678900."
这条命令会启动一个 UI-TARS GUI Agent,让它自主完成「把 Tom 及其手机号 12345678900 添加到联系人」这一整串任务——包括观察屏幕截图、定位输入框、唤起键盘、逐字输入并最终保存。全程无需预写任何点击坐标或 UI 自动化脚本。
从 命令定义 可以看到,start 子命令总共支持三个核心选项:
| 短参数 | 长参数 | 说明 | 示例 |
|---|---|---|---|
-p |
--presets <url> |
指向一份远程 YAML 模型配置预设的 URL | -p 'https://example.com/preset.yaml' |
-t |
--target <target> |
指定执行目标 Operator,当前可选 adb(Android 真机)或 nut-js(桌面电脑) |
-t adb |
-q |
--query <query> |
直接通过命令行传入任务指令,跳过交互式输入 | -q "打开设置并开启飞行模式" |
此外还支持不带任何参数的 ui-tars start——此时 CLI 会进入交互模式,逐步引导你填写模型配置与任务指令(详见下文)。
接入你的视觉模型:Presets 远程预设与本地配置文件
Agent 的「大脑」来自视觉语言模型(VLM)。CLI 支持两种配置方式,核心实现位于 start.ts 配置加载逻辑。
方式一:通过 -p 传入远程 YAML Preset
当传入 -p 时,CLI 会用 node-fetch 拉取该 URL 内容,并按 YAML 解析为配置:
# 一份适用于 @ui-tars/cli 的模型 preset 示例
vlmBaseUrl: https://your-endpoint.huggingface.cloud/v1
vlmApiKey: your_api_key
vlmModelName: your_model_name
useResponsesApi: false
字段到运行时配置的映射关系如下(见 start.ts):
| Preset YAML 字段 | 运行时字段 | 作用 |
|---|---|---|
vlmBaseUrl |
baseURL |
VLM 服务地址(OpenAI 兼容 /v1 结尾的 Endpoint) |
vlmApiKey |
apiKey |
访问密钥 |
vlmModelName |
model |
使用的模型名称 |
useResponsesApi |
useResponsesApi |
是否走 Responses API,缺省为 false |
仓库里的 examples/presets/default.yaml 是配套 UI-TARS Desktop 的完整示例预设,除上述模型字段外还包含 vlmProvider、reportStorageBaseUrl、utioBaseUrl 等桌面端专属字段——CLI 加载预设时只消费与模型推理直接相关的四个字段,其余字段会被忽略,这也意味着同一份预设可以按需在桌面端与 CLI 之间复用。
方式二:本地配置文件 + 首次交互引导
如果不带 -p,CLI 会优先读取用户主目录下的 ~/.ui-tars-cli.json:
{
"baseURL": "https://your-endpoint.huggingface.cloud/v1",
"apiKey": "your_api_key",
"model": "your_model_name",
"useResponsesApi": false
}
若本地不存在该文件(或缺少 baseURL/apiKey/model 任一字段),CLI 会通过 @clack/prompts 弹出三个交互式输入框,收集 baseURL、apiKey 与模型名,并在完成后把配置自动回写保存到 ~/.ui-tars-cli.json(写入失败时仅打印错误,不中断流程),实现「首次交互填写、之后免配置」的体验。注意:交互收集到的配置不会包含 useResponsesApi,如需开启该开关,建议改用 Presets 方式或手动编辑本地 JSON。
选择执行目标 Operator:adb 与 nut-js
Agent 的「手脚」由 Operator 承担。CLI 当前内置两种目标,选择逻辑见 start.ts:
-t adb:通过 @ui-tars/operator-adb 驱动 Android 真机。CLI 会先调用getAndroidDeviceId()枚举设备:- 执行
adb devices并解析输出(跳过首行说明文字,按制表符切分提取设备序列号); - 若未检测到任何设备,直接报错退出(
No Android devices found...); - 若检测到多台设备,则会弹出列表让用户选择要调试的那一台,避免误操作。
- 执行
-t nut-js(默认):通过 @ui-tars/operator-nut-js 操作电脑桌面。从源码的switch结构看,当目标不是adb时一律落到NutJSOperator,因此它也是不传-t时的兜底默认值;browser(浏览器操作)在源码中以注释形式预留为 TODO,尚未实现。
需要特别说明的是:即便不传 -t,交互模式下 CLI 也会弹出「Please select your operator target」让用户二选一,因此实际使用中 -t 主要用于脚本化、无人值守的场景。
底层原理:ADB Operator 如何执行每一条 Agent 动作
深入 @ui-tars/operator-adb 的 Operator 实现,可以看到 ADB 目标对模型暴露的完整动作空间(ACTION_SPACES),也就是 Agent 在每轮推理中可以选择的「原语」,见源码第 61-73 行:
click(start_box='[x1, y1, x2, y2]')—— 在框定区域内点击;type(content='')—— 输入文本;swipe(start_box='[x1, y1, x2, y2]', end_box='[x3, y3, x4, y4]')—— 从起点滑动到终点;scroll(start_box='[...]', direction='down or up or right or left')—— 以 start_box 为锚点向指定方向滚动;hotkey(key='')—— 物理按键,可用键包括enter/back/home/backspace/delete/menu/power/volume_up/volume_down/mute/lock;wait()—— 等待 2 秒并重新截屏观察变化;press_home()—— 回到桌面;finished()—— 宣布任务完成;call_user()—— 任务无法独立解决或需要用户协助时提交并求助。
这些动作在执行阶段(execute 方法)被翻译为一条条真实的 adb shell 命令,例如:
click→adb -s <deviceId> shell input tap x y(坐标由模型输出的归一化 box 经parseBoxToScreenCoords换算得到);swipe/drag→adb shell input swipe ...,并在起止坐标间做 300ms 滑动;scroll→ 基于start_box与direction计算终点偏移(上/下 100px、左/右 100px)后调用同样的 swipe 原语;press_home→adb shell input keyevent KEYCODE_HOME;hotkey→ 按 key 映射到对应KEYCODE_*键码(如backspace为 67、delete为 112、lock为 26 等)。
整个 GUI 循环遵循通用的「截屏 → 视觉模型推理 → 执行动作 → 再截屏验证」范式:每次 screenshot() 通过 adb exec-out screencap -p 抓取真机画面并编码为 base64(scaleFactor: 1)交给模型,模型以多模态输入理解当前界面并产出下一个动作原语。SDK 层的编排则由 @ui-tars/sdk 的 GUIAgent 承担——CLI 在 start.ts 中只负责把模型配置、Operator 实例与中止信号(AbortSignal)组装起来,然后 await guiAgent.run(instruction) 驱动整个任务闭环。
中文输入:需要 ADBKeyBoard 配合
真机输入(type)是 ADB Operator 中最特殊的一环。adb shell input text 本身不支持中文,因此实现中内置了 ADBKeyBoard 协作逻辑(对应源码):
- 首次输入时,通过
settings get secure default_input_method探测当前输入法; - 若待输入文本包含 CJK 汉字(Unicode 范围
0x4e00–0x9fff)且未启用 AdbIME,则自动执行adb shell ime set com.android.adbkeyboard/.AdbIME; - 若 AdbIME 不可用(报
cannot be selected),会输出引导性错误并中止该轮,避免静默失败; - 输入内容会先转义
'、"、\等特殊字符,再通过广播ADB_INPUT_TEXT或adb shell input text下发。
因此若你的任务涉及中文输入,需先在设备上完成 ADBKeyBoard 安装与激活,官方操作见 @ui-tars/operator-adb README:
# 安装 ADBKeyBoard
adb install /path/to/AdbKeyboard.apk
# 激活 ADBKeyBoard 输入法
adb shell ime set com.android.adbkeyboard/.AdbIME
交互模式、取消与错误处理
CLI 从设计上同时照顾了「一次性脚本」与「人机交互」两类用法:
- 指令来源:传入
-q时直接使用该字符串作为任务指令;否则弹出Input your instruction文本输入框(见 start.ts)。 - 中断控制:程序监听
SIGINT(即终端 Ctrl+C),触发后调用abortController.abort()将中止信号传递给GUIAgent,从而优雅终止正在进行的任务(start.ts)。 - 错误处理:构造
GUIAgent时注入的onError回调会把异常与相关数据打印到终端(源码中另有被注释掉的onData钩子,可用于逐轮观察模型输出的结构化数据,方便调试);start命令的顶层try/catch捕获到致命错误时会打印堆栈并以非零码退出(commands.ts)。 - 取消保护:所有交互式
@clack/prompts组都注册了onCancel,用户按 Esc 会提示取消并正常退出进程,而不是抛出不友好的异常。
在仓库中本地构建与开发
@ui-tars/cli 与仓库内的其余包一样基于 rslib 构建,package.json 中提供了标准的开发脚本:
npm run build—— 一次构建产物(dist);npm run dev/build:watch—— rslib watch 模式,改动源码即增量重建,适合本地联调;npm test—— 运行 vitest 测试。
其直接依赖包括 commander(命令行解析)、@clack/prompts(交互式提示)、js-yaml(解析 Presets)、@ui-tars/sdk(Agent 编排核心)、@ui-tars/operator-adb 与 @ui-tars/operator-nut-js(两种执行器)。由于这些 Operator/SDK 在仓库内以 workspace:* 形式引用,若要在本地跑通完整链路,建议在 monorepo 根目录先安装依赖并构建上游包,再进入本包目录启动;而对于一般使用,直接 npx @ui-tars/cli 即可从 npm registry 拉取已发布版本。
参考资料与进一步阅读
- CLI 源码入口(命令注册)
- CLI 核心启动逻辑(配置加载与 Agent 组装)
- ADB Operator 完整实现与动作空间
- ADBKeyBoard 安装与激活说明
- Presets 示例配置
- Agent SDK 包(GUIAgent 等编排能力)
- UI-TARS Desktop 桌面应用(CLI 之外的另一使用形态)
总体而言,@ui-tars/cli 把「多模态 GUI Agent + ADB」封装为一条可复制的命令行,适合脚本化调用、CI 集成或快速验证模型在真实设备上的操作能力;配合 Presets 远程配置,还能做到「模型配置与代码分离、随时热切换」,是体验 UI-TARS Agent 能力成本最低的入口之一。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.24 K639- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python720
SlideSCIPPT插件,支持素材库、AI助手、一键添加图片标题,复制粘贴位置、一键图片对齐、一键插入Markdown(加粗、超链接等行内样式、代码块、LaTeX等块级样式)、便捷导出图片!C#360
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python53275
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go22845
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java36851