# 已知问题详解 本文档记录实际调试中发现的问题,按影响程度排序。 --- ## 1. 延迟来源:观测往返 ### 现象 完整任务耗时远超单次操作耗时之和。曾观察到单次调用卡顿 40 秒。 ### 测量 | 项目 | 耗时 | | --- | --- | | 热调用(纯 JavaScript) | 0.09 – 0.14 s | | `Q()` 按文本定位 | 0.11 – 0.14 s | | 单次截图 | ~0.5 s | | 首次绑定标签(冷启动) | 3 – 9 s | | 首次 AX 读取(冷启动) | ~9 s | | 后续 AX 读取 | ~0.2 s | 截图并非瓶颈。瓶颈在于每个操作所需的「截图 → 裁剪 → 分析 → 规划」循环, 单轮约 20 – 40 秒。 一个包含 16 次点击的任务,工具侧总耗时 8.3 秒,实际总耗时 32 秒。 差额来自观测往返。 ### 结论 优化目标是减少往返次数,而非优化单次调用。 --- ## 2. 手动等待与内部等待叠加 ### 现象 间歇性出现 5 – 40 秒的卡顿,无法复现,无错误输出。 ### 原因 官方文档说明观测方法内部已有等待: > "`getAXState()`, `getScreenshot()` and `getAXStateAndScreenshot()` automatically > wait an appropriate amount of time before capturing new state. In order to > complete the task as quickly as possible, don't pause or delay > (ex: `setTimeout(...)`) before getting UI state. Instead, rely on the internal wait." 在观测调用前额外添加 `setTimeout` 会与内部等待叠加。 ### 处理 移除观测前的所有固定等待。唯一例外:等待 CSS 过渡以触发悬停状态时, `setTimeout` 是必要的(见第 4 节)。 --- ## 3. 完整 AX 树与上下文占用 ### 现象 按文本匹配元素时,模型上下文被大量无关内容占满。 ### 原因 文本匹配需要完整 AX 树,因此必须使用 `disableDiffing: true`。 但该调用默认会将整棵树写入工具结果。 ### 处理 ```js const L = String(await app.getAXState({ disableDiffing: true, emit: false })).split("\n"); ``` `emit: false` 抑制结果输出,仅返回字符串供内部匹配。 实测对比: | 方式 | 输出大小 | | --- | --- | | 默认差分(会 emit) | ~401 字节 | | 完整树(会 emit) | 300+ 行 | | 完整树 + `emit: false` | 59 字节 | 注意:首次使用某 API 时,官方文档仍会输出一次,这属于预期行为。 --- ## 4. 仅在悬停时出现的控件 ### 现象 操作按钮(如"删除")在界面上不可见,AX 树中也无法找到。 ### 原因 按钮使用 `display: none`,仅在指针悬停时显示。未显示的元素不会进入 AX 树。 ### 处理 使用拖拽使指针停留在目标元素上: ```js await tab.drag([cardX + 200, rowY], [cardX - 50, rowY]); await new Promise(r => setTimeout(r, 900)); // 等待 CSS 过渡 const hits = await Q(/link 删除/); await tab.click(hits[0].n); ``` 原理:拖拽的终点即指针最终位置;由于 mousedown 与 mouseup 不在同一元素上, 不会触发点击事件。 --- ## 5. 坐标偏移 ### 现象 按截图计算出的坐标点击后,落点偏离目标。 ### 原因 `Vec2` 使用的坐标空间与截图像素之间存在未文档化的偏移。 本机实测约为 `+46, +40`(物理像素)。 ### 标定方法 ```js // 1. 点击一个无副作用的位置 await app.click([1432, 980]); // 2. 截图确认指针实际落点 await app.getScreenshot(); // 3. 差值为偏移量,后续所有坐标减去该偏移 ``` 偏移量可能因显示器缩放、窗口位置而变化,建议在每次会话开始时重新标定。 --- ## 6. 弹窗按钮合并为单一文本节点 ### 现象 确认弹窗中「取消」与「确认」两个按钮在 AX 树中显示为一个文本节点: ``` 290 text 取消 确认 ``` 无法通过元素索引单独点击。 ### 处理 使用标定后的坐标点击,或先截图量取按钮中心位置。 键盘方式不可靠:Tab 焦点可能落在背景页面而非弹窗内。 --- ## 7. `--shot` 的截图目标 ### 现象 截取的画面与被操作的标签不一致,且连续多次截图返回内容完全相同。 ### 原因 `--shot` 默认截取窗口的当前活动标签。当被操作的标签位于后台时, 截取的是其他标签的画面。 该问题不会产生任何错误输出,调用方无法从返回值中察觉。 ### 处理 ```js globalThis.CUATAB = await cua.getTab(tabId, { browser: "1" }); ``` `--shot` 优先截取 `CUATAB` 指向的标签。 ### 自检 比较连续两次截图的 SHA-256。若完全相同,说明画面未更新,应检查绑定是否正确。 --- ## 8. 自绘控件与文本匹配 ### 现象 对 canvas 或自绘控件使用文本匹配定位,点击被记录(如步数增加)但界面状态未变化。 ### 原因 文本匹配可能命中容器元素而非实际按钮,事件被记录但未触达目标。 ### 处理 改用元素索引: ```js await tab.playwright.locator("button").nth(index).click(); ``` 实测:文本匹配方式 4 次点击后计数变为 26 且画面无变化; 索引方式 1 次点击对应 1 次状态变化。 --- ## 9. 站点反自动化 ### 现象 标签可正常绑定,但 `playwright.evaluate` 与 `domSnapshot` 持续返回 `Debugger unattached`。 ### 原因 部分站点会主动断开 CDP 调试器连接。 ### 处理 退回原生 AX 通道(`cua.getApp`)。该通道不依赖 CDP,不受此影响。 ### 补充观测 同一页面下: - 指针在浏览器窗口内移动 → 调试器断开 - 指针在窗口外移动 → 不受影响 - 指针静止 → 不受影响 此外,此类站点在自动化创建的标签页中可能长时间停留在加载状态, 使用浏览器自身创建的标签页可正常渲染。 --- ## 10. 结果未知时不要重放 ### 现象 提交类操作(点击提交、确认删除)后连接中断,无法确认是否成功。 ### 处理 不应自动重放。应先重新观察界面状态: ```js const state = await app.getAXState(); // 根据实际状态判断:已完成 / 未开始 / 进行中 ``` 若界面提供了「检查状态」入口,应优先使用,而非再次提交。 重放写操作可能导致重复提交。