AI自动化UI测试学习思路(1)
本文记录一个 AI 自动化 UI 测试系统的真实架构演进过程。核心不是"最终架构长什么样",而是每一步为什么踩坑、为什么推翻、怎么演变到下一步。
注意:文中所有具体 URL、token、项目名均已脱敏。
一、系统不是一次设计出来的——架构演进时间线
V0 盲写脚本 → 让 LLM 直接根据任务描述生成 Playwright 代码
V1 单次 MCP 探测 → 先用 MCP 看一下页面结构,再让 LLM 写代码
V2 Claude CLI 方案 → 用 claude --bare --print 生成脚本(已废弃)
V3 API 直调方案 → 直接调用 LLM API,自己组装 Prompt
V4 执行层补齐 → 加 trace、截图、watchdog、自动修复闭环
V5 断言层演进 → 从"完全信 AI"到"AI + DOM 双保险"
V6 知识库接入 → 记住"这个错上次怎么修的"
V7 启动链路分离 → 认证注入、页面加载不再散落在各脚本里
V8 配置分层 → 系统级 / 项目级 / 用例级三层配置
下面按版本展开,重点讲每个版本的架构决策失误。
二、V0:盲写脚本——让 AI 瞎猜选择器
2.1 当时的设计思路
“AI 不是挺聪明的吗?直接告诉它’打开某某页面,点击登录按钮’,它应该能写出对的代码吧?”
于是最早的流程是:
task.txt(自然语言描述)
↓
直接扔给 LLM → 生成 Python 脚本
↓
保存为 script_v1_0.py
2.2 踩的坑
坑 1:AI 没有页面信息,选择器全是猜的
LLM 生成的代码里有 //button[@id='submit'],但目标页面实际用的是 //button[@class='ant-btn-primary']。LLM 没见过页面,只能根据常识瞎猜,猜中的概率极低。
坑 2:生成的代码无法直接运行
- 没有
try/finally关闭浏览器,进程泄漏 - 没有等待逻辑,元素还没渲染就操作
- 没有异常处理,一步失败全盘崩
2.3 结论
不给 AI 看页面就让它写代码,等于闭着眼射箭。
必须让 AI"先看一眼"页面结构,再写选择器。
三、V1:单次 MCP 探测——看了,但没看透
3.1 当时的设计思路
“既然盲写不行,那让 AI 先通过 MCP(Model Context Protocol)获取页面结构,拿到无障碍树和 DOM 元素列表,再写代码总该准了吧?”
task.txt
↓
提取 URL → MCP 获取页面结构(无障碍树 + 交互元素列表)
↓
把页面结构塞进 Prompt → LLM 生成脚本
↓
保存 script_v1_0.py
3.2 踩的坑
坑 3:MCP 返回的结果没有充分利用
虽然拿到了页面结构,但生成的脚本仍然只依赖单一选择器。为什么?因为 Prompt 里没明确要求"生成多策略降级定位"。LLM 拿到信息后,还是习惯性地写一个最像 XPath 的选择器就完事了。
坑 4:只依赖单一选择器,SPA 一重构就崩
前端用的是 React/Vue,元素嵌套层级、class 名随时可能变。脚本里只写了一个 //div[3]/div[1]/button,前端加了个 wrapper div,脚本第二天就挂。
坑 5:MCP 探测的时机和错误处理没做好
- 页面还没加载完就探测,无障碍树只有
<html>一个节点 - MCP 返回
isError=True时,代码没有中断,继续往下执行,生成一堆基于错误信息的垃圾代码 - 某些自定义组件(如自定义 textbox)直接
type()不生效,需要click()后再type(),但 MCP 探测不到这种行为层面的信息
3.3 结论
看了页面不够,还要让 AI 生成"备胎选择器";探测页面不够,还要处理探测失败的情况。
四、V2:Claude CLI 方案——一条死路
4.1 当时的设计思路
“既然要组装 Prompt 调 LLM,不如直接用 Claude Code 的 CLI 模式:claude --bare --print,把任务描述和页面结构传进去,让它直接输出代码。这样不用自己管理 API Key、模型选择,Claude 自己搞定。”
4.2 踩的坑
坑 6:Windows 下子进程卡死
Python 里用 subprocess.Popen(['claude.cmd', '--bare', '--print'], ...),在 Windows 上直接 hang 住,永远拿不到输出。根本原因是 claude.cmd 内部依赖 git-bash 环境,而 Python subprocess 的 PIPE 重定向和它冲突。
坑 7:中文 Prompt 被编码搞坏
Windows 的 cmd.exe 默认用 GBK 编码,直接把 UTF-8 的中文 Prompt 传给 CLI,中文字符全部乱码,Claude 收到的 prompt 是残缺的,输出的代码也是残缺的。
坑 8:特定 trigger 词导致空响应
Prompt 里如果包含 AIVisualAssertion、assert_business_goal、03_ai_assertion_input 这类和项目代码相关的词,Claude CLI 会返回空响应(0 字符)。原因不明,猜测是 CLI 内部的安全过滤或上下文冲突。
4.3 结论
依赖外部 CLI 工具做核心链路,等于把命脉交给不可控的第三方。Windows 编码 + 子进程 + 第三方 CLI = 三重不稳定。
彻底废弃 CLI 方案,改为直接调用 HTTP API。
五、V3:API 直调方案——Prompt 架构混乱
5.1 当时的设计思路
“直接用 requests 调用 Claude/GPT/Qwen API,自己组装 system prompt + user prompt,总该稳定了吧?”
5.2 踩的坑
坑 9:Prompt 内容三层重复
最早的设计里,同时存在:
generate_script.mdskill 文件(生成脚本的规范)fix_script.mdskill 文件(修复脚本的规范)- system prompt 里的通用编码规范
- user prompt 里的项目特定配置
结果是同一个"要关闭浏览器"的要求,在 4 个地方各写了一遍。改一处,另外三处忘改,AI 的行为前后不一致。
坑 10:project.config.md 的双重身份危机
project.config.md 既想让机器读(YAML frontmatter 格式的配置),又想让人读(Markdown 文档说明)。结果是:
- 代码解析 frontmatter 时,把说明文字也当成配置读进去
- 人看文档时,被 YAML 的
---分隔符搞糊涂 - 一些值(如
screenshot.skip_font_wait、ai_vision.*)写进去了但代码从来不读,成了"僵尸配置"
坑 11:System Prompt 在修复时丢失
生成脚本时传了 system prompt(编码规范、关闭浏览器等要求),但修复脚本时走的是 _call_with_images(带截图的 API 调用),这个函数没传 system prompt。结果是:修复后的脚本经常忘记 try/finally,选择器质量也下降。
坑 12:AI 返回的代码被截断或为空
- 代码跑到一半,类定义没闭合(检测末尾字符重试解决)
- 返回 0 字符,生成空文件(加空内容检查解决)
- 修复前缀带中文解释(如"我来分析这个错误并修复脚本。"),导致 Python
SyntaxError: invalid character '。'
5.3 结论
Prompt 不是越详细越好,是分层的:System 管通用规则(只写一次),User 管动态内容(每次不同)。配置文件的机器部分和人读部分必须分开。
最终方案:合并成单一 system prompt skill,删掉 generate_script.md 和 fix_script.md,project.config.md 只保留项目特定配置,通用规则全部进 system prompt。
六、V4:执行层补齐——从"跑完拉倒"到"可观测可修复"
6.1 当时的设计思路
“脚本生成了,直接 python script_v1_0.py 运行,成功就成功,失败就失败。”
6.2 踩的坑
坑 13:没有过程回溯,失败了完全不知道发生了什么
最早只有最终成功/失败的结果,没有:
- 每步执行后的截图(不知道页面长什么样)
- Playwright trace(无法回放执行过程)
- 详细日志(不知道卡在哪一步)
脚本失败了,只能看终端的 traceback,但很多错误(如元素定位失败)光看代码行号根本定位不了问题。
坑 14:trace 目录创建了,但根本没录
代码里创建了 traces/ 目录,但 tracing.start() 根本没调用。目录是空的,只是"看起来有 trace 功能"。
坑 15:脚本卡住拖垮整体
某一步 page.click() 或 page.wait_for_selector() 卡住 10 分钟不返回,整个用例被拖死。没有 watchdog 机制,没有超时强制终止。
坑 16:Python stdout 缓冲导致"假死"
子进程执行脚本时,stdout 连接 PIPE,Python 默认缓冲输出。脚本其实在正常运行,但父进程看不到任何输出,以为卡死了。
坑 17:修复时改坏没出错的步骤
第 3 步报错,让 AI 修复,结果 AI 把整个脚本重写了一遍,第 1 步、第 2 步也改坏了。原因是 Prompt 没约束"只修改出错步骤,其他原样保留"。
坑 18:修复无限循环耗光额度
同一类错误反复修反复挂,没有限制修复轮数。理论上可以无限循环下去,把 API 额度烧光。
6.3 结论
执行层不能只是"运行脚本",必须是一个闭环:运行 → 观测(截图+trace+日志)→ 诊断 → 修复 → 再运行。没有观测就没有诊断,没有诊断就没有修复。
最终补齐的组件:
- 每步截图(
screenshots/时间戳/) - Playwright trace(失败时保留 zip)
- 视频录制(失败时保留 webm,成功时自动清理)
- Watchdog(120 秒无输出强制杀进程)
- 修复轮数限制(最多 2 轮)
- 修复 Prompt 强制约束"只改出错步"
七、V5:断言层演进——从"完全信 AI"到"AI + DOM 双保险"
7.1 当时的设计思路
“传统断言太脆弱了,文案改一个字就崩。让 AI 看截图判断’任务完成了吗’,多灵活。”
7.2 踩的坑
坑 19:完全信任 AI 断言,结果被幻觉骗了
AI 说"页面显示正常,任务已完成",但实际上关键按钮根本没渲染出来。AI 的"正常"是基于它对截图的理解,不是基于 DOM 状态。
坑 20:AI 断言截图看不清
字体没加载完就截图,截图里的文字是方框或模糊,AI 根本无法正确判断。等加了 page.wait_for_timeout(3000) 等字体加载后才解决。
坑 21:AI 置信度虚高
AI 返回 confidence=0.5 但说"通过"。如果不设最小置信度阈值,低置信度的错误判断会被当成正确结果。
坑 22:断言 Prompt 里包含 trigger 词导致空响应
和 CLI 方案类似,断言相关的 Prompt 如果包含某些项目代码里的类名或函数名,API 也会返回空。解决方法是 Prompt 里不写具体代码引用,只写自然语言描述。
7.3 结论
AI 断言是"柔性判断",DOM 断言是"刚性校验"。两者互补,缺一不可。
最终方案:
- AI 断言先看截图判断业务目标是否达成
- 通过后追加 DOM 验证(
input.value、page.url、元素数量等) - 设置最小置信度阈值(如 0.7),低于则判失败
八、V6:知识库接入——让系统"越跑越聪明"
8.1 当时的设计思路
“同一个任务,上次成功了,下次应该也能成功。把成功案例存下来,下次直接用。”
8.2 踩的坑
坑 23:知识库只存成功,不存失败
早期只记录"这个任务+这个页面结构=这个脚本可以成功"。结果同一个任务换个描述、或者页面结构稍有变化,又失败了,系统从零开始修,上次修好的经验完全没复用。
坑 24:知识库没有和修复链路打通
即使存了失败案例,修复时也没有自动去查"上次这个错误怎么修的"。知识库成了摆设,AI 修复时还是全靠猜。
8.3 结论
失败比成功更值得记住。知识库存的不是"正确答案",而是"错误模式 → 修复方案"的映射。
最终方案:
- 成功案例:任务描述 + 页面结构 + 脚本
- 失败案例:任务描述 + 错误信息 + 修复后的脚本 + 修复结果
- 修复时自动搜索相似错误,把历史方案附加到修复 Prompt 中
九、V7:启动链路分离——别让每个脚本重复造轮子
9.1 当时的设计思路
“每个脚本都需要认证注入和页面加载,那就每个脚本里都写一遍呗。”
9.2 踩的坑
坑 25:认证信息分散在 N 个脚本里
每个生成的脚本里都有:
page.add_init_script("window.localStorage.setItem('token', 'xxx')")
page.goto("about:blank")
page.goto(TARGET_URL)
page.reload()
Token 过期时,要改 N 个文件。加载逻辑也不统一,有的等 3 秒,有的等 10 秒。
坑 26:启动脚本的 import 路径混乱
提取了 startup_flow.py 后,脚本计算的是 project_root,但 startup_flow.py 实际在 project_folder(脚本的父目录)。脚本运行时找不到模块,需要手动加 sys.path。
坑 27:add_init_script 时机不对
错误地在 page.goto() 之后才调用 add_init_script(),结果 token 没注入成功,页面跳转到 login.html。add_init_script 只在新页面加载时执行,已经打开的页面不生效。
9.3 结论
启动链路(认证 + 加载 + 等待)是横切关注点,必须抽离成独立模块,所有脚本统一调用。
最终方案:startup_flow.py 统一封装认证注入、页面加载、等待策略,脚本第一步调用 run_startup_flow(page)。
十、V8:配置分层——别让配置散落在代码里
10.1 当时的设计思路
“配置直接写在代码里,或者写在一个文件里,简单省事。”
10.2 踩的坑
坑 28:API Key 写死在代码里,差点提交到 git
早期直接把 api_key = "sk-xxx" 写在 Python 文件里,.gitignore 差点没拦住。
坑 29:超时设置一刀切
简单页面和复杂页面用同一个超时,要么简单页面等太久,要么复杂页面超时失败。
坑 30:无头模式调试难
headless=True 跑正式用例,出问题了看不见浏览器在干嘛,只能猜。
10.3 结论
配置要分层:系统级(全局)、项目级(每个项目不同)、用例级(每个任务不同)。敏感信息必须隔离出代码。
最终方案:
- 系统级:
config.yaml(LLM provider、API Key、超时、headless) - 项目级:
project.config.md(base_url、认证信息、浏览器类型) - 用例级:
task.txt(自然语言任务描述)
十一、完整执行流程(最终版)
开始
│
├─ 环境健康检查(Playwright / MCP / LLM API 是否可用)
│ └─ 不健康 → 尝试修复 → 仍失败 → 终止
│
├─ 扫描 tasks 目录,整理散落 txt
│
├─ 对每个用例:
│ │
│ ├─ 读取 task.txt
│ │
│ ├─ 有现成脚本且未失效?
│ │ ├─ 是 → 直接执行
│ │ └─ 否 → MCP 页面探测 → AI 生成脚本 → 保存
│ │
│ ├─ 执行脚本(每步截图 + trace + watchdog)
│ │ ├─ 成功 → AI 业务断言(截图判断 + DOM 双保险)
│ │ │ ├─ 通过 → 记录成功案例到知识库
│ │ │ └─ 不通过 → 记录失败原因
│ │ └─ 失败 → 收集截图 / 日志 / trace
│ │ │
│ │ ├─ 查知识库(历史解决方案)
│ │ │
│ │ ├─ 第 1/2 轮修复:
│ │ │ ├─ 组装修复 Prompt(错误 + 截图 + trace + 历史方案)
│ │ │ ├─ 调用 AI 修复(强制只改出错步)→ 新脚本
│ │ │ └─ 重新执行
│ │ │
│ │ └─ 第 3 次仍失败 → 终止,记录失败到知识库
│ │
│ └─ 下一个用例
│
└─ 生成最终报告(成功率、耗时、失败模式)
十二、核心口诀(用血换来的)
脚本生成
先看页面再写码,选择器要验过才能用。
执行修复
失败了给截图,修代码只改出错步。
断言验证
AI 断言看截图,DOM 验证防幻觉。
定位策略
SPA 别单吊一个选择器,role/text/class 多备几个。
认证注入
init_script 在导航前,跳 login 就是注入败。
过程观测
没有截图和 trace,失败就是黑盒。
Prompt 工程
System 管规则,User 管内容,别在四个地方写同一条规则。
知识库
失败比成功更值得存,下次修得更快。
十三、架构决策检查清单
如果你也在搭类似的系统,每做一个设计决策前,对照一下:
| 检查项 | 如果回答"否",你迟早会踩坑 |
|---|---|
| 生成脚本前,AI 是否见过页面结构? | 盲写 = 瞎猜选择器 |
| 每个选择器是否有 2-3 个备选? | SPA 一重构就崩 |
| 执行过程是否有截图 + trace + 日志? | 失败无法诊断 |
| 脚本卡住是否有 watchdog 强制终止? | 一步卡死拖垮整体 |
| AI 断言后是否有 DOM 二次验证? | AI 幻觉导致假通过 |
| 修复时是否限制只改出错步骤? | 修一步坏两步 |
| 修复是否有轮数上限? | 无限循环耗额度 |
| 知识库是否记录了失败案例? | 重复踩同一个坑 |
| 启动链路是否抽离成独立模块? | 认证逻辑分散、不一致 |
| Prompt 是否分层(System / User)? | 同一规则四处重复、前后矛盾 |
| 敏感配置是否隔离出代码? | API Key 泄露风险 |
十四、建议动手验证的架构假设
- 盲写 vs 探测后生成:故意不给页面结构,让 AI 生成脚本,观察选择器准确率
- 单选择器 vs 多策略:用一个选择器跑一周,观察 SPA 重构后的稳定性差异
- 无 trace vs 有 trace:故意制造一个失败,对比有/无 trace 时的诊断效率
- 单保险 vs 双保险断言:构造一个 AI 会误判的截图,验证 DOM 二次校验能否拦住
- 有/无知识库修复:同一个错误出现两次,对比有/无历史方案时的修复质量和速度
- 集中启动 vs 分散启动:token 过期时,对比修改一处 vs 修改 N 处的工作量
- CLI 方案稳定性:在 Windows 上连续调用 20 次 CLI 生成,观察卡死率和空响应率
最后更新:2026-05-06
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)