# DeepSeek Harness Mobile GUI Agent [![CI](https://github.com/kunjinkao-os/dsh-mobile-gui-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/kunjinkao-os/dsh-mobile-gui-agent/actions/workflows/ci.yml) [![awesome · DSH plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com) [![DeepSeek Harness](https://img.shields.io/badge/DeepSeek%20Harness-0.1.0--rc.6-4B32C3)](https://github.com/deepseek-ai/deepseek-harness) [![Android](https://img.shields.io/badge/Android-ADB-3DDC84?logo=android&logoColor=white)](https://developer.android.com/tools/adb) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](../LICENSE) [English](../README.md) | 中文 | [更新日志](CHANGELOG.zh.md) `dsh-mobile-gui-agent` 是一个可安装的 DeepSeek Harness Android GUI Agent 插件。它通过 ADB 控制真机或模拟器,在 Harness Web UI 中增加 **mobile_gui_agent** 入口,并让每个任务严格执行“观察 → 决策 → 动作 → 验证”的循环。 本仓库是单个可发布 npm 包。bundle patch 只插入一个 Cordis 插件行;该插件在同一生命周期下组合 ADB Provider、Phone Agent Consumer、Phone 工具、Typert Remote 适配器和浏览器客户端。 ## 快速开始 连接并授权 Android 设备,然后把固定版本安装到 Harness Web profile: ```bash adb devices -l dsh plugin --profile web add github:kunjinkao-os/dsh-mobile-gui-agent#v0.2.1 dsh --profile web --dump-config dsh --profile web ``` 设备行必须显示 `device`。配置输出中必须出现 `# == dsh-mobile-gui-agent` 层和一个 `dsh-mobile-gui-agent` 插件行。推荐先在 Harness 普通对话框发送一条简短消息,再点击 **mobile_gui_agent**,并在其中的**任务**输入框填写真实手机命令。不要把手机命令直接输入 Harness 普通对话框。操作包含账号或个人数据的设备前,请先阅读[前置条件](#前置条件)和[使用](#使用)。 ## 推荐操作流程 1. 选择工作区,在 Harness 普通对话框发送一条简短的初始化消息,例如“准备使用手机任务”。 2. 点击会话顶部的 **mobile_gui_agent** 标签页。 3. 选择已连接设备,只在 **mobile_gui_agent** 内的**任务**输入框填写真实手机命令。 4. 点击 **Start**,查看经过验证的执行步骤。不要把手机命令发送到 Harness 普通对话框。 空白会话的输入框工具栏仍提供快捷入口,但以上流程能更清楚地区分 Harness 对话消息与手机任务。 ![mobile_gui_agent 设置并启用 12:00 闹钟](images/mobile-gui-agent-alarm-task.png) ## 兼容性 - DeepSeek Harness:`^0.1.0-rc.5` - 已对上游提交 `47f943859bef60e4160492346772ded9b24f765a` 验证 - 同时使用 npm 已发布的 `0.1.0-rc.6` Harness 包完成构建和测试 - Node.js:`^22.19.0 || >=24.0.0` - Android:能被 `adb devices` 发现的真机或模拟器 插件遵循上游的 `dsh.bundle.patch` 与 `dsh.client` manifest,不修改 Harness Agent loop,也不依赖未发布的 Phone 包。 ## 能力 - ADB 设备发现、无线连接、截图、UIAutomator hierarchy、点击、长按、滑动、经验证的文本输入/替换、按键、返回、主页和启动应用 - 截图与裁剪压缩后的语义 UI 共同构成观察,每轮元素使用短 ID - 严格的 `phone_observe` 与 `phone_act` Harness 工具 - 每个模型回合只执行一个有意义动作,随后重新观察并确定性验证 - 过期元素保护、自适应稳定等待、卡住检测、步骤与时间限制、可恢复 ADB 错误 - 对发送、发布、删除、购买、支付、转账、拨号、安装和账号安全修改等语义控件复用 Harness 审批 - 空白会话 **mobile_gui_agent** 入口与 Web 会话标签页:设备选择、无线连接、截图刷新、开始/暂停/继续/停止、动作覆盖框和验证步骤 - 用于无密钥 CI 的 FakePhoneDevice 与脚本化页面状态 本插件不安装 Android 无障碍服务。它组合 ADB 截图与 Android UIAutomator hierarchy。Canvas、WebView、游戏和纯图片控件可能能在截图中看到,却不存在于 hierarchy;此时 Agent 可使用支持视觉输入的模型,或由其他插件提供可选的 `PhoneVisionProvider`。 ## 前置条件 1. 安装 Android Platform Tools,确认 `adb version` 可执行。 2. 在 Android 中开启开发者选项和 USB 调试。 3. 手机弹出 RSA 调试授权时允许此电脑。 4. 执行: ```bash adb devices -l ``` 目标行必须是 `device`,不能是 `offline` 或 `unauthorized`。 5. 使用 DeepSeek Harness Web profile。**mobile_gui_agent** 是浏览器客户端贡献,纯 headless profile 不会显示该入口。 观察、导航和可打印 ASCII 输入不需要 root,也不要求手机安装 APK。Unicode 输入使用外部 ADB Keyboard helper(`com.android.adbkeyboard/.AdbIME`)。只要 helper 已安装,插件即使在它未启用时也能发现它,并仅在本次 Unicode 输入期间临时启用和选中,完成后恢复用户原输入法及启用状态;模型无需先执行初始化,用户也无需手工切换。helper 确实缺失时,可用 `adb.unicodeImeApkPath` 配置一个已审查 APK 在宿主机上的绝对路径;插件不会下载 APK,安装仍然需要 Harness 显式审批。 ## 安装方式 把本地 checkout 安装到标准 Web profile: ```bash dsh plugin --profile web add ./dsh-mobile-gui-agent dsh --profile web --dump-config dsh --profile web ``` 如果从 Harness 源码仓库运行,请把 `dsh` 替换为 `pnpm dsh`: ```bash pnpm dsh plugin --profile web add ../dsh-mobile-gui-agent pnpm dsh --profile web --dump-config pnpm dsh --profile web ``` 配置输出中应出现 `# == dsh-mobile-gui-agent` 层和一个名为 `dsh-mobile-gui-agent` 的插件行。打开 Web UI 并选择工作区。空白会话的输入框工具栏可以立即打开任务面板;推荐先在普通对话框发送一条简短初始化消息,让会话进入非空状态,再点击常规 **mobile_gui_agent** 标签页并在其中输入手机任务。 仓库提交了预构建 `lib/`,因此固定 Git 提交安装时无需允许依赖构建脚本: ```bash dsh plugin --profile web add github:kunjinkao-os/dsh-mobile-gui-agent#v0.2.1 ``` 如需不可变的审查目标,可把发布标签替换为对应提交 SHA。也可以直接安装 Release tarball,不需要 Git 构建步骤: ```bash dsh plugin --profile web add ./dsh-mobile-gui-agent-0.2.1.tgz ``` 安装能控制真实设备的插件时,应固定到经过审查的标签或提交。 ## 无线 ADB 如果 Android 版本要求配对,先使用 Platform Tools 完成配对。可以在启动 Harness 前连接: ```bash adb connect DEVICE_IP:PORT adb devices -l ``` 也可以在 **mobile_gui_agent** 面板输入 `DEVICE_IP:PORT` 后点击 **Connect**。电脑和 Android 设备必须网络互通;Android 重启无线调试后端口可能变化。 ## 使用 1. 打开 Harness 会话,在普通对话框发送一条简短初始化消息,例如“准备使用手机任务”。 2. 点击会话顶部的 **mobile_gui_agent** 标签页;空白会话的输入框工具栏入口仍可作为备用方式。 3. 选择已连接设备并刷新截图。 4. 在 **mobile_gui_agent** 的**任务**输入框填写真实手机命令,不要在 Harness 普通对话框中填写。例如: ```text 打开设置并进入 Wi-Fi 页面 ``` 5. 点击 **Start**;需要时使用 **Pause**、**Resume** 或 **Stop**。 6. 在 Harness 原生审批 UI 中处理确认。需要审批的动作在获得一次性许可前不会执行。 更多任务示例: ```text 打开 Android 设置 打开浏览器并点击地址栏 在当前文本框输入 hello world 打开微信,找到文件传输助手,准备发送“测试123” ``` 最后一个示例在点击语义明确的“发送”控件前会请求审批。 ## 配置 [`cordis.patch.yml`](../cordis.patch.yml) 提供默认值。Harness patch 会整体替换插件行的 `config`,不会深度合并;因此在 profile 的 `cordis.patch.yml` 覆盖时应写出完整配置。完整示例见[英文 README 的配置章节](../README.md#configuration)。 关键配置: - `adb.unicodeImeApkPath`:可选;宿主机上已审查 ADB Keyboard APK 的绝对路径。该路径不能由模型传入。已经安装的 helper 会通过 Android 全量 IME 列表发现,插件按需临时启用/选中并在输入后恢复,无需审批。helper 缺失且配置了该路径时,Agent 可调用 `setup_unicode_input`,安装始终需要一次性审批。插件不内置也不下载 [ADBKeyBoard](https://github.com/senzhk/ADBKeyBoard),配置前应自行审查源码、APK 和许可证。 - `adb.commandTimeoutMs`:单个 ADB 子进程的超时。 - `agent.actionTimeoutMs`:动作、页面稳定等待和动作后观察的总超时,应大于 ADB 命令超时;较慢的无线设备可设为 30–60 秒。 - `agent.maxSteps`、`agent.taskTimeoutMs`:限制完整任务。 - `agent.maxConsecutiveFailures`:连续验证失败上限。 - `agent.traceScreenshots`:视觉模型可用时,把截图存入 Harness attachment store。 - `agent.maxTraceSteps`:GUI 当前及终态步骤的保留上限。 截图、hierarchy 和诊断输出都有字节上限,避免 ADB 输出或执行轨迹无限增长。 ## 架构 ```text dsh.bundle patch └── dsh-mobile-gui-agent(一个 Cordis Loader 行) ├── AdbPhoneDeviceRegistry 提供 ctx.phone ├── PhoneAgentService 提供 ctx.phoneRuns │ └── Agent 范围工具 phone_observe + phone_act └── PhoneAgentRemote Typert Host namespace dsh.client 浏览器贡献 ├── 挂载生成的 phoneAgent Typert Remote 描述 ├── 注册 mobile_gui_agent 会话视图 └── 注册空白会话 mobile_gui_agent 快捷入口 ``` 规划与回合执行仍由 Harness 现有 Agent 负责。启动 Phone run 后,插件只在该 Agent 范围安装 Phone 工具和专用 system prompt。每次 `phone_act` 会重新获取动作前状态、验证严格 action、解析元素边界、按需审批、通过 `PhoneDevice` 执行、等待画面稳定、再次获取状态、验证结果,再把新观察交回同一 Agent loop。 压缩 hierarchy 会过滤不可见和无意义容器,优先保留文本与交互节点,为元素分配短 ID,并限制元素数和序列化字节数。`tap_element` 同时携带 observation ID;执行前会重新核对目标元素的 resource ID、class、content description 与 bounds。无关动画不会阻塞动作,但目标移动、消失、身份变化或匹配不唯一时仍会按过期观察拒绝。 ## 模型体验 启动 run 后,插件会向 Harness 现有 Agent 增加专用 Phone prompt 和两个 Agent 范围工具。`phone_observe` 返回前台应用、Activity、压缩 hierarchy、模型支持时的截图附件、最近验证步骤和失败上下文。`phone_act` 只接受一个严格 action,并始终返回新的动作后观察。模型不会看到原始 UIAutomator XML,也不能执行任意 ADB shell。 一次 run 内的 prompt 前缀保持稳定,每轮压缩观察随手机画面变化。元素数和序列化字节上限约束 hierarchy 体积;只有选定模型接受图片输入时才使用截图附件。纯文本模型仍可操作 hierarchy 中的控件,但没有 `PhoneVisionProvider` 时无法可靠理解纯图片 UI。 ## 安全模型 - 模型不能提交任意 ADB shell 文本;底层诊断命令使用封闭的 `PhoneShellRequest` 分类。 - `approvalEnabled` 开启时,具有真实副作用的语义控件复用 Harness 审批服务。 - Unicode helper 初始化即使在普通动作审批关闭时也始终要求 Harness 审批服务。 - 审批只允许一次;拒绝、缺少审批 Provider 或取消会成为结构化动作失败。 - 原始坐标动作不一定能识别语义副作用。涉及账号、支付或敏感数据时,应人工核对目标并配置严格的 Harness 权限。 - 所有设备动作都是真实动作。评估时使用测试设备和非生产账号。 安全问题请查看 [SECURITY.md](../SECURITY.md)。 ## 已知限制 - UIAutomator 可能遗漏 Canvas、游戏、纯图片和部分 WebView 控件。 - Unicode 文本输入要求兼容的外部 ADB Keyboard helper。已安装 helper 会无感启用、切换并恢复;安装缺失 helper 仍要求配置 `adb.unicodeImeApkPath` 并显式审批。 - 无线 ADB 的延迟与可靠性取决于网络和当前 Android 调试端口。 - 原始坐标动作不一定能按语义判断影响;应优先使用语义元素并核对审批提示。 - MVP 在观察和动作后刷新截图,不提供 scrcpy 视频流。 ## 开发与测试 ```bash pnpm install pnpm run typecheck pnpm run build pnpm run test pnpm run verify:package pnpm pack ``` 普通测试使用 `FakePhoneDevice`。真实 ADB 集成测试会在未提供设备序列号时自动跳过。只读截图与 hierarchy 检查: ```bash DSH_PHONE_ADB_INTEGRATION_DEVICE=emulator-5554 pnpm run test:adb ``` 会改变设备状态的检查需要另行显式开启: ```bash DSH_PHONE_ADB_INTEGRATION_DEVICE=emulator-5554 DSH_PHONE_ADB_MUTATION_TESTS=1 pnpm run test:adb ``` 变更检查可能执行 Home、Back、tap、swipe 和输入文字,只能对允许这些动作的测试设备运行。 ## 故障排查 ### 看不到 `mobile_gui_agent` 入口 确认插件安装到了当前启动的 Web profile,检查 `--dump-config`,安装后强制刷新浏览器。headless profile 没有浏览器会话视图。空白新会话中,Harness 仍会隐藏常规标签栏,但选择工作区后,输入框工具栏里应显示插件提供的 **mobile_gui_agent** 按钮;点击它即可直接打开任务面板。 ### `device unauthorized` 解锁手机并接受 RSA 授权,再执行 `adb kill-server`、`adb start-server` 和 `adb devices -l`。如果不再弹出授权,可在 Android 中撤销 USB 调试授权后重试。 ### `exec-out screencap -p` 超时 无线 ADB 可能变慢或已断开。先手工运行 `adb -s DEVICE exec-out screencap -p > /tmp/phone.png`,重新连接设备、保持屏幕解锁,并同时调高 `adb.commandTimeoutMs` 与 `agent.actionTimeoutMs`。插件会把超时作为可恢复动作结果返回,不会假定点击成功。 ### hierarchy 为空或不完整 UIAutomator 不会暴露所有 Canvas、WebView、游戏或自定义渲染控件。使用支持视觉输入的模型,刷新截图,尝试滚动或关闭遮罩,也可以提供 `PhoneVisionProvider` 插件。 ### 文本输入不正确 ADB 文本输入对已聚焦的普通文本框最可靠。可打印 ASCII 使用 Android 原生 `input text`;Unicode 通过 ADB Keyboard(`com.android.adbkeyboard/.AdbIME`)接收 UTF-8 Base64 广播。可用以下只读命令检查状态: ```bash adb shell ime list -s adb shell settings get secure enabled_input_methods adb shell settings get secure default_input_method ``` 插件用 `ime list -a -s` 判断是否安装,不会再把“已安装但未启用”的 IME 误判为缺失。ADB Keyboard 已安装时,`input_text` 和 `replace_text` 会按需临时启用/选中它,并在广播完成或取消后恢复原输入法及原启用状态。helper 缺失时,把 `adb.unicodeImeApkPath` 设为你已审查 APK 的绝对路径;Agent 会调用不能绕过 Harness 审批的 `setup_unicode_input`,审批后仅在确实缺失时安装。未配置路径时,初始化返回 `PHONE_UNICODE_INPUT_UNSUPPORTED`,Agent 会停止而不是重试。`replace_text` 会单步更新已聚焦文本框,不再逐字符按 Delete。 ## 许可证 [MIT](../LICENSE) 仓库使用 [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic,供 DSH 社区目录发现。