From 6ec036cf870b6ea7a3476ccea3b9dcea52e9b113 Mon Sep 17 00:00:00 2001 From: blakcat <982539845@qq.com> Date: Mon, 3 Aug 2026 01:47:22 +0800 Subject: [PATCH] docs: distinguish iOS Agent from legacy WDA --- docs/AGENT_RULES.md | 131 +++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 56 deletions(-) diff --git a/docs/AGENT_RULES.md b/docs/AGENT_RULES.md index 94cfb01..9a34a6f 100644 --- a/docs/AGENT_RULES.md +++ b/docs/AGENT_RULES.md @@ -17,24 +17,26 @@ AScript 脚本工程师助手。AScript 是 Python 跨平台移动端自动化 **进入 ③ UI 之前**(平台 + run_mode 分流): - Android `accessibility` / `root` / `hid`:Selector > Ocr / FindImages - Android **`screen_only`**:**无控件树,Selector / dump_ui_tree 一律不可用**,只能 `screen_capture` + Ocr / FindImages / FindColors -- iOS:Ocr / FindImages > Selector(WDA dump 不稳,见 §3.2.1) +- iOS Agent no-WDA:语义明确的控件优先 Selector;文字 / 图像 / 色块目标分别用 Ocr / FindImages / FindColors +- iOS legacy WDA:Ocr / FindImages > Selector(避免反复 WDA XML,见 §3.2.2) **进 ③ 前的硬性确认**: - `run_mode` = `accessibility` / `root` → 直接 `action.click` -- 其他(`hid` / `screen_only` / iOS / Windows)→ **必问点击通道**(§3.3,id=103 插件) +- Android `hid` / `screen_only`、iOS 明确需要物理级输入或软件通道受限、Windows → **必问点击通道**(§3.3,id=103 插件) +- 已确认 iOS Agent 可用且只需正常点击 / 输入 → 直接使用 Agent API,不强制询问 HID **关键决策步**(click / 输入 / 提交)前 `preconditions_pass()` 检查;**辅助步**(滑动 / sleep)不必。 **eval_python 红线**(§3.5):无 `while True` / `sleep ≤ 5s` / 总预算 ≤ 30s / globals 共享(不重 import)。 -**Token 经济**(§2.3.1):Android(有控件树)默认 dump、`screen_only` 默认截图、iOS 默认截图;`Ocr/FindImages` 自带读屏,**不要每步给 AI 截图**。 +**Token 经济**(§2.3.1):Android(有控件树)默认 dump、`screen_only` 默认截图、iOS Agent 优先精确 Selector、legacy WDA 默认截图;`Ocr/FindImages` 自带读屏,**不要每步给 AI 截图**。 **反模式 Top 6**(详见 §六): - 凭视觉截图猜 id/desc → §4.7 - 单 `text` selector → §4.4 - 裸坐标 `action.click` → §1.3 - `screen_only` 直接 `action.click` → §3.3 -- iOS 反复 `dump_ui_tree` → §3.2.1 +- iOS legacy WDA 反复 `dump_ui_tree` → §3.2.2 - 写 `if __name__ == "__main__":` → §3.4 --- @@ -79,7 +81,8 @@ AScript 脚本工程师助手。AScript 是 Python 跨平台移动端自动化 无障碍全局动作(GLOBAL_ACTION_HOME 等)/ Shizuku 系统服务调用 iOS: URL Scheme(具体可用性视 iOS 版本)/ - WDA 设备级方法 / 快捷指令深链 + Agent 系统 API / 快捷指令深链; + legacy WDA 环境可用 WDA 设备级方法 探索:search_api(keyword="intent"/"broadcast"/"url scheme"...) 或直接问用户"有没有可用的间接调用方式" @@ -89,11 +92,13 @@ AScript 脚本工程师助手。AScript 是 Python 跨平台移动端自动化 有稳定文字 → Ocr / Ocr.click 有可视特征 → FindImages(裁模板,见 §五) 是颜色块 → FindColors / FindBlock - iOS(优先图色,WDA dump 不稳定,详见 §3.2.1): - 有稳定文字 → Ocr.click - 有可视特征 → FindImages - 是颜色块 → FindColors - 上述都没辙 → Selector(兜底,慎用) + iOS Agent no-WDA(详见 §3.2.1): + 有稳定 label/name/type → Selector + 有稳定文字 → Ocr / Ocr.click + 有可视特征 → FindImages + 是颜色块 → FindColors + iOS legacy WDA(详见 §3.2.2): + Ocr / FindImages / FindColors > Selector ``` #### 1.2.1 高频动作 → 先 ①,不要走 UI @@ -106,8 +111,8 @@ AScript 脚本工程师助手。AScript 是 Python 跨平台移动端自动化 | 浏览器 URL / 深链 | `browser` / `scheme` | `system.browser(url)` | `system.scheme_start(scheme)` | | 跳到 App 系统设置页 | `setting` / `open_app_setting` | `system.open_app_setting(pkg)` | — | | 系统按键(HOME/BACK/RECENTS/锁屏/截屏) | `Key` | `action.Key.home()` / `back()` / `recents()` / `lockscreen()` / `screenshot()` | (走 HID,见 §3.3) | -| 模拟文本输入 | `input` | `action.input(msg, selector=None)` | 走 `ime` / WDA | -| 剪贴板读写 | `Clipboard` | `system.Clipboard.put(msg)` / `get()` | — | +| 模拟文本输入 | `input` | `action.input(msg, selector=None)` | Agent:`Selector().input(msg).find()`;legacy WDA 可走 `ime` / WDA | +| 剪贴板读写 | `Clipboard` | `system.Clipboard.put(msg)` / `get()` | `system.set_clipboard(msg)` / `get_clipboard()` | | 设备信息 / 亮度 / 亮屏 / 电量 | `Device` | `Device.battery` / `Device.set_brightness(v)` / `Device.wake_up()` | `system.info()` / `get_ios_version()` | | 等包启动 / 拿前台 App | `wait_for_package` / `foreground` | `system.wait_for_package(pkg, timeout)` / `system.get_foreground_app()` | — | | 监听按键 / 通知 / 触摸(常驻) | `event` / `KeyEvent` | `event.KeyEvent.on(...)` / `NotificationEvent.on(...)` / `TouchEvent.on(...)` | — | @@ -116,9 +121,9 @@ AScript 脚本工程师助手。AScript 是 Python 跨平台移动端自动化 | 持久化键值 | `KeyValue` | `system.KeyValue.save/get/remove` | `system.KeyValue.save/get` | **反模式**:看到"打开 App"就 dump 桌面找图标点击 → `system.open` 一行解决。 -**iOS 短板**:iOS 不暴露 `shell` / 系统按键 / 剪贴板 / 事件监听,这些动作要么走 HID(§3.3)要么问用户。 +**iOS 短板**:iOS 不暴露 `shell` / 通用系统按键 / 事件监听;需要物理按键或软件通道受限时走 HID(§3.3),不要把正常 Agent 点击 / 输入也强制转成 HID。 -进入 ③ 之前 §3.3 "点击通道"必问一次。 +进入 ③ 之前先按 §3.2 确认 iOS 通道;只有命中 §3.3 的物理输入 / 软件通道受限条件时才询问点击通道。 ### 1.3 前置条件 — 动作是被条件触发的,不是顺序跑 @@ -180,7 +185,7 @@ def safe_action(): _result = json.dumps(safe_action()) ``` -> iOS Selector 写法不同(无 mode),且应优先 Ocr/FindImages,见 §3.2.1。 +> iOS Selector 的**查找模式**与属性的**匹配模式**是两套概念,不能套用 Android mode;见 §3.2。 跨多步流程:**每个关键决策步前都重新检查**,不要假设上一步真的成功(弹窗、网络异常都可能让"上一步"实际未生效)。 @@ -217,7 +222,7 @@ _result = json.dumps(safe_action()) | 工具 | 用途 | 何时用 / 陷阱 | |---|---|---| | `dump_ui_tree(mode=)` | 控件树 | Android 主力。**mode 必须和 `Selector(mode=)` 一致**(见 §3.1) | -| `screen_capture()` | 截屏 | iOS 主力(WDA dump 不稳);Android 用于裁模板 / 收尾给用户看 | +| `screen_capture()` | 截屏 | iOS 视觉目标和 legacy WDA 主力;Android 用于裁模板 / 收尾给用户看 | | `test_selector(...)` | 测试 selector 命中 | 写完 selector 立刻验,免得 dump 几轮才发现错 | | `ocr(...)` | 屏幕 OCR | 直接读屏文字,**无需先 screen_capture** | | `find_colors(...)` / `compare_colors(...)` | 多点找色 / 比色 | 颜色块目标(HP/CD/Buff)主力 | @@ -230,9 +235,10 @@ _result = json.dumps(safe_action()) |---|---|---| | Android `accessibility` / `root` / `hid` | `dump_ui_tree` | 有控件树,首选 dump | | Android **`screen_only`** | **`screen_capture`** | **无控件树,Selector / dump_ui_tree 一律不可用,只能截图 + OCR / 找图 / 找色** | -| iOS | `screen_capture` | WDA dump 反复调可能让 App 卡死,详见 §3.2.1 | +| iOS Agent no-WDA | 精确 `Selector` | 目标有稳定 label/name/type 时优先;视觉目标再截图,详见 §3.2.1 | +| iOS legacy WDA | `screen_capture` | 避免反复拉取 WDA XML,详见 §3.2.2 | -Android 有控件树场景下,只在以下情况补 `screen_capture`:dump 拿到但缺关键属性(无 id/text/desc 的纯图标) / 需视觉理解(裁模板、判断动画) / 任务收尾给用户看。 +Android 有控件树或 iOS Agent 场景下,只在以下情况补 `screen_capture`:控件检索缺关键属性 / 目标是纯图标或颜色 / 需视觉理解(裁模板、判断动画) / 任务收尾给用户看。 **通用铁律**:`Ocr.click` / `FindImages.find` 是**设备端自带读屏**,运行时不需要 AI 先 `screen_capture` 一遍。**为每步动作给 AI 截图 = 双倍 token 浪费**。 @@ -292,60 +298,73 @@ Android 有控件树场景下,只在以下情况补 `screen_capture`:dump 拿到 - 不调 `get_device_status` 直接 `dump_ui_tree()`(默认 mode=0)在 root/hid 设备上必拿空树 - `code="hid"` 是辅助控件**有控件树**;`code="screen_only"` 才是真没控件树 -### 3.2 iOS Selector 与 Android 完全不同 +### 3.2 iOS Agent no-WDA 与 legacy WDA 必须分开 -iOS 只有 **WebDriverAgent (WDA)** 一套引擎,不分 mode。 +iOS 当前主推 **Agent no-WDA**(iOS 15+),legacy WDA 仅作为兼容通道。不要再把所有 iOS 任务都按 WDA 处理。官方入口: -- `Selector()` **不接** mode 参数 -- `dump_ui_tree` 在 iOS 上**不传 mode**,返回 WDA XML +- [iOS Agent 介绍](https://ascript.cn/docs/ios/intro/) +- [Selector API](https://ascript.cn/docs/ios/api/node/selector/) +- [Agent 激活说明](https://ascript.cn/docs/ios/download/wda-mode/) -iOS 的 `MODE_*` 是给**单条件**用的匹配运算符: +Agent 激活页显示 `Automation Running` 才算激活成功。运行时内部仍可能出现 `wdapy`、`AppiumClient`、`wda_selector` 等历史兼容命名,**这些名字不能单独证明正在走 legacy WDA**。应结合激活状态、公开 Selector 能力和一次只读查询判断;能力探针不得点击或输入。 -| 常量 | 含义 | -|---|---| -| `Selector.MODE_EQUAL` (0) | 完全相等(默认) | -| `Selector.MODE_CONTAINS` (1) | 包含 | -| `Selector.MODE_MATCHES` (2) | 正则 | -| `Selector.MODE_GREATER` (3) | 数值大于 | -| `Selector.MODE_LESS` (4) | 数值小于 | +iOS Selector 有两套不同的 mode: + +1. **构造器查找模式**:`Selector.SIMPLE`(默认智能)、`Selector.POINT`(坐标点)、`Selector.COMPLEX`(多窗口 / 浮层)、`Selector.VISIBLE`(可见控件 / 大列表)。 +2. **属性匹配模式**:`MODE_EQUAL`、`MODE_CONTAINS`、`MODE_MATCHES`、`MODE_GREATER`、`MODE_LESS`。 ```python from ascript.ios.node import Selector -Selector().text("登录").find() # 完全匹配(默认) -Selector().text("登录", mode=Selector.MODE_CONTAINS).find() # 包含 -Selector().text(r"登录\d+", mode=Selector.MODE_MATCHES).find() # 正则 +# 构造器查找模式 +login = Selector(Selector.SIMPLE).label("登录").type( + "XCUIElementTypeButton" +).find(timeout=1200) +visible_buttons = Selector(Selector.VISIBLE).type( + "XCUIElementTypeButton" +).find_all() +point_node = Selector(Selector.POINT, xy=(300, 600)).find() + +# 属性匹配模式 +contains_login = Selector().label( + "登录", + mode=Selector.MODE_CONTAINS, +).find() ``` -iOS 还有点击 / 滑动专用 mode:`MODE_CLICK_ACCESS` / `MODE_CLICK_XY`、`MODE_SCROLL_VISIBLE/LEFT/RIGHT/UP/DOWN`。 - **反模式**: -- ❌ `Selector(mode=2)` —— iOS Selector 不接 mode -- ❌ `dump_ui_tree(mode=9)` —— iOS 不识别 -- ❌ 把 Android 的 `MODE_ACC_*` 拿来 iOS 用 —— 没这个常量 +- ❌ 用内部类名判断 Agent / WDA —— 兼容层可能保留历史命名 +- ❌ 把查找模式与属性匹配模式混为一谈 +- ❌ 把 Android 的 `MODE_ACC_*` 或数字 mode 直接套给 iOS +- ❌ `dump_ui_tree(mode=9)` —— Android 的 mode 不能跨平台使用 + +官网给出的简单页面控件检索约 15ms、最快截图约 50ms,来自特定设备、系统和页面,**是参考基准而非 SLA**。验收应在目标设备记录命中率和 p50/p95,不能凭一次耗时判定 Agent 是否生效。 **跨平台路径转译**:`eval_python` 自动把 `ascript.android.*` → `ascript.ios.*`(且预加载 `cv2`/`numpy`/`Pillow`),片段两端通常都跑得了 —— **但 mode/Selector 的逻辑差异不会自动转,平台分流要你自己写**。 -#### 3.2.1 iOS 默认走图色,控件检索是兜底 +#### 3.2.1 Agent no-WDA 路径 + +语义明确的控件优先精确 Selector,不主动拉全量 XML。稳定文字 / 图片 / 颜色目标仍按目标特征使用 Ocr / FindImages / FindColors。正常点击和输入直接使用 Agent API,无需为了 iOS 身份强制外接 HID。 + +#### 3.2.2 legacy WDA 兼容路径 -iOS 控件基于 WDA,**反复 dump 可能让 App 卡死或崩溃**。路径优先级**与 Android 相反**:`Ocr / FindImages / FindColors > Selector`(Selector 仅"高确定性、单次关键步骤、其他都解不掉"时用)。 +只有明确处于 legacy WDA 环境时,才应用“图色优先、避免反复 WDA XML”的经验: | 动作 | 做 | 不做 | |---|---|---| | 观察 | `screen_capture` | 反复 `dump_ui_tree` | | 操作 | `Ocr.click(...)` / `FindImages.find(...)` | 操作前再 `screen_capture`(同 §2.3.1) | -**实战节奏**:开头 1 次 capture 看起点 → N 次 `Ocr.click` / `FindImages.find` → 收尾 1 次 capture 看效果。 +**实战节奏**:开头 1 次 capture 看起点 → N 次 `Ocr.click` / `FindImages.find` → 收尾 1 次 capture 看效果。WDA 设备级方法也只归到这个兼容通道。 -### 3.3 图色 / HID 模式必须先问点击通道(强制对话规则) +### 3.3 只有需要物理输入或软件通道受限时才问 HID **何时必问**(以下任一,与 §1.3 的"前置条件检查"无关,这是接到任务时的 1 次性确认): - Android `run_mode.code` 是 `hid` 或 `screen_only`(`get_device_status()` 返回值) -- 平台是 iOS(WDA click 常被 App 检测/拦截,稳妥起见走外接 HID) +- iOS 用户明确要求物理级输入、目标 App 拦截软件输入、Agent 未激活 / 不可用,或 legacy WDA 无法完成动作 - 平台是 Windows(暂缺,如遇此情况让用户提供方案) -⚠ 设备本身**没有原生点击能力**。AI 写 `action.click(x, y)` 会**静默失败或报错**。 -**必须先和用户对话确认点击通道,拿到答案前不写任何 click 调用。** +已确认 iOS Agent 可用且任务只是正常点击 / 输入时,直接使用 Agent API,**不得卡住任务反复追问 HID**。只有命中上述条件时,才在写 click / input 前确认通道。 第一句必问: @@ -390,7 +409,7 @@ eval_python 跑在 App 主进程主线程,几百毫秒一轮。硬约束: ## 四、Selector 写法手册 > 本章主要服务 **Android**(走 §1.2 ③ 控件路径时)。 -> **iOS 仅在 §3.2.1 例外条件下使用 Selector**(高确定性、单次、不可重复 dump)。 +> iOS Agent no-WDA 同样优先精确 Selector;legacy WDA 才按 §3.2.2 走视觉优先并避免反复 XML。 ### 4.1 核心原则:特异性 >> 数量 @@ -504,7 +523,7 @@ _result = json.dumps({ | `find(selector)` | 在当前子树里继续查(缩小范围,避免全局误匹配) | | `find_all(selector)` | 子树里查全部 | -**写 selector 前必先 `dump_ui_tree`** —— 从输出读 text/desc/id/className 真实值,**禁止凭视觉截图猜**(截图看不到 id 和 desc)。 +**Android 写 selector 前先 `dump_ui_tree`** —— 从输出读 text/desc/id/className 真实值,**禁止凭视觉截图猜**(截图看不到 id 和 desc)。iOS Agent 应使用控件查看器或精确、只读 Selector 探针获取 label/name/type,不要为了取属性主动拉全量 XML。 --- @@ -551,12 +570,12 @@ _result = json.dumps({ | 裸坐标 `action.click(540, 1800)` 没特征验证 | §1.3 | | 用 `time.sleep` 等界面切换(应改用 wait_for / OCR 锚点) | §1.3 | | 一次写 50 行直接 upload+run(没 eval 验证) | §2.4 | -| `Ocr.click` / `FindImages.find` 之前还每步 `screen_capture` | §2.3.1 §3.2.1 | -| iOS 反复 `dump_ui_tree` 做逐步观察(WDA 卡死风险) | §3.2.1 | +| `Ocr.click` / `FindImages.find` 之前还每步 `screen_capture` | §2.3.1 §3.2.2 | +| iOS legacy WDA 反复 `dump_ui_tree` 做逐步观察(WDA 卡死风险) | §3.2.2 | | `dump_ui_tree(mode=)` 和 `Selector(mode=)` 不一致 | §3.1 | | 在 root / hid 模式下 dump 默认 mode=0(必拿空树) | §3.1 | -| `Selector(mode=2)` 在 iOS 上(iOS Selector 不接 mode) | §3.2 | -| screen_only / hid / iOS 直接写 `action.click` 不问点击通道 | §3.3 | +| 混淆 iOS Selector 查找模式与属性匹配模式,或套用 Android mode | §3.2 | +| screen_only / hid / 需要物理输入的 iOS / Windows 不问点击通道 | §3.3 | | 写 `if __name__ == "__main__":`(永远不执行) | §3.4 | | 用 `sys.argv` / `argparse`(脚本不是命令行启动) | §3.4 | | 工程入口起名 `main.py` / `app.py` / `run.py` | §3.4 | @@ -576,14 +595,14 @@ _result = json.dumps({ | | Android | iOS | |---|---|---| | 命名空间 | `ascript.android.*` | `ascript.ios.*` | -| 控件引擎 | 无障碍 / Shizuku / Root | WebDriverAgent (WDA) | -| 控件检索 | `dump + Selector(mode=)` | `dump + Selector()`(无 mode) | -| **③ UI 路径优先** | Selector > Ocr / FindImages | **Ocr / FindImages > Selector**(WDA 不稳) | -| 间接 API 通道 | Intent / Broadcast / ContentProvider / 无障碍全局 | URL Scheme / WDA 设备级方法 / 快捷指令 | +| 控件引擎 | 无障碍 / Shizuku / Root | **Agent no-WDA(主推)** / legacy WDA(兼容) | +| 控件检索 | `dump + Selector(mode=)` | Agent:精确 Selector(含 SIMPLE/POINT/COMPLEX/VISIBLE);legacy WDA:避免反复 XML | +| **③ UI 路径优先** | Selector > Ocr / FindImages | Agent:Selector 或按目标特征走视觉;legacy WDA:Ocr / FindImages > Selector | +| 间接 API 通道 | Intent / Broadcast / ContentProvider / 无障碍全局 | Agent 系统 API / URL Scheme / 快捷指令;legacy WDA 设备级方法 | | `get_device_status` | ✓ | ✗ | | `run_project_debug` | ✓(USB + VS Code 断点) | ✗ | | `eval_python` | ✓ | ✓(自动转译 `android.*` → `ios.*`) | -| 硬件 HID | `screen_only` / `hid` 时必配 | **必配**(WDA click 易被 App 检测/拦截),见 §3.3 | +| 硬件 HID | `screen_only` / `hid` 时必配 | 仅物理输入、软件通道受限或 Agent 不可用时按需,见 §3.3 | > Windows 暂缺(命名空间 `ascript.windows.*`,以后补)。