从0到1设计AI驱动的UI自动化测试工程:Harness多Agent架构实战

48 阅读8分钟

告别提示词乱飞,用状态机和护栏让AI稳定写出可维护的UI自动化测试

前言:你也被AI“坑”过吗?

让AI帮忙写一个UI自动化测试,它秒出一段代码,你满怀信心跑起来——结果直接报错。
更“骚”的是,有些AI为了通过测试,会悄悄修改已有的Page对象,害得其他10个用例一起挂。

为什么会这样?
因为你把「需求分析 → 代码实现 → 验证修复」三种不同能力全扔给同一个AI。它没有足够的上下文,也没有自我纠错的机制。

本文分享一套经过实战验证的 Harness设计模式:把AI装进“马具”里,用多Agent + 状态机 + 产物契约,让它稳定输出符合工程规范的UI自动化测试。全文手把手教你从零设计这套架构,并附完整代码模板。


一、什么是Harness设计模式?

Harness原意是“马具”——一套约束装置,把马的力量精确引导到正确方向。

在AI工作流中,Harness就是一套结构化的约束

  • 不是限制AI,而是给它明确边界
  • 让AI的行为可预测、可审查、可重放

Harness的四个核心要素

要素作用
① 角色边界每个Agent只做一件事
② 状态机定义流程走向,防止跳步
③ 产物契约Agent间通过文件传递信息,不靠对话记忆
④ 护栏规则明确禁止清单,防止AI“走捷径”

为什么UI自动化特别适合Harness?

  1. 步骤多:分析、实现、验证需要不同能力,天然适合拆分
  2. 有现成质量标准:分层规范、命名规范、AI定位规范都可被机器检查
  3. 失败是常态:需要结构化的失败→分析→修复→重试循环

二、为什么一个AI搞不定测试?

写一个合格的UI自动化测试,需要三种完全不同能力:

阶段核心能力常见问题
分析读需求、扫代码库、判断复用边界“现有代码能复用多少?”
实现按规范写代码、遵守分层约束“怎么写才符合项目架构?”
验证运行测试、看日志/截图、分析根因“失败了是哪一步的锅?”

解决方案:流水线 + 职责分离

用户需求
   ↓
[总控 Harness]  ← 调度 + 状态机
   ↓
[分析Agent] → _analysis.md[实现Agent] → 源码变更
   ↓
[验证Agent] → _verification.md
   ↓
总控判断 → 完成 或 修复重试

三、整体架构:总控 + 三个Agent

docs/codex/ui-automation/
├── SKILL.md                   ← 总控(状态机、调度规则)
├── agents/
│   ├── analyzer-agent.md      ← 分析Agent prompt
│   ├── implementer-agent.md   ← 实现Agent prompt
│   └── verifier-agent.md      ← 验证Agent prompt
└── artifacts/
    ├── _analysis.md           ← 分析结果(中间产物)
    └── _verification.md       ← 验证结果(中间产物)

关键设计:Agent之间不靠对话上下文传递信息,而是读写artifacts/下的文件。
这样信息不会在多轮对话中丢失,而且每一步都可审查。


四、从零设计总控层(Harness)

4.1 总控的五要素

  1. 角色定义:明确告诉AI它是总控,不要自己做实现
  2. 子Agent列表:每个Agent的职责和产物
  3. 状态机:定义流程走向和跳转条件
  4. 调度细则:每次调用子Agent传什么参数
  5. 禁止行为:8~10条禁止清单,防止越权

4.2 状态机设计(核心)

START → ANALYZE → IMPLEMENT → VERIFY → JUDGE
                                      ↓
                      ├── DONE(通过)
                      ├── IMPLEMENT(architecture_fix) → VERIFY → JUDGE
                      ├── IMPLEMENT(retry_fix)       → VERIFY → JUDGE
                      └── 失败超2次 → 停止并报告

重试计数规则

  • 架构违规 / AI定位规范违规 → 不计入重试(修复后理论上不再出现)
  • 测试运行失败 → 计入重试,最多2次

4.3 禁止行为清单(示例)

#禁止
1跳过分析阶段直接实现
2实现完成后不调用验证
3总控亲自写测试代码
4总控亲自跑测试命令
5测试失败超过2次后继续自动重试
6Agent之间只口头传递信息,不写入artifact

五、手写三个Agent Prompt(精华版)

5.1 分析Agent(Analyzer)

唯一职责:把自然语言需求 → 变成实现Agent可直接使用的结构化文档。
最关键要求:让下游Agent不需要再读源码。

输出文件 _analysis.md 必须包含:

# 需求分析结果
## 操作步骤
1. [PageClass] 操作描述
## 代码复用分析
| 步骤 | 现有代码 | 覆盖内容 |
| 需扩展 | 文件 | 新增内容 |
| 需新建 | 文件 | 用途 |
## 代码上下文(Agent2直接使用)
- 需扩展文件的现有代码结构
- 相邻Case的完整代码片段
- 测试文件模板

护栏规则:不执行测试、不启动浏览器、不写实现代码。

5.2 实现Agent(Implementer)

四种运行模式

模式触发时机核心任务
first_run首次实现只读_analysis.md,按复用计划写代码
review_fixAI定位规范违规修复Page里的定位描述
architecture_fix分层违规把定位逻辑移到正确的层
retry_fix测试运行失败读日志/截图,修复具体错误

已有函数保护机制(最高优先级)

禁止修改任何已存在的函数,除非该函数明确出现在approved_modifications清单中。
需要修改时必须报告,等待用户确认。被拒绝时改用新增方法(如clickXxxWithOptions)。

代码模板示例(Page层)

async searchByKeyword(keyword: string) {
  await this.withFallback({
    label: "searchByKeyword()",
    cssAction: async () => false,   // 强制走AI
    aiFallback: async () => {
      await this.getAgent().aiInput(
        "应用列表页面顶部的「搜索应用」输入框",
        { value: keyword }
      );
    },
    aiOnly: true,
  });
}

5.3 验证Agent(Verifier)

两层审查 + 测试执行

  1. 架构合规审查:检查测试层/服务层是否出现禁止代码(如page.locator.aiTap等),违规则直接输出ARCHITECTURE_VIOLATION,不跑测试
  2. AI-Only合规审查:确认Page方法使用了aiOnly: true
  3. 运行测试
  4. 分类错误 + 截图分析
  5. 输出_verification.md

输出格式示例

# 验证报告
## 状态
[ARCHITECTURE_VIOLATION / AI_ONLY_VIOLATION / ✅通过 / ❌失败]
## 测试结果(仅通过审查时)
- 状态:✅通过 / ❌失败
- 通过/失败用例数:1/0
## 错误详情(仅失败时)
- 类型:selector_not_found / timeout / assertion_failed
- 截图分析:页面处于XX状态,失败在第2步
- 修复方向:增加容器上下文描述

禁止行为:不修改源码、不跳过架构审查、日志禁止截断。


六、实操演示:让AI写出第一个测试

你的输入(自然语言)

帮我写一个测试:进入应用列表,在搜索框输入"autotest",验证结果列表中显示了包含该关键词的应用。

AI自动执行流程

① 分析阶段(自动)

[Analyzer] 扫描代码库...
[Analyzer] 读取最佳实践文档 app-list.md
[Analyzer] 读取相邻case: search-app.test.ts
[Analyzer] 输出 _analysis.md

展示确认摘要(唯一人工确认点):

步骤1: 导航到应用列表 → [复用] navigateToAppList()
步骤2: 输入关键词 → [扩展] 需新增 searchByKeyword()
步骤3: 验证结果 → [扩展] 需新增 verifySearchResult()
请确认是否继续?

② 实现阶段(自动)

实现Agent在AppListPage.ts中新增两个AI-Only方法,并创建测试文件:

// src/tests/app-list/search-app.test.ts
test("搜索应用 - 关键词匹配成功", async () => {
  const svc = new AppListService(ctx.page);
  await svc.navigateToAppList();
  const found = await svc.searchApp("autotest");
  expect(found).toBe(true);
}, 180_000);

③ 验证阶段(自动)

  • 静态审查 → 通过
  • 运行测试 → 通过(或失败则自动修复重试,最多2次)

七、常见问题与进阶技巧

7.1 错误类型速查

错误类型根因修复方向
selector_not_foundAI描述太模糊加容器上下文、文案、元素类型
timeout页面慢/弹窗未出现增加aiWaitFor()
assertion_failed实际值与预期不符检查断言逻辑或等待时机
import_error路径错误或未导出修正导入路径,检查index.ts

7.2 AI定位描述的好与坏

// ❌ 太模糊
await aiTap("确定按钮");

// ✅ 四要素:容器 + 位置 + 文案 + 类型
await aiTap("在「添加用户」弹窗底部右侧的「确定」按钮");

7.3 如何迁移到你的项目?

  1. 定义你的代码分层架构(Verifier用它做静态审查)
  2. 建立最佳实践文档库(Analyzer按关键词读取)
  3. 更新测试目录映射测试运行命令
  4. 替换AI方法API(如把Midscene换成你用的库)

八、总结

Harness设计模式不是一套固定代码,而是一种让AI稳定工作的工程方法

  • 角色边界防止AI越权
  • 状态机防止跳步和无限循环
  • 产物契约让信息可审查、可重放
  • 护栏规则明确禁止“走捷径”

当AI从“万能助理”变成“可调度的流水线工人”,UI自动化测试的生成和维护才能真正落地。

特种兵 + AI 的协作模式已经形成。
与其焦虑被淘汰,不如学会给AI装上Harness,让它成为你的得力干将。


参考资料:本文核心思想与架构设计参考自「霍格沃兹测试学院」公众号系列文章,结合工程实践整理。

如果觉得有用,欢迎点赞、收藏、评论交流~