android-midscene-automation 0.1.41 → 0.1.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/AI_RECOGNITION_GUIDE.md +370 -0
  2. package/CHANGELOG.md +16 -0
  3. package/README.md +35 -3
  4. package/USAGE.md +18 -3
  5. package/package.json +2 -1
  6. package/remote-agent/index.ts +1 -0
  7. package/server/appium-auto-save.test.ts +61 -0
  8. package/server/appium-config.test.ts +165 -0
  9. package/server/appium-recorder/ai-deduplication.test.ts +80 -0
  10. package/server/appium-recorder/ai-deduplication.ts +74 -0
  11. package/server/appium-recorder/ai-observation.test.ts +125 -0
  12. package/server/appium-recorder/ai-observation.ts +125 -0
  13. package/server/appium-recorder/ai-recognition.ts +118 -22
  14. package/server/appium-recorder/appium-runner.ts +79 -14
  15. package/server/appium-recorder/bounded-loop.test.ts +55 -0
  16. package/server/appium-recorder/bounded-loop.ts +12 -1
  17. package/server/appium-recorder/history-capture.ts +1 -0
  18. package/server/appium-recorder/image-check.ts +1 -1
  19. package/server/appium-recorder/managed-appium.ts +15 -0
  20. package/server/appium-recorder/managed-cleanup.test.ts +82 -0
  21. package/server/appium-recorder/prompt-optimizer.ts +57 -0
  22. package/server/appium-recorder/replay-log.ts +13 -0
  23. package/server/appium-recorder/report-summary.test.ts +78 -0
  24. package/server/appium-recorder/report-summary.ts +60 -0
  25. package/server/appium-recorder/report.ts +45 -14
  26. package/server/appium-recorder/repository.ts +7 -0
  27. package/server/appium-recorder/routes.ts +78 -4
  28. package/server/appium-recorder/screenshot.ts +1 -0
  29. package/server/config.ts +34 -4
  30. package/server/http-api.ts +31 -5
  31. package/server/model-tester.ts +2 -1
  32. package/src/App.vue +53 -30
  33. package/src/api.ts +11 -4
  34. package/src/appium-recorder/AppiumPage.vue +167 -68
  35. package/src/appium-recorder/RunHistoryDialog.vue +1 -1
  36. package/src/appium-recorder/action-descriptions.ts +2 -1
  37. package/src/appium-recorder/ai-deduplication.ts +34 -0
  38. package/src/appium-recorder/ai-prompt-presets.ts +34 -0
  39. package/src/appium-recorder/ai-recognition.ts +27 -2
  40. package/src/appium-recorder/api.ts +36 -2
  41. package/src/appium-recorder/bounded-loop.ts +9 -1
  42. package/src/appium-recorder/components/AiRecognitionPromptInput.vue +41 -0
  43. package/src/appium-recorder/components/AiRecognitionSettings.vue +167 -0
  44. package/src/appium-recorder/components/AiRecognitionTestDialog.vue +86 -0
  45. package/src/appium-recorder/components/BreakLoopSettings.vue +19 -3
  46. package/src/appium-recorder/components/ComponentTree.vue +5 -5
  47. package/src/appium-recorder/components/CoordinatePointPicker.vue +23 -0
  48. package/src/appium-recorder/components/FlowActionMenu.vue +50 -20
  49. package/src/appium-recorder/components/FlowCanvas.vue +3 -2
  50. package/src/appium-recorder/components/FlowNodeCard.vue +2 -2
  51. package/src/appium-recorder/components/FlowStepEditor.vue +17 -3
  52. package/src/appium-recorder/components/NestedConditionBranches.vue +25 -50
  53. package/src/appium-recorder/components/RecordedSteps.vue +23 -6
  54. package/src/appium-recorder/coordinate-preview.ts +37 -0
  55. package/src/appium-recorder/flow-appearance.ts +12 -1
  56. package/src/appium-recorder/flow-copy.ts +1 -0
  57. package/src/appium-recorder/flow-graph.ts +7 -4
  58. package/src/appium-recorder/flow-labels.ts +9 -5
  59. package/src/appium-recorder/flow-merge.ts +1 -1
  60. package/src/appium-recorder/flow-unmerge.ts +2 -2
  61. package/src/appium-recorder/report-summary.ts +60 -0
  62. package/src/appium-recorder/types.ts +6 -0
  63. package/src/components/config/AiDeduplicationSettings.vue +60 -0
  64. package/src/components/config/AiPromptPresetsSettings.vue +63 -0
  65. package/src/components/config/AppiumConfigPanel.vue +76 -3
  66. package/src/components/config/AppiumModelSettings.vue +45 -0
  67. package/src/components/config/BaseConfigPanel.vue +20 -8
  68. package/src/components/config/FlowAppearanceSettings.vue +51 -32
  69. package/src/components/config/MidsceneConfigPanel.vue +84 -86
  70. package/src/components/config/ReplayReportSummarySettings.vue +118 -0
  71. package/src/components/device/DevicePreviewPanel.vue +18 -3
  72. package/src/config/midscene-model-presets.ts +37 -9
  73. package/src/config/use-appium-auto-save.ts +62 -0
  74. package/src/pages/ConfigPage.vue +8 -6
  75. package/src/style.css +5 -55
  76. package/src/types.ts +4 -1
  77. package/src/ui-refresh.css +17 -4
@@ -0,0 +1,370 @@
1
+ # AI 识别详细使用说明
2
+
3
+ 本文针对本项目 **Appium 流程中的“AI 识别”节点**,说明模型配置、预设场景、单帧识别、持续观察、提前结束、分支、截图去重和超时处理。它与“Midscene 配置”中的模型配置相互独立。
4
+
5
+ 文中的场景参数是调试起点,请用实际设备、页面和模型验证后调整。
6
+
7
+ ## 1. 先根据目标选择配置
8
+
9
+ | 你的目标 | 识别方式 | 观察模式 | 启用分支 | 建议起始配置 |
10
+ | --- | --- | --- | --- | --- |
11
+ | 判断当前稳定画面有没有广告 | 单帧识别 | 不适用 | 开启 | 一张截图判断,true / false 走不同流程 |
12
+ | 开屏广告出现时间不固定,出现后尽快执行下一步 | 持续观察 | 检测命中后提前结束 | 需要区分有无广告时开启 | 最长 15000ms,间隔 1000ms |
13
+ | 统计观察期间有没有出现过广告,优先减少请求次数 | 持续观察 | 观察结束后统一分析 | 开启 | 15000ms,间隔 1000ms |
14
+ | 提取广告标题、产品名称、按钮文字 | 单帧识别,或持续观察 | 持续观察时选统一分析 | 关闭 | 稳定画面用单帧;短暂广告用 10000ms / 1000ms |
15
+ | 等待视频、图片或预览区域出图 | 持续观察 | 检测命中后提前结束 | 建议开启 | 最长 20000ms,间隔 1000ms |
16
+ | 等待“跳过”或“关闭”按钮出现 | 持续观察 | 检测命中后提前结束 | 建议开启 | 最长 10000ms,间隔 500ms |
17
+ | 检查当前画面是否有网络异常、加载失败提示 | 单帧识别 | 不适用 | 开启 | 页面仍在变化时再改成持续观察 |
18
+ | 记录页面描述,只需要模型回答 | 单帧识别 | 不适用 | 关闭 | 使用开放式提示词,回答写入回放日志 |
19
+ | 判断纯黑屏、固定模板、颜色占比等明确图像特征 | 优先考虑“图像判断”节点 | 按对应判断功能设置 | 按需求 | 这类需求通常无需语言模型理解 |
20
+
21
+ 选择的关键是:**只看现在用单帧;需要等待或捕捉短暂事件用持续观察;需要命中后尽快继续才选提前结束。**
22
+
23
+ ## 2. 配置 AI 识别模型
24
+
25
+ 入口:**参数配置 → Appium 配置 → AI 识别配置 → 模型连接**。
26
+
27
+ 1. 填写模型服务的 `Base URL`。
28
+ 2. 填写 `API Key`。
29
+ 3. 填写实际可调用的 `Model Name`。
30
+ 4. 点击 **测试并保存**。
31
+
32
+ 测试通过才保存当前模型,失败时保留之前的模型配置。只修改输入框不会自动保存模型。截图去重和预设提示词等其他 Appium 配置仍按修改自动保存。
33
+
34
+ 模型需要支持图片输入;持续观察还需要支持同一请求中的多张图片。当前实现使用兼容 OpenAI Chat Completions 的请求格式。
35
+
36
+ **“测试并保存”目前验证的是文本连接请求,不能代替图片识别测试。** 配置成功后,应在 AI 节点上执行一次“测试 AI 识别”,检查真实设备截图能否被模型正确识别。持续观察还应单独测试多图请求。
37
+
38
+ AI 识别读取设备实际截图,不依赖组件树来理解内容。设备预览只是观察界面,不能以预览显示正常代替实际截图和模型调用测试。
39
+
40
+ ## 3. 管理预设测试场景
41
+
42
+ 入口:**参数配置 → Appium 配置 → AI 识别配置 → 预设提示词**。
43
+
44
+ 每条预设包含两个部分:
45
+
46
+ | 字段 | 用途 | 示例 |
47
+ | --- | --- | --- |
48
+ | 测试场景 | 下拉选择时显示的名称 | 开屏广告检测 |
49
+ | 提示词内容 | 实际发给模型的识别要求 | 当前画面是否出现广告?升级弹窗和通知弹窗不属于广告。 |
50
+
51
+ 填写后点击“添加提示词”。右侧列表显示场景名称和内容,可删除不再需要的预设。列表修改后自动保存。
52
+
53
+ 内置场景包括出图检测、广告及跳过关闭按钮检测、广告内容提取、异常提示检测。旧版纯文本预设会保留内容并补上场景名称;删除全部预设后不会在刷新时重新恢复。
54
+
55
+ 在新建 AI 节点或编辑“识别内容 / 命中条件”时:
56
+
57
+ 1. 打开“选择测试场景”下拉框。
58
+ 2. 按场景名称搜索或选择。
59
+ 3. 鼠标悬停在场景上查看完整提示词。
60
+ 4. 选中后,提示词填入下方输入框,可继续修改。
61
+
62
+ **选择场景只填入提示词,不会自动改变识别方式、观察模式、超时或分支配置。**
63
+
64
+ 节点保存的是独立的提示词文本。删除预设不会清空已有节点,调整预设也不会自动改写已有脚本。场景名称最长 80 字,提示词最长 4000 字;重复场景名称或重复内容不能再次添加。
65
+
66
+ ## 4. 新建和保存 AI 节点
67
+
68
+ 1. 在 Appium 流程中选择插入位置。
69
+ 2. 选择 **视觉识别 → AI 识别**。
70
+ 3. 选择测试场景,或手动填写识别内容。
71
+ 4. 添加节点后,配置识别方式、观察模式和分支。
72
+ 5. 对需要判断条件的节点,完成“验证并保存问题”。
73
+ 6. 保存脚本,再进行回放。
74
+
75
+ 新建 AI 节点默认使用单帧识别、不启用分支。需要 true / false 路径时主动勾选“启用分支”,并连接两个方向的后续节点。
76
+
77
+ ## 5. 识别方式:单帧与持续观察
78
+
79
+ ### 单帧识别
80
+
81
+ 节点执行时取得一张设备截图,再交给模型分析。
82
+
83
+ 适合已经稳定的页面,例如检查当前页面是否有弹窗、读取当前广告文字、描述当前画面。
84
+
85
+ 它不能看到截图前已经消失的内容,也不会等待截图后才出现的内容。因此,“启动 App → 固定延时 5 秒 → 单帧识别”仍可能错过第 7 秒才加载的广告。
86
+
87
+ ### 持续观察
88
+
89
+ 从当前 AI 节点开始,按采样间隔连续截图,再根据观察模式提交模型。它观察的是一组离散截图,并不是连续视频。
90
+
91
+ | 参数 | 含义 | 当前范围 / 默认值 |
92
+ | --- | --- | --- |
93
+ | 观察时长 / 最长观察时间 | 本节点的采样窗口 | 1000~60000ms;默认 15000ms |
94
+ | 采样间隔 | 两次采样的计划间隔 | 500~10000ms;默认 1000ms |
95
+ | 采样数量 | 一次观察允许的截图数量 | 最多 30 帧 |
96
+
97
+ 节点开始会立即采样,因此 **15000ms、间隔 1000ms,通常约采集 16 张,而不是固定 15 张**。实际数量受截图速度、处理耗时和提前结束影响;提交模型的图片数量还会受去重影响。
98
+
99
+ 配置校验按以下公式估算帧数:
100
+
101
+ ```text
102
+ 预计帧数 = 向上取整(观察时长 ÷ 采样间隔)+ 1
103
+ ```
104
+
105
+ 例如:
106
+
107
+ | 观察时长 | 采样间隔 | 预计帧数 | 是否可用 |
108
+ | --- | --- | --- | --- |
109
+ | 15000ms | 1000ms | 16 | 可用 |
110
+ | 10000ms | 500ms | 21 | 可用 |
111
+ | 30000ms | 1000ms | 31 | 超过上限 |
112
+ | 30000ms | 2000ms | 16 | 可用 |
113
+ | 60000ms | 3000ms | 21 | 可用 |
114
+
115
+ 采样间隔越短,越容易捕捉短暂画面,但截图和图片分析的消耗也可能增加。持续时间短于采样间隔的广告仍可能漏采。
116
+
117
+ 观察从 AI 节点开始,不会追溯上一节点的画面。应紧接启动 App 或触发广告的操作放置节点;如果广告在上一节点执行期间已经出现并消失,后续观察仍无法补拍。
118
+
119
+ ## 6. 观察模式怎么选
120
+
121
+ ### 观察结束后统一分析
122
+
123
+ 执行过程:**采样 → 本地去重 → 观察结束 → 将保留截图统一提交模型 → 得到结果**。
124
+
125
+ 适合以下需求:
126
+
127
+ - 想知道观察期间是否出现过广告,不需要立即操作。
128
+ - 需要提取短暂广告的内容。
129
+ - 希望减少模型请求次数。
130
+ - 需要比较多张截图中的页面变化。
131
+
132
+ 这个模式在采样完成后发起一次多图分析请求。一次请求仍可能包含多张图片,所以“请求次数少”不等于“图片输入成本一定很低”。
133
+
134
+ 存在性判断按“观察期间任意一帧明确出现目标”理解。例如问题写“当前画面有没有广告”,持续观察下判断的是本次观察期间是否看到过广告,而不是只看最后一张图。
135
+
136
+ ### 检测命中后提前结束
137
+
138
+ 执行过程:**持续采样并保留关键帧 → 逐次提交新关键帧识别 → 模型确认命中 → 停止观察**。
139
+
140
+ 同一时间只进行一个模型识别请求。模型处理中采到的新关键帧会排队,在下一次请求中一起分析,因此不是固定“一秒调用一次模型”。
141
+
142
+ | 执行结果 | 已启用分支 | 未启用分支 |
143
+ | --- | --- | --- |
144
+ | 模型确认命中 | 停止观察,进入 true(是)分支 | 停止观察,执行下一节点 |
145
+ | 采样结束且所有待分析关键帧均未命中 | 进入 false(否)分支 | 执行下一节点 |
146
+ | 模型报错、无法判断、返回格式错误 | 按识别失败处理 | 按识别失败处理 |
147
+
148
+ 最长观察时间结束后停止的是采样,还需要等待正在进行和剩余关键帧的识别。因此:
149
+
150
+ - 设置最长观察 15 秒,节点总耗时可能超过 15 秒。
151
+ - 提前结束发生在模型返回命中结果时,不是广告出现在屏幕上的瞬间。
152
+ - 即使未启用分支,也必须填写能判断“是 / 否”的命中条件。
153
+ - 未启用分支时,有没有命中都会继续下一节点;要让“没检测到广告”走异常流程,应启用分支。
154
+
155
+ 不要用这个模式要求模型完整描述全过程,也不要用它精确测量广告出现或消失时间。
156
+
157
+ ## 7. 分支条件和问题验证
158
+
159
+ “启用分支”适合明确的判断问题;不启用分支的单帧或统一分析模式适合描述和内容提取。
160
+
161
+ | 提示词 | 适合分支 / 提前结束 | 原因 |
162
+ | --- | --- | --- |
163
+ | 当前画面是否出现广告? | 是 | 判断一个明确对象是否存在 |
164
+ | 当前广告页面是否存在可见的“跳过”按钮? | 是 | 判断指定按钮是否存在 |
165
+ | 视频区域是否已显示正常图像,而不是黑屏或加载占位? | 是 | 判断指定区域的状态 |
166
+ | 描述当前画面 | 否 | 开放式描述 |
167
+ | 提取广告标题、产品名称和全部按钮文字 | 否 | 内容提取任务 |
168
+ | 有没有广告,如果有请列出全部广告内容 | 不建议 | 将判断与开放式提取混在一起,可能无法通过验证 |
169
+
170
+ 内置的“广告及跳过关闭按钮检测”包含多项检查要求。用于信息分析时可直接选择;用于分支时,建议改成单一明确条件,例如:
171
+
172
+ ```text
173
+ 当前画面是否出现商业推广广告?
174
+ 升级弹窗、系统通知弹窗、权限申请弹窗不属于广告。
175
+ ```
176
+
177
+ 如果你只希望在“广告出现并且能跳过”时继续,应明确写成:
178
+
179
+ ```text
180
+ 当前画面是否同时满足以下条件:
181
+ 1. 已出现商业推广广告;
182
+ 2. 广告上存在可见的“跳过”或“关闭”按钮。
183
+ 升级弹窗、通知弹窗和权限申请弹窗不属于广告。
184
+ ```
185
+
186
+ 开启分支、切换到提前结束模式,或者修改判断问题时,系统会调用模型验证问题类型。点击“验证并保存问题”后,验证期间按钮禁用,下方显示“正在验证…”。失败时不会保存本次问题修改。
187
+
188
+ **问题验证不提供设备截图,只检查这段文字是否适合判断。** 验证通过不代表广告已经出现;检查当前设备必须执行“测试 AI 识别”或回放。
189
+
190
+ ## 8. 按场景配置
191
+
192
+ ### 场景 A:开屏广告加载时间不固定
193
+
194
+ 例如有时 3 秒出现,有时 7 秒出现,固定延时容易拍到闪屏页。
195
+
196
+ | 项目 | 建议 |
197
+ | --- | --- |
198
+ | 节点位置 | 启动 App 后立即进入 AI 识别 |
199
+ | 识别方式 | 持续观察 |
200
+ | 观察模式 | 检测命中后提前结束 |
201
+ | 最长观察时间 | 15000ms;实测存在更长加载时再调整 |
202
+ | 采样间隔 | 1000ms |
203
+ | 分支 | 开启 |
204
+ | 去重 | 先用 pixelmatch 默认参数 |
205
+ | true 分支 | 进入后续广告检查或独立点击节点 |
206
+ | false 分支 | 记录“观察期间未发现广告”,按测试要求结束或执行其他流程 |
207
+
208
+ 提示词:
209
+
210
+ ```text
211
+ 当前画面是否出现商业推广广告?
212
+ App 品牌启动页、升级弹窗、系统通知弹窗和权限申请弹窗不属于广告。
213
+ ```
214
+
215
+ 不要把“尚未出现广告”与“永远不会出现广告”等同。false 只能说明这次采样并分析的画面没有命中。
216
+
217
+ ### 场景 B:广告出现后等待跳过按钮
218
+
219
+ 如果广告出现时按钮尚未展示,不要只判断“广告出现”。改用上文“广告与按钮同时存在”的条件,或拆成“广告检测 → 按钮检测”两个节点。
220
+
221
+ 建议持续观察、提前结束、10000ms / 500ms、启用分支。为了保留小按钮变化,可先将 pixelmatch 的允许变化面积从默认 0.50% 降到 0.10%,再用截图验证。
222
+
223
+ AI 节点只判断,不会因为提示词写了“关闭广告”就自动点击。下一步应使用独立的点击节点。模型返回时按钮也可能已经消失,后续操作仍应考虑实际页面状态。
224
+
225
+ ### 场景 C:广告内容提取,包含升级弹窗等干扰
226
+
227
+ | 项目 | 建议 |
228
+ | --- | --- |
229
+ | 识别方式 | 广告稳定时用单帧;短暂展示时用持续观察 |
230
+ | 观察模式 | 观察结束后统一分析 |
231
+ | 观察时长 / 间隔 | 10000ms / 1000ms |
232
+ | 分支 | 关闭 |
233
+ | 去重 | pixelmatch 默认;重要小字被漏掉时降低去重强度 |
234
+
235
+ 提示词:
236
+
237
+ ```text
238
+ 请检查截图中的商业推广广告,并回答:
239
+ 1. 是否发现广告;
240
+ 2. 广告标题和产品名称;
241
+ 3. 是否有“跳过”或“关闭”按钮;
242
+ 4. 其他清晰可见的广告文字。
243
+ 升级弹窗、系统通知弹窗和权限申请弹窗不属于广告。
244
+ 没有看到的内容写“未发现”或“无法辨认”,不要推测。
245
+ ```
246
+
247
+ 回答写入识别结果和回放日志。当前 AI 识别节点不会自动把广告标题等结果写入可供后续节点引用的变量。
248
+
249
+ ### 场景 D:等待视频、摄像头预览或图片出图
250
+
251
+ 建议持续观察、提前结束、20000ms / 1000ms、启用分支。
252
+
253
+ 提示词应指出目标区域,避免把导航栏或背景图片误当成“出图”:
254
+
255
+ ```text
256
+ 当前画面中部的视频预览区域是否已显示实际场景图像?
257
+ 纯黑、纯白、加载动画、默认封面和离线占位图均视为未出图。
258
+ ```
259
+
260
+ 如果目标只是检查黑屏比例,可优先使用“图像判断”;如果需要区分封面、占位图与真实场景,再使用 AI 识别。
261
+
262
+ ### 场景 E:只想确认观察期间是否出现过广告,控制模型消耗
263
+
264
+ 建议持续观察、统一分析、15000ms / 1000ms、启用分支、pixelmatch 默认参数。
265
+
266
+ 这个模式即使较早采到广告,也会完成观察后再分析。它适合结果统计,不适合立即点击即将消失的广告。
267
+
268
+ ### 场景 F:按钮、角标或小文字发生变化
269
+
270
+ 先使用 pixelmatch,降低允许变化面积,例如 0.10%;必要时再降低像素颜色容差,例如 0.05。若仍漏掉关键细节,临时切换“不去重”作对照测试。
271
+
272
+ 这些数值是调试起点。不要为了减少图片数量直接增大允许变化面积:一个重要按钮可能只占屏幕很小的一部分。
273
+
274
+ ### 场景 G:视频或动画持续变化,提交图片太多
275
+
276
+ 先缩短观察窗口或增大采样间隔;若无需立即操作,使用统一分析。对不重要的细微变化,可尝试 OpenCV(SSIM),从默认 0.990 开始调整。
277
+
278
+ 视频帧内容本来就可能差异很大,两种去重都不能保证大幅减少图片。也不要把阈值调得过宽,把真正需要识别的广告或按钮一起过滤掉。
279
+
280
+ ## 9. 截图去重怎么调
281
+
282
+ 入口:**参数配置 → Appium 配置 → AI 识别配置 → 持续观察截图去重**。
283
+
284
+ 去重是全局配置,用于后续持续观察回放和识别测试,不影响单帧识别。两种算法都在本地比较图片,不调用模型;第一帧始终保留,之后与上一张保留的截图比较。
285
+
286
+ | 方式 | 适合 | 注意事项 |
287
+ | --- | --- | --- |
288
+ | 不去重 | 排查漏帧、确认每次采样内容、细节非常重要 | 图片输入消耗较高;仍无法补回采样间隔内没拍到的内容 |
289
+ | pixelmatch(默认) | 同一设备的页面、弹窗、按钮变化 | 比较像素颜色与变化面积,参数过宽会过滤小变化 |
290
+ | OpenCV(SSIM) | 更关注整体画面相似度,希望忽略部分细微噪声 | 不理解广告等语义,不自动对齐位移;缩图可能损失小文字差异 |
291
+
292
+ 参数含义:
293
+
294
+ | 参数 | 默认值 | 增大后的影响 |
295
+ | --- | --- | --- |
296
+ | pixelmatch:像素颜色容差 | 0.10 | 更容易忽略细微颜色差异,通常保留更少 |
297
+ | pixelmatch:允许变化面积 | 0.50% | 更大面积的变化也可能被视为重复,保留更少 |
298
+ | OpenCV:SSIM 相似度阈值 | 0.990 | 越接近 1,判重复越严格,通常保留更多 |
299
+ | OpenCV:比较图像最长边 | 512px | 比较时保留更多细节,也增加本地计算量 |
300
+
301
+ 注意,pixelmatch 的“允许变化面积”与 SSIM 的阈值调整方向不同。要更保守地保留画面,前者调低,后者调高。
302
+
303
+ OpenCV 的最长边参数只影响本地比较图片。持续观察提交模型的图片另有统一压缩:最长边不超过 1600px、JPEG 质量 85;观察图片数据还受约 24MB 上限约束。小文字识别也会受设备分辨率、图片压缩和模型能力影响。
304
+
305
+ ## 10. 超时应该怎么设置
306
+
307
+ AI 节点需要主动勾选“启用超时”,设置值才会生效。新建节点默认不启用;启用时请检查数值,通用默认值 3000ms 对部分多图请求可能过短。
308
+
309
+ | 时间设置 | 控制什么 | 不控制什么 |
310
+ | --- | --- | --- |
311
+ | 观察时长 / 最长观察时间 | 持续采样的窗口 | 不是整个节点的总耗时上限 |
312
+ | 模型请求超时 | 持续观察中每次模型请求的等待时间 | 不会把 15 秒观察窗口截成 3 秒 |
313
+ | 单帧超时时间 | 单帧识别的请求流程,包含需要进行的截图等待 | 不等于广告等待窗口 |
314
+
315
+ 可先启用 30000ms 作为模型请求超时的调试起点,再根据实际耗时调整。后端将正数超时限制在 1000~300000ms;不要把填入 0 理解为禁用,禁用应取消勾选“启用超时”。
316
+
317
+ 统一分析总耗时大致由“观察采样 + 模型分析”组成;提前结束模式的采样与识别有重叠,但仍可能等待多个请求。
318
+
319
+ 未启用节点超时也不意味着任何外部连接都能无限等待;网络、模型服务和设备截图本身仍可能报错。需要停止等待时可手动终止回放。
320
+
321
+ 启用分支并启用超时时,可以配置“超时后处理”:默认超时终止,也可以指定进入 true 或 false 分支。**因超时进入某分支不代表模型判断为真或假**,回放日志会记录超时原因。模型 HTTP 错误、格式错误或无法判断,不会自动被当成“未命中”。
322
+
323
+ ## 11. 测试与排查
324
+
325
+ 在节点上点击“测试 AI 识别”,查看判断 / 回答、耗时及实际使用的截图。持续观察还会显示采集帧数、提交帧数、去重方式;提前结束模式会显示是否命中和模型请求次数。
326
+
327
+ 测试会立即开始识别,使用的是此时设备的画面,不会自动执行前面的“启动 App”等节点。验证完整开屏流程,应回放包含启动操作的脚本。
328
+
329
+ 按下面的顺序排查:
330
+
331
+ | 现象 | 优先检查 |
332
+ | --- | --- |
333
+ | 配置测试通过,但 AI 识别报错 | 模型是否支持图片、多图;检查 HTTP 错误和节点测试结果 |
334
+ | 问题验证失败 | 是否混入开放式提取要求;先改成单一“是否”条件 |
335
+ | 明明有广告,却走 false | 提交的截图有没有拍到广告;再看是否被去重、提示词是否排除了目标 |
336
+ | 采集很多帧,只提交少量 | 通常是去重生效;对照实际提交画面检查是否误删关键变化 |
337
+ | 观察 15 秒,执行超过 15 秒 | 检查模型耗时、剩余关键帧和逐次请求等待 |
338
+ | 命中后仍没有走 true | 检查是否启用分支、true 路径是否连接,以及是否编辑后尚未验证保存 |
339
+ | 没有命中却执行了下一节点 | 未启用分支时这是正常行为;需要区分结果请开启分支 |
340
+ | 每次都拍到启动页 | AI 节点位置、观察窗口和采样时机可能不合适 |
341
+ | 小按钮出现但没检测到 | 降低 pixelmatch 允许变化面积,或提高 SSIM 阈值;用不去重对照 |
342
+ | 提前命中后点击失败 | 模型识别的是已采集画面,返回时目标可能已消失;检查后续点击节点 |
343
+
344
+ 调试时先确认“拍到了什么”,再调整“模型怎么理解”,最后才优化图片数量和模型消耗。保留正常页面、真实广告、升级弹窗和通知弹窗等样本,有助于发现提示词的误判边界。
345
+
346
+ ## 12. 可直接采用的开屏广告流程
347
+
348
+ ```text
349
+ 启动目标 App
350
+ ↓
351
+ AI 识别:持续观察 / 检测命中后提前结束
352
+ 最长观察 15000ms,采样间隔 1000ms
353
+ 启用分支,pixelmatch 先用默认参数
354
+ 模型超时先试 30000ms,超时默认终止
355
+ ├─ true:进入广告相关的独立检查或操作节点
356
+ └─ false:记录“观察期间未发现广告”,按用例要求处理
357
+ ```
358
+
359
+ 测试场景名称:**开屏广告检测**。
360
+
361
+ 命中条件:
362
+
363
+ ```text
364
+ 当前画面是否出现商业推广广告?
365
+ App 品牌启动页、升级弹窗、系统通知弹窗和权限申请弹窗不属于广告。
366
+ ```
367
+
368
+ 如果下一步必须立刻点击“跳过”,将命中条件改为“广告与可见的跳过 / 关闭按钮同时存在”,并重点验证小按钮变化不会被去重。
369
+
370
+ 如果目标是提取广告内容,应改用统一分析、关闭分支,或另设内容提取节点;第二个节点会重新截图,不能保证仍看到第一个节点捕获的短暂广告。
package/CHANGELOG.md CHANGED
@@ -1,4 +1,20 @@
1
1
  # 更新记录
2
+ ## v0.1.43
3
+ - 新增独立的“提示词优化模型”,统一用于优化 AI 识别提示词、报告提示词和生成回放测试报告总结;AI 识别与回放报告均可输入简要要求后由模型补充提示词,模型测试成功改为弹窗提示。
4
+ - 回放设置新增报告模板选择,不选择时使用默认测试报告;开启报告总结后会在回放前检测提示词优化模型,模型不可用时弹窗提示并继续生成默认格式的基础报告。总结请求失败、超时或返回空内容时同样保留基础报告,不改变测试结果。
5
+ - 回放输出弹窗支持同时拖拽调整宽度和高度,并保留最小尺寸与窗口边界限制。
6
+ - 连接脚本列表显示同一 App 的其他已保存脚本,不再因入口 Activity 不同或未绑定而隐藏;选择时说明等待入口页面或直接从当前画面执行的行为。
7
+ - 点击坐标操作新增“获取点击坐标”,直接在左侧设备预览点击画面即可填入实际 X、Y 坐标,无需弹窗,支持取消,取点不会执行设备操作。
8
+
9
+ ## v0.1.42
10
+ - 修复回放终止或会话创建失败后设备端 UiAutomator2 残留,导致组件树刷新失败的问题。
11
+ - AI 持续观察新增“检测命中后提前结束”模式,边采样去重边识别,命中后停止观察并继续流程或进入“是”分支;到达最长观察时间后完成剩余关键帧判断,未命中走原顺序或“否”分支,支持命中条件校验、取消和逐次模型请求超时。
12
+ - Appium 参数的AI 识别配置,新增持续观察选项,支持 pixelmatch(默认)、OpenCV(SSIM)和不去重选项。
13
+ - AI 识别新增“持续观察”,可设置观察时长与采样间隔,按时间顺序采集多帧后统一判断目标是否出现过或读取内容;观察与模型超时分开计时,支持取消、采样日志与测试画面预览,原单帧识别保持兼容。
14
+ - Midscene 配置新增官方文档中的模型提供方预设。
15
+ - 新增 AI 识别默认顺序执行模式,模型回答写入日志后继续下一节点;勾选“启用分支”及修改分支问题时通过模型验证判断语义,不符合时提示且不保存分支修改。
16
+ - Appium 配置新增默认关闭的“截图与 HTML 报告”开关;开启后才采集节点前后截图并生成 HTML。
17
+
2
18
  ## v0.1.41
3
19
  - 修复部分华为设备录屏启动时报编码器错误的问题:使用设备默认编码参数,视频流启动失败时降低分辨率和码率重试,并输出各次尝试的诊断日志。
4
20
 
package/README.md CHANGED
@@ -52,6 +52,7 @@ npx android-midscene-automation --port 5174
52
52
  ## 功能文档
53
53
 
54
54
  - [Appium 录制器使用说明](#appium-录制器使用说明)
55
+ - [AI 识别详细使用说明](./AI_RECOGNITION_GUIDE.md)
55
56
  - [常见问题](#常见问题)
56
57
  - [项目更新记录](#项目更新记录)
57
58
 
@@ -171,6 +172,8 @@ midscene_run/ # Midscene 执行报告和运行产物
171
172
 
172
173
  本文档随 npm 包发布,也可以从 README 的“功能文档”直接打开。常见问题请查看 [常见问题](#常见问题)。
173
174
 
175
+ AI 识别的场景选择、预设提示词、持续观察、分支、截图去重及超时配置,请查看 [AI 识别详细使用说明](./AI_RECOGNITION_GUIDE.md)。
176
+
174
177
  ## 使用前准备
175
178
 
176
179
  ### 1. 连接 Android 设备
@@ -895,9 +898,9 @@ Home 键
895
898
 
896
899
  校验规则:
897
900
 
898
- - 主流程中连接脚本:插入点 Activity 优先使用前一步录制的 `pageAfter.activity`,只允许选择入口 Activity 与插入点一致的脚本。
899
- - 判断分支中连接脚本:允许选择同一 App 的其他 Activity 脚本,适合登录成功后进入首页等跳转路径。
900
- - 分支连接在回放时会等待手机进入目标脚本的入口 Activity;页面没有真正跳转时会超时失败,不会提前执行其他页面的步骤。
901
+ - 主流程和判断分支均可选择同一 App 的其他已保存脚本,不要求入口 Activity 与插入点一致。
902
+ - 目标脚本已绑定入口 Activity 时,回放会等待手机进入该 Activity;页面没有真正跳转时会超时失败。
903
+ - 目标脚本未绑定入口 Activity 时,直接从当前画面执行。连接脚本不会自动切换手机页面,需在前序步骤中完成点击、返回等操作。
901
904
  - 目标脚本必须属于当前 App,且不能连接当前脚本自身。
902
905
  - 脚本互相连接形成循环时,回放会终止并提示循环连接。
903
906
 
@@ -931,6 +934,19 @@ Home 键
931
934
  - 子脚本单独回放时仍会正常执行自己的“启动 App”节点。
932
935
  - 如果脚本互相连接形成循环,会终止并提示循环连接。
933
936
 
937
+ ### 回放报告总结
938
+
939
+ 在“参数配置 → Appium配置 → 回放报告总结”中配置,默认关闭,与截图报告独立。
940
+
941
+ 1. 在上方“提示词优化模型”填写 Base URL、API Key、Model Name,并点击“测试并保存”。该文本模型同时用于优化提示词和生成回放报告总结。
942
+ 2. 开启“回放报告总结”。开关和报告提示词修改后自动保存。
943
+ 3. 默认格式包括测试概览(脚本名称、起止时间、结果、总时长)、执行配置及节点参数、用例描述表格、失败详情和汇总。可选择默认测试报告或出图时间分析,也可修改提示词调整内容。
944
+ 4. 回放结束后将脱敏日志、主脚本与连接脚本配置发送给总结模型,生成的 Markdown 使用原回放报告路径,保存在配置的报告输出目录(默认 `output`);完成日志会显示报告路径。
945
+
946
+ 总结在设备操作结束后生成,页面会显示生成进度,总结耗时不计入测试总时长。关闭时继续生成原基础报告且不调用总结模型;请求失败、超时或返回空内容时保留基础报告和原始日志,并输出失败原因,不改变测试结果。模型请求最多等待 60 秒。
947
+
948
+ 报告不会向模型发送截图和图片模板。为控制输入大小,脚本配置最多保留约 50000 字符,日志最多保留约 100000 字符;超限时保留头尾并标注中段省略,生成报告也会注明信息不完整。模型的原因分析属于辅助判断,应结合原始日志核实。
949
+
934
950
  ## 脚本保存与管理
935
951
 
936
952
  ### 保存和更新
@@ -1229,6 +1245,22 @@ Linux: ~/.local/share/android-midscene-automation
1229
1245
 
1230
1246
  # 项目更新记录
1231
1247
 
1248
+ ## v0.1.43
1249
+ - 新增独立的“提示词优化模型”,统一用于优化 AI 识别提示词、报告提示词和生成回放测试报告总结;AI 识别与回放报告均可输入简要要求后由模型补充提示词,模型测试成功改为弹窗提示。
1250
+ - 回放设置新增报告模板选择,不选择时使用默认测试报告;开启报告总结后会在回放前检测提示词优化模型,模型不可用时弹窗提示并继续生成默认格式的基础报告。总结请求失败、超时或返回空内容时同样保留基础报告,不改变测试结果。
1251
+ - 回放输出弹窗支持同时拖拽调整宽度和高度,并保留最小尺寸与窗口边界限制。
1252
+ - 连接脚本列表显示同一 App 的其他已保存脚本,不再因入口 Activity 不同或未绑定而隐藏;选择时说明等待入口页面或直接从当前画面执行的行为。
1253
+ - 点击坐标操作新增“获取点击坐标”,直接在左侧设备预览点击画面即可填入实际 X、Y 坐标,无需弹窗,支持取消,取点不会执行设备操作。
1254
+
1255
+ ## v0.1.42
1256
+ - 修复回放终止或会话创建失败后设备端 UiAutomator2 残留,导致组件树刷新失败的问题。
1257
+ - AI 持续观察新增“检测命中后提前结束”模式,边采样去重边识别,命中后停止观察并继续流程或进入“是”分支;到达最长观察时间后完成剩余关键帧判断,未命中走原顺序或“否”分支,支持命中条件校验、取消和逐次模型请求超时。
1258
+ - Appium 参数的AI 识别配置,新增持续观察选项,支持 pixelmatch(默认)、OpenCV(SSIM)和不去重选项。
1259
+ - AI 识别新增“持续观察”,可设置观察时长与采样间隔,按时间顺序采集多帧后统一判断目标是否出现过或读取内容;观察与模型超时分开计时,支持取消、采样日志与测试画面预览,原单帧识别保持兼容。
1260
+ - Midscene 配置新增官方文档中的模型提供方预设。
1261
+ - 新增 AI 识别默认顺序执行模式,模型回答写入日志后继续下一节点;勾选“启用分支”及修改分支问题时通过模型验证判断语义,不符合时提示且不保存分支修改。
1262
+ - Appium 配置新增默认关闭的“截图与 HTML 报告”开关;开启后才采集节点前后截图并生成 HTML。
1263
+
1232
1264
  ## v0.1.41
1233
1265
  - 修复部分华为设备录屏启动时报编码器错误的问题:使用设备默认编码参数,视频流启动失败时降低分辨率和码率重试,并输出各次尝试的诊断日志。
1234
1266
 
package/USAGE.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  本文档随 npm 包发布,也可以从 README 的“功能文档”直接打开。常见问题请查看 [FAQ.md](./FAQ.md)。
6
6
 
7
+ AI 识别的场景选择、预设提示词、持续观察、分支、截图去重及超时配置,请查看 [AI 识别详细使用说明](./AI_RECOGNITION_GUIDE.md)。
8
+
7
9
  ## 使用前准备
8
10
 
9
11
  ### 1. 连接 Android 设备
@@ -728,9 +730,9 @@ Home 键
728
730
 
729
731
  校验规则:
730
732
 
731
- - 主流程中连接脚本:插入点 Activity 优先使用前一步录制的 `pageAfter.activity`,只允许选择入口 Activity 与插入点一致的脚本。
732
- - 判断分支中连接脚本:允许选择同一 App 的其他 Activity 脚本,适合登录成功后进入首页等跳转路径。
733
- - 分支连接在回放时会等待手机进入目标脚本的入口 Activity;页面没有真正跳转时会超时失败,不会提前执行其他页面的步骤。
733
+ - 主流程和判断分支均可选择同一 App 的其他已保存脚本,不要求入口 Activity 与插入点一致。
734
+ - 目标脚本已绑定入口 Activity 时,回放会等待手机进入该 Activity;页面没有真正跳转时会超时失败。
735
+ - 目标脚本未绑定入口 Activity 时,直接从当前画面执行。连接脚本不会自动切换手机页面,需在前序步骤中完成点击、返回等操作。
734
736
  - 目标脚本必须属于当前 App,且不能连接当前脚本自身。
735
737
  - 脚本互相连接形成循环时,回放会终止并提示循环连接。
736
738
 
@@ -764,6 +766,19 @@ Home 键
764
766
  - 子脚本单独回放时仍会正常执行自己的“启动 App”节点。
765
767
  - 如果脚本互相连接形成循环,会终止并提示循环连接。
766
768
 
769
+ ### 回放报告总结
770
+
771
+ 在“参数配置 → Appium配置 → 回放报告总结”中配置,默认关闭,与截图报告独立。
772
+
773
+ 1. 在上方“提示词优化模型”填写 Base URL、API Key、Model Name,并点击“测试并保存”。该文本模型同时用于优化提示词和生成回放报告总结。
774
+ 2. 开启“回放报告总结”。开关和报告提示词修改后自动保存。
775
+ 3. 默认格式包括测试概览(脚本名称、起止时间、结果、总时长)、执行配置及节点参数、用例描述表格、失败详情和汇总。可选择默认测试报告或出图时间分析,也可修改提示词调整内容。
776
+ 4. 回放结束后将脱敏日志、主脚本与连接脚本配置发送给总结模型,生成的 Markdown 使用原回放报告路径,保存在配置的报告输出目录(默认 `output`);完成日志会显示报告路径。
777
+
778
+ 总结在设备操作结束后生成,页面会显示生成进度,总结耗时不计入测试总时长。关闭时继续生成原基础报告且不调用总结模型;请求失败、超时或返回空内容时保留基础报告和原始日志,并输出失败原因,不改变测试结果。模型请求最多等待 60 秒。
779
+
780
+ 报告不会向模型发送截图和图片模板。为控制输入大小,脚本配置最多保留约 50000 字符,日志最多保留约 100000 字符;超限时保留头尾并标注中段省略,生成报告也会注明信息不完整。模型的原因分析属于辅助判断,应结合原始日志核实。
781
+
767
782
  ## 脚本保存与管理
768
783
 
769
784
  ### 保存和更新
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "android-midscene-automation",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/oooooooko/android-midscene-automation.git"
@@ -23,6 +23,7 @@
23
23
  "vite.config.ts",
24
24
  "README.md",
25
25
  "USAGE.md",
26
+ "AI_RECOGNITION_GUIDE.md",
26
27
  "FAQ.md",
27
28
  "CHANGELOG.md"
28
29
  ],
@@ -136,6 +136,7 @@ async function handleCommand(command: RemoteCommand) {
136
136
  const script = payload.script as AppiumRecordedScriptRecord | undefined;
137
137
  if (!script) throw new Error('远程回放缺少脚本内容');
138
138
  return await replayAppiumScript(script, command.deviceId, undefined, undefined, {
139
+ screenshotReport: payload.screenshotReport === true,
139
140
  parameters: payload.parameters as import('../src/appium-recorder/variables').TestVariable[] | undefined,
140
141
  globalVariables: payload.globalVariables as import('../src/appium-recorder/variables').TestVariable[] | undefined,
141
142
  linkedScripts: payload.linkedScripts as AppiumRecordedScriptRecord[] | undefined,
@@ -0,0 +1,61 @@
1
+ import { resolveReportSummary } from '../src/appium-recorder/report-summary';
2
+ // Run: npx tsx --test server/appium-auto-save.test.ts
3
+ import assert from 'node:assert/strict';
4
+ import { test } from 'node:test';
5
+ import { setTimeout as delay } from 'node:timers/promises';
6
+ import { effectScope, reactive } from 'vue';
7
+ import { useAppiumAutoSave } from '../src/config/use-appium-auto-save';
8
+ import type { ConfigForm } from '../src/types';
9
+
10
+ test('loads quietly, serializes rapid edits, validates colors and surfaces save errors', async () => {
11
+ const scope = effectScope();
12
+ const form = reactive({ appium: { reportSummary: resolveReportSummary(), model: { baseUrl: '', apiKey: '', name: '' } } } as ConfigForm);
13
+ const submissions: Omit<ConfigForm['appium'], 'model'>[] = [];
14
+ const errors: unknown[] = [];
15
+ let release: (() => void) | undefined;
16
+ let fail = false;
17
+ const auto = scope.run(() => useAppiumAutoSave(form, async value => {
18
+ submissions.push(value);
19
+ if (fail) throw new Error('offline');
20
+ if (submissions.length === 1) await new Promise<void>(resolve => { release = resolve; });
21
+ }, error => errors.push(error)))!;
22
+ try {
23
+ form.appium.model.name = 'loaded';
24
+ auto.initialize();
25
+ await delay(400);
26
+ assert.equal(submissions.length, 0, 'loading must never overwrite stored config');
27
+ form.appium.model.name = 'untested';
28
+ await delay(400);
29
+ assert.equal(submissions.length, 0, 'model drafts must never auto-save');
30
+ form.appium.flowBackgroundColor = '#abc';
31
+ form.appium.flowBackgroundColor = '#def';
32
+ await delay(400);
33
+ assert.equal(submissions.length, 1);
34
+ assert.equal(submissions[0].flowBackgroundColor, '#def');
35
+ assert.equal('model' in submissions[0], false);
36
+ form.appium.flowBackgroundColor = '#123456';
37
+ await delay(400);
38
+ assert.equal(submissions.length, 1, 'only one request may be in flight');
39
+ release!();
40
+ await delay(20);
41
+ assert.equal(submissions.length, 2);
42
+ assert.equal(submissions[1].flowBackgroundColor, '#123456');
43
+ assert.equal(auto.status.value, '已自动保存');
44
+ form.appium.flowLineColor = '#';
45
+ await delay(400);
46
+ assert.equal(submissions.length, 2, 'partial hex input must not be persisted');
47
+ form.appium.flowLineColor = '#123';
48
+ await delay(400);
49
+ assert.equal(submissions.length, 3);
50
+ fail = true;
51
+ form.appium.screenshotReport = true;
52
+ await delay(400);
53
+ assert.equal(errors.length, 1);
54
+ assert.match(auto.status.value, /失败/);
55
+ fail = false;
56
+ form.appium.flowBackgroundColor = '#abcdef';
57
+ await delay(400);
58
+ assert.equal(auto.status.value, '已自动保存');
59
+ assert.equal(submissions.at(-1)?.screenshotReport, true);
60
+ } finally { scope.stop(); }
61
+ });