从 9 月到现在,我把一套自动化测试改成由 AI Agent 执行:读需求、出测试计划、造数据、跑用例、归档证据。

两个月 422 次提交,最终收敛成四层结构。这篇讲怎么实现的。

一、整体结构

层 位置 职责
规范层 AGENTS.md(1012 行) 行为契约:能做什么、按什么顺序、什么不许做
技能层 skills/<name>/(52 个) 能力单元:SKILL.md + scripts/ + references/
执行层 automation-test-framework/ 项目配置 + 运行器 + 并发控制
沉淀层 案例/ docs/ 案例、业务链路、系统清单、遗漏模式

四层的关系:规范约束技能,技能被调度编排,执行结果沉淀成资产,资产反过来补规范。

下面按层讲。

二、规范层:把口头要求写成可执行契约

第一版规范只有 273 行,本质是操作手册。现在 1012 行,因为它开始分层:

层 内容
原则 核心原则、操作纪律
流程 执行流程、审批节点、状态机
质量 证据管理、缺陷规范
安全 数据边界、红线
演化 记忆、版本、错误处理

其中流程写得最多,因为它最容易出岔子。AI 不缺能力,缺的是”下一步做什么”的确定性。

规范靠会话启动时加载,所以它必须是文件——改一个文件,下一次会话就生效。

关键是把模糊要求翻译成可判定规则:

没用的写法 可执行的写法
注意数据安全 禁止直接改测试环境数据;造数必须走专用脚本;脚本必须支持幂等重试
及时沟通 遇到环境异常/数据冲突/覆盖不足/执行超时,必须暂停并请求确认
保证质量 每条用例产出三类证据:操作截图、请求响应、环境快照

写成这样有个副作用:你得先真的想过,才写得出来。

三、技能层:知识比代码更值钱

52 个技能,每个是一个目录:

1
2
3
4
skills/app-device-testing/
├── SKILL.md # 什么时候用 + 硬性约定
├── scripts/ # adb 操作脚本
└── references/ # 设备限制、自动化模式

SKILL.md 的 front-matter 决定路由(description 写清”什么场景用它”),正文放的是踩坑得出的硬约定,不是使用说明。

举个真实例子,真机测试那个技能里写着三条:

  1. 读屏幕只走 uiautomator dump,不要同时开 Appium 会话。 实测两者并存会互相破坏:source 超时 9 次、instrumentation crashed、截图超时,占该需求 blocked 用例的最大单一类别(11 次)。
  2. uiautomator dump 有固定成本,按次数做预算。 单次稳定 2.2 秒,开销在 uiautomator 启动,不在传输方式。所以只能减少调用次数——用缓存(写操作后失效、轮询显式 fresh)。不要写”每 700ms 轮询 8 秒”这种循环,真实代价是若干次 2.2 秒。
  3. 写断言前先 dump 真实结构。 曾按”电池健康度”关键字过滤,漏掉了文案里不含该词的”无法检测”,把已修复的行为读成失败,多跑三轮真机。

技能的价值不在脚本,在这些数字和约束。 脚本谁都能写,2.2 秒和 11 次 blocked 是跑出来的。

四、执行层:用配置描述被测端,用锁解决并发

每个被测端一个 project.json:

1
2
3
4
5
6
7
8
9
{
"schemaVersion": 1,
"id": "admin-web",
"type": "web",
"runner": "playwright",
"defaultEnvironment": "test",
"defaultDevice": "desktop",
"tags": ["regression"]
}

type 决定用哪个运行器(web→playwright、api→http runner、app→appium/adb、小程序→专用 e2e)。新增一个被测端,只加一个 json,不动代码。

并发是这套东西最容易崩的地方。lib/ 里有三类锁:

锁 解决什么 不做的后果
device-lock 同一台真机不能并发 两个用例抢设备,互相点乱
requirement-lock 同一需求不能并行执行 数据互相污染
yunxiao-lock CI 与本地执行互斥 同一环境两份配置打架

配套 rate-limit-retry:限流类失败按退避重试,其他失败直接抛。不区分错误类型的重试,等于把偶发问题变成常态问题。

五、沉淀层:案例只是其中一种

一开始我以为沉淀就是”把需求归档”。做了两个月才发现,沉淀至少分四类,而且价值完全不同:

类型 回答什么问题 粒度 生命周期 现状
需求案例 这个需求当时怎么测的 单个需求 短,随版本过期 22 个
业务操作知识 这条业务链路怎么运转 业务域 × 系统 × 操作 长,跨需求复用 45 篇
系统清单 有哪些端、谁依赖谁 系统级 长 前后端各一份
遗漏模式 这类需求容易漏什么 抽象规则 最长 起步阶段

案例:能重跑才算数

1
2
3
4
5
6
案例/<需求>/
├── README.md # 结论与范围
├── one-shot-prompt.md # 当时给 Agent 的输入
├── prompt-playbook.md # 可复用的执行步骤
├── executable/RUN.md # 可执行入口
└── snapshot/ # 计划、用例、结果、报告

executable/RUN.md 是关键——下次遇到同类需求能直接跑。没有证据的”通过”不算通过,因为既证明不了有效性,也没法复用。

业务链路:这才是能变成知识库的部分

案例会过期,但业务链路不会。比如”售后工单的补差金额怎么算”这种知识,换个需求、换个人,还是要用。

我按「业务域_系统_操作」组织这类文档:

1
2
3
4
5
docs/raw-operations/
├── 售后业务_管理后台_售后工单价格截图与补差回读.md
├── 同售业务_管理后台_三方平台商品异步导入.md
├── 价格业务_管理后台_三方爬价受理与解析.md
└── ...

每篇记四件事:链路怎么走、涉及哪些接口和字段、有哪些前置状态、哪些地方容易出错。

案例和链路的关系是漏斗:

1
22 个需求案例  →  提炼共性  →  45 篇链路知识  →  抽成规则  →  遗漏模式

前两层我已经在做了,第三层(模式库)刚起步——建了目录,只沉淀了 1 条。这一步最难,因为它要求你从”这件事怎么做的”跳到”这类事容易漏什么”。

系统清单:给 Agent 一张地图

前端项目清单、后端项目清单 各一份,写清有哪些端、分别负责什么、依赖哪些服务。

作用很直接:Agent 拿到一个需求,先看它落在哪个端、涉及哪几个系统,再决定测什么。没有这张图,它只能靠猜。

一个准入原则

知识库最容易变成文档垃圾场。我加了一条:归档时必须回答”这次有没有新的业务知识?”

  • 没有 → 只归档案例,不动链路文档
  • 有 → 更新对应链路文档,并在案例里回指

不这样约束,半年后就会有一堆没人看、也没人敢删的文档。

六、CI:把本地链路搬到流水线

云效 YAML 和 Jenkins 跑同一个入口 ci/ci-entry.sh,这样本地和流水线不会分叉。

Jenkins 那个文件里留了两条实测注释,都是血泪:

1. 必须 deleteDir()。
Jenkins 复用工作区,git checkout -f 不会删被 .gitignore 排除的文件。不清会留下上一次的 config.json、node_modules、.appium,导致静默跑在旧配置上——结果看起来全绿,其实测的是上一个版本。

2. 配置只能通过凭据绑定注入。
不要写 CONFIG_JSON="$(cat 文件)",sh 步骤默认开 xtrace,会把变量值(含测试账号)原样打进构建日志。

这两条都不是”最佳实践”,是踩过才知道的。

七、两个月的数据

指标 9 月 10 月
提交 156 266
新功能 58 105
修复 15 57
文档 28 49

累计 52 个技能、22 个需求案例、81 份测试文档。

修复从 15 涨到 57,不是质量变差:9 月是想到什么加什么,10 月真跑起来,边角问题才暴露。修复量上涨,是系统从”能跑”到”可靠”的信号。

八、几个技术取舍

为什么规范是文件,不是代码注释?
读它的是 Agent,不是编译器。注释给人看,规范给执行者看;而且改文件不用重启。

为什么用三层锁,不用数据库事务?
锁的作用域是”设备/需求/流水线”,不是数据行。用文件锁最轻,坏了能手工删。

为什么更强调度能力评估后不接?
它会更快,但也更难定位问题、更难回滚。而现在真正的瓶颈是用例质量和数据准备,不是调度速度——瓶颈不在那儿,加能力只会加复杂度。

为什么事实源只有一份?
规范和对话里各说一套,Agent 会按对话执行,结果和文档对不上。所有约束只写在规范里,临时要求要么补进去,要么明确”仅本次有效”。

九、五条边界

  1. 能读 ≠ 能写:分析阶段只读,写操作要明确指令 + 确认
  2. 造数与业务操作分离:专用脚本、不碰真实链路、重试必须幂等
  3. 失败要留现场:缺证据记为未完成,不算通过
  4. 规范是唯一事实源:口头要求不进规范就等于不存在
  5. “不做”写进文件:红线放最前面,默契不算约束

十、最后

这套系统的能力上限,不取决于模型多强,取决于你把约束写得多清楚。

模型还会更强。今天写的边界明年可能不适用,但顺序不会变:先想清楚,再让它做。


文中数据来自提交历史与仓库统计,业务细节已脱敏。