Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 75 additions & 56 deletions docs/AGENT_RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

---
Expand Down Expand Up @@ -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"...)
或直接问用户"有没有可用的间接调用方式"

Expand All @@ -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
Expand All @@ -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(...)` | — |
Expand All @@ -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 前置条件 — 动作是被条件触发的,不是顺序跑

Expand Down Expand Up @@ -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。

跨多步流程:**每个关键决策步前都重新检查**,不要假设上一步真的成功(弹窗、网络异常都可能让"上一步"实际未生效)。

Expand Down Expand Up @@ -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)主力 |
Expand All @@ -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 浪费**。

Expand Down Expand Up @@ -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 前确认通道。

第一句必问:

Expand Down Expand Up @@ -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 核心原则:特异性 >> 数量

Expand Down Expand Up @@ -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

---

Expand Down Expand Up @@ -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 |
Expand All @@ -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.*`,以后补)。

Expand Down