AI UI 测试平台从 0 到 1:8 次跑通一条 19 步用例的实战复盘 AI UI 测试平台从 0 到 18 次跑通一条 19 步用例的实战复盘系列文章《AI 测试平台从 Demo 到生产》(1/N)本文是 TestStar 项目 Tier 1 稳定性验证的工程实录所有数字、所有错误、所有我们当时没想到的事故都是真实发生的。写在前面做 AI UI 测试平台最容易掉进去的坑是demo 跑得漂亮信心爆棚上生产后第三天开始怀疑人生。这篇文章不讲概念不讲AI 改变测试的宏观叙事。只讲一件事我们在 Tier 1 稳定性验证里用一条 19 步的真实业务用例登录 → 进 SQL 控制台 → 输 SQL → 执行 → 断言连续跑了 8 次发生了什么怎么解决的以及每个解决方案背后踩过哪些坑。如果你也在做 AI 测试平台或者正在评估一个 AI 测试工具能不能上生产——这篇值得读一遍。一、为什么是一条用例 8 次很多团队做 AI 测试稳定性验证会跑几十条用例各跑一次统计整体通过率。我们选了一条用例跑 8 次原因是稳定性不是通过率是同一个失败模式会不会在多次运行中复现。一条用例跑 8 次每次失败原因都不同——这说明系统里没有一个稳定的失败模式是好事说明每次失败都有具体原因可查。一条用例跑 8 次第 3 次开始都是同一个错——这说明底层有 bug需要修。Tier 1 验证的 8 次跑通本质是在测系统的可重复性 失败的可解释性。二、原始数据8 次跑通的完整时间线用例登录 → 进入 SQL 控制台 → 输入 SQL → 执行 → 断言19 步Run模式状态耗时total_tokensAI 调用次数备注1headedfailed367s480,32999数据问题表不存在2headedfailed104s74,21318浏览器驱动崩溃3headedsucceeded178s191,44040修 SQL 后首次跑通4headedsucceeded122s93,12719cache 部分命中5headedsucceeded99s57,01917cache 高命中6headless假失败115s54,46016子进程成功worker 卡死7headedsucceeded114s54,46016修记忆后 cache 完整命中8headlesssucceeded65s——记忆异步化后关键数据Token 节省88.7%480K → 54KAI 调用次数节省83.8%99 → 16耗时节省68.9%367s → 114s修后用例稳定率100%5/5这四个数字不是孤立优化的结果而是cache 工程卫生 自愈的整体效果。下面展开每个 Run 背后的坑。三、Run 1第一次跑就挂了挂在数据上症状超时失败没有任何有用的错误信息。排查路径这是很多新人会踩的坑列出来供大家避坑# Step 1看日志用 wc -l 看大小wc-lbackend/logs/vision_star/runs/run_001/*.log# Step 2找最后一行错误grep-nError\|Timeout\|failedbackend/logs/vision_star/runs/run_001/agent.log|tail-5# Step 3看 AI 最后思考了什么tail-200backend/logs/vision_star/runs/run_001/ai.log# Step 4看浏览器实际状态ls-lhbackend/logs/vision_star/runs/run_001/screenshots/根因测试用的数据表daily_orders_2024_q4不存在AI 一直在等这个表加载超时。修复补数据 在测试 setup 里加表存在性断言。教训AI 测试不像传统测试有明确的报错。AI 说超时可能是 50 种原因定位必须靠日志三件套——浏览器日志 / AI 日志 / 应用日志缺一不可。四、Run 2浏览器驱动崩了这次是真崩症状跑 104 秒后整个 run 中断exit code 非零。排查# 看错误码catbackend/logs/vision_star/runs/run_002/agent.log|grep-iexit\|signal\|crash# 看浏览器进程是否还活着psaux|grep-ichrom|grep-vgrep根因Puppeteer 在某个边缘场景下崩溃具体触发条件没完全复现但和登录态反复切换有关。修复subprocess 加 900 秒硬超时之前是软超时AI 卡住不会杀进程加process.on(SIGTERM)处理器确保子进程能优雅退出浏览器驱动崩溃后自动清理残留进程pkill -f chrom关键代码片段抽象版可直接套用# 伪代码执行引擎的进程边界importsubprocessimportsignalclassVisionRunner:defrun_case(self,case_yaml:str,timeout_sec:int900):procsubprocess.Popen([midscene,--yaml,case_yaml],stdoutsubprocess.PIPE,stderrsubprocess.PIPE,preexec_fnos.setsid# 独立进程组便于整组 kill)try:stdout,stderrproc.communicate(timeouttimeout_sec)ifproc.returncode!0:raiseEngineCrashError(stderr.decode())returnself._parse_result(stdout)exceptsubprocess.TimeoutExpired:# 整组杀避免僵尸进程os.killpg(proc.pid,signal.SIGTERM)raiseTimeoutError(fRun exceeded{timeout_sec}s)教训进程隔离是 AI 测试平台的硬性需求。AI 浏览器驱动一旦崩了绝不能拖垮主进程。subprocess 进程组 硬超时三件套缺一不可。五、Run 3-5cache 命中带来的省钱三连cache 是 TestStar 接入的底层引擎自带的机制——相同页面 相同元素描述的查询结果会被复用不需要每次重新调 AI。Run 3修数据后第一次跑191K tokens178s。Run 4cache 部分命中93K tokens122s。Run 5cache 高命中57K tokens99s。为什么 cache 这么重要因为token 不是优化问题是生产化的生死线。1000 个用例 × 每天跑 2 次 × ¥3/次 ¥6000/天 ¥18万/月 1000 个用例 × 每天跑 2 次 × ¥0.34/次 ¥680/天 ¥2万/月差 9 倍。任何 AI 测试平台没有 cache 不应该上生产——这不是夸张是事实。cache 接入的关键配置脱敏版# config.yamlvision_star:cache:enabled:truebackend:sqlite# 或 redisttl_days:7similarity_threshold:0.85# 元素描述相似度阈值max_entries:100000关键参数similarity_threshold 0.85低于这个相似度不命中宁可重算不命中错ttl_days 7超过 7 天强制失效避免 UI 改版后用旧 cachebackend单机用 sqlite集群用 redis六、Run 6最关键的假失败症状headless 模式第一次跑115s 后 worker 报failed。但子进程的 exit code 是 0成功。排查# Step 1子进程日志说执行成功grepExit codebackend/logs/vision_star/runs/run_006/cli.log# 输出Exit code: 0# Step 2但 API 返回的是 Nonecurlhttp://localhost:8000/api/v1/runs/6# {status: failed, error: null}# Step 3worker 线程卡在哪里py-spy dump--pid$(pgrep-fvision_star.worker)|head-50# 输出卡在记忆服务的 sync_vector_search()# Step 4记忆服务的调用栈SELECT * FROM trace_event WHERErun_id6ANDevent_typememory_callORDER BY ts DESC LIMIT20;# 输出调用 19 次每次都成功但 worker 主线程阻塞在第 16 次之后根因每跑一步 AI 操作记忆服务都要同步执行一次向量检索 写入。等到第 19 步worker 线程已经被同步阻塞队列彻底拖死。修复把同步写入挪到独立后台线程主流程不等它。关键代码片段# 伪代码异步记忆写入importqueueimportthreadingclassAsyncMemoryStore:def__init__(self):self._add_queuequeue.Queue(maxsize1024)self._ensure_async_worker()def_ensure_async_worker(self):ifnothasattr(self,_worker)ornotself._worker.is_alive():self._workerthreading.Thread(targetself._async_add_worker,daemonTrue)self._worker.start()def_async_add_worker(self):whileTrue:try:itemself._add_queue.get()self._do_write(item)# 同步写入但不阻塞主流程exceptExceptionase:logger.exception(fmemory write failed:{e})defadd(self,memory):# 不等待立即返回try:self._add_queue.put_nowait(memory)exceptqueue.Full:logger.warning(memory queue full, dropping)defsearch(self,query):# 搜索保持同步自愈和查询依赖returnself._do_search(query)Run 7 验证114s 跑通token 54Kcache 完整命中AI 调用 16 次。教训写进团队反模式清单任何在请求路径上的同步阻塞都可能让整个平台卡死。这条值得写进每个 Harness 层的反模式清单。七、Run 8headless 模式跑通耗时 65stoken 进一步下降。Run 7 是 headed 模式带浏览器界面Run 8 是 headless无界面。headless 少了一些截图交互开销所以更省时。生产建议本地调试用 headed方便观察 AI 行为CI 跑用 headless省资源真实环境复现用 headed更接近用户场景八、自愈层让失败不再卡死前面 5 个失败 case 都是硬失败——挂了就是挂了没人救。Tier 1 收尾阶段我们加了自愈层。原理不复杂# 伪代码自愈主流程defheal(run,case,error):# Step 1分类失败categoryclassify_failure(error)# category ∈ {timeout, network, element_not_found,# assertion_failed, ui_changed, login_expired,# data_accumulated, deadloop, ...}# Step 2根据分类选策略ifcategoryelement_not_found:returndeep_locate_retry(case)# 深度重定位elifcategoryassertion_failed:returnai_diagnose(case,error)# AI 诊断elifcategorynetwork:returnbackoff_retry(run)# 退避重试# ...# Step 3生成修复 patchpatchgenerate_patch(case,error)# Step 4置信度门控ifpatch.confidenceCONFIDENCE_THRESHOLD:apply_patch(case,patch)returnretry(run)else:# 低置信度弹窗交人工审核returnawait_human_review(case,patch,error)5 个故意失败用例实测救回率Case失败类型结果说明case1network不救回DNS 不可达物理失败case2element救回元素定位修复case3assertion救回断言条件更新case4timeout不救回1s 极端超时测试设计错误case5rename救回元素改名修复可修复失败救回率3/3 100%总体救回率3/5 60%case41s 极端超时我们讨论过要不要救——比如让 AI 自动延长等待时间。最后决定不救。因为这就是测试设计错误用例硬等 1 秒但页面元素就是需要 3 秒才出现。AI 救了等于帮开发者掩盖一个本该重写用例的真正问题。四个字诚实 虚增。九、记忆系统知识库注入的具体做法记忆分三类类型内容例子Wiki页面元素结构化描述“登录按钮密码登录 tab登录表单底部”Skill成功模式蒸馏“表单提交前要等 500ms 防止 race condition”失败记忆失败指纹修复方案“case X 失败原因Xpath 改了 → 修复改 aiAct”知识库注入Wiki 知识的具体应用# db_knowledge/sql_console.yamlelements:password_login_tab:desc:密码登录location:登录页顶部 tab 区域phone_input:desc:手机号输入框location:登录表单第一行password_input:desc:密码输入框location:登录表单第二行login_button:desc:登录按钮location:登录表单底部instance_card_connect:desc:实例卡片连接按钮location:实例列表每行右侧sql_editor:desc:SQL 编辑器location:SQL 控制台中央execute_button:desc:执行按钮location:SQL 编辑器右下角注入流程执行 yaml → 静态分析 → 命中元素 → 注入 location 提示 ↓ aiAct: 点击登录位置登录表单底部 ↓ AI 优先用结构化锚点定位 → 失败回退纯视觉理解实测结果冷 cachetoken 297K注入让 prompt 变长热 cachetoken 57K恢复正常稳定性显著提升解决了之前密码 vs 密码登录的混淆问题工程判断先做单用例深度跑 2 周看哪些字段真有用再决定要不要抽象。过早抽象是工程灾难。十、CI 接入36% 通过率的故事第一次接入 CI 时跑出 36% 通过率。Stakeholder 第一反应“这么差怎么上生产”我们的回应是“第一次跑出 36% 是正常的。重要的是 exit code 是 0CI 没阻塞、KPI 被采集自愈率/token/耗时、数据写入 DB生产环境可以持续观察。未来 2 周的真实数据积累才是这次 CI 接入的真正价值所在。”CI smoke 脚本脱敏版#!/bin/bash# scripts/ci_visionstar_smoke.shset-eecho TestStar CI Smoke # 1. 启动服务./scripts/start.sh --ci-mode# 2. 等待健康检查foriin{1..30};doifcurl-sfhttp://localhost:8000/api/v1/health/dev/null;thenechoService readybreakfisleep2done# 3. 跑 smoke 用例curl-XPOST http://localhost:8000/api/v1/vision-star/joint-runs\-HContent-Type: application/json\-d{suite_id: smoke_suite, headless: true}# 4. 等待结果sleep300# 5. 采集 KPIcurl-shttp://localhost:8000/api/v1/vision-star/dashboard/overviewci_metrics.json# 6. 验证关键指标python scripts/verify_kpis.py ci_metrics.jsonecho CI Smoke Complete 端到端验证2 suites / 88 runs36% 成功率自愈控制功能正常CI 通过exit 0很多团队不理解36% 成功率意味着失败。对一个新集成的 AI 测试平台第一次跑 CI 出现 36% 成功率是正常的。重要的是数据被采集、被持续观察。十一、可观测性6 类日志 SSE 实时流AI 测试失败时看到的常常是“assertion failed”一份 JSON 报告一堆 AI 思考过程的文本但这些文本可能是编造的为了让 QA 能真正定位问题TestStar 把执行过程的日志分成 6 类日志类型内容agent.log浏览器引擎主日志device-task-executor.log设备任务执行器日志ai.logAI 每一步思考记录plan.logAI 规划的过程task.log任务执行结果cli.logCLI 子进程输出SSE 实时日志流让 QA 边跑边看// frontend: 订阅 run 日志consteventSourcenewEventSource(/api/v1/runs/${runId}/logs/stream);eventSource.onmessage(event){constlogJSON.parse(event.data);appendToUI([${log.level}]${log.message});};好处AI 测试和传统 CI 测试不一样——AI 测试适合边跑边看。QA 能实时判断 AI 是不是在胡思乱想必要时人工介入。十二、经验总结5 条铁律Tier 1 跑完团队把血的教训写成 5 条铁律铁律一每条任务必须有不做会怎样防止为了看起来忙加任务。每个 TODO 必须能回答两个问题不做会怎样做了能避免什么铁律二P1 任务严格串行每个 P1 完成 Tier 1 验收 KPI 后才进下一个避免并发堆债。铁律三P2 标记暂禁是默认任何新建议都要回答为什么不做 Tier 1/2 的核心。铁律四做完 P0/P1 必须更新 tier report不沉淀等于没做。铁律五不使用 TODO/TBD/FIXME 作为任务名每条要明确做什么。内部金句“扩生成能力等于扩垃圾”——Tier 1 主动清到只剩 1 个用例就是为了不分散精力。十三、什么场景能用什么不能用能用已实测表单 列表 详情的中后台系统OA、CRM、运营后台、BI 平台、数据查询控制台有稳定结构化页面的 Web 应用重复执行的回归测试不建议已踩坑Canvas / WebGL 应用游戏、可视化、白板—— AI 看不懂像素含义强风控页面验证码、滑块、行为检测—— AI 没有真人环境极短用例5 步—— cache 命中收益不抵启动开销跨域联邦登录OAuth、SAML 等多步跳转—— 状态太复杂强动态 SPA每次刷新 DOM 完全重排—— 难以稳定锚点承认边界不丢人假装没有边界才丢人。十四、踩坑速查表最后给一个速查表遇到问题按这个顺序排查现象第一步排查第二步排查第三步排查Run 失败无错误看 6 类日志最后 100 行看 AI 思考路径看浏览器截图序列Run 超时看 agent.log 时间分布看 AI 调用次数看 worker 线程栈Token 异常高看 cache 命中率看 prompt 长度看 AI 调用重复模式AI 找不到元素看页面截图看 Wiki 知识库覆盖看元素描述相似度自愈改坏用例看 patch diff看置信度看错误信号源CI 通过率突降看 Run 时间分布看环境变量变化看数据快照系列预告 互动这是 TestStar 实战系列的第一篇后续会写AI UI 测试自愈层实战16 类失败分类的具体实现记忆系统的工程化从本地 JSON 到语义检索的演进多端测试接入Android / iOS / Harmony / Desktop 的实战真实 CI 数据复盘2 周生产数据的真实失败模式分析AI 测试的诚实 虚增实践怎么跟 stakeholder 解释 60% 通过率如果你也在做 AI 测试平台遇到了类似的坑或者有不同的解法欢迎评论区交流。每一篇评论我都会认真看认真回。如果这篇对你有帮助点赞、收藏、关注专栏是最大的支持。我们下篇见。本文为系列文章首发所有工程数据均来自 TestStar 项目 Tier 1 验证2026-08。原创声明本文为 TestStar 团队原创转载请联系并注明来源。