本文基于我实习期间的一段真实工作,讲的是我如何设计一个 Skill,让 Agent 把手工测试用例批量改写成能自动执行的自动化用例。
涉及的公司信息、内部地址、库表名、租户 ID 等均已脱敏,文中出现的都是化名或占位符,仅用于说明结构。
1. 背景:我到底在解决什么问题
我实习的团队当时在推测试自动化 。在这之前,测试用例都是手工用例------一个测试人员照着用例描述,一步步在页面上点、看、判断。现在要把这套流程交给测试平台上的一个 执行 Agent:我们把用例的步骤、期望写好,直接丢给它,它自己去点页面、查数据库、做断言。
听起来很美好,但我很快撞上了第一堵墙:手工用例根本没法直接喂给 Agent 跑。
手工用例是写给"人"看的。它的前置条件往往是一句业务状态描述("当前会话展示知识答案 K"),默认读的人知道怎么把环境搭出来;步骤写得很粗("点击发送");期望很模糊("不新增记录");而且几乎从不写后置清理------因为人测完自己会收拾,或者干脆不收拾。
这些"人默认懂"的东西,Agent 全都不懂。
而且我发现一件更关键的事:不同的 Agent,约束是不一样的。 我们平台的这个执行 Agent 有几个很硬的特性------它逐行读取、逐步执行,不持有整份用例的上下文 ;它执行 UI 操作时以界面截图为输入,界面一刷新,它的判断就可能变。这些特性直接决定了"用例该怎么写它才跑得动"。
所以问题就很清楚了:我需要一套改写规则 ,把手工用例翻译成这个 Agent 能稳定执行的形式。一开始我是一条条手改的,改到第几条就意识到------这些规则是可以固化下来的,与其我改,不如把改写经验做成一个 Skill,让 Agent 自己改。
这篇文章讲的就是这个 Skill 的架构:它由哪几块组成、每块负责什么、我为什么这么分、以及怎么迁移到别的模块。
2. 整体架构:为什么是三层结构
先看整体。这个 Skill(我叫它 review-platform-case-rewriter)拆成三个模块:
review-platform-case-rewriter/
├── SKILL.md ← Agent 入口:工作流 + 改写要求(精简、宏观)
├── references/ ← 业务知识库(按需读取)
│ ├── core-rules.md ← 改写核心规则(SKILL.md 的展开)
│ ├── profile-defaults.json ← 默认租户 ID、账号等常量
│ ├── tool_catalog.json ← 可用工具清单,防止 Agent 造工具名
│ ├── db-schema.json ← 库表结构 + 查询模板 + 踩坑记录
│ ├── scene-mapping.md ← 场景文案与按钮映射
│ └── ...(SOP 数据、用例模板等业务文件)
└── scripts/
└── search_reference.py ← 关键词检索,供 Agent 按需读取大文件
| 模块 | 职责 |
|---|---|
SKILL.md |
定义工作流、改写规则、质量门禁(大部分可复用) |
references/ |
存放所有业务事实(SOP、场景文案、工具参数、表结构),Agent 按需读取 |
scripts/ |
用关键词过滤大型 JSON/文本文件,供 Agent 按需读取,避免全量加载 |
这个结构不是一开始就想好的,是改着改着自然长出来的。但回头看,它背后其实是两条我一直在坚持的设计原则。
第一条:规则与知识分离。
我很快发现,我要维护的东西其实是两类:一类是"改写逻辑"(比如"前后对比断言要在操作前先记基准值"),一类是"业务事实"(比如"某个节点的真实名字叫什么""某张表有哪些字段")。这两类东西的变化频率、变化原因完全不同------逻辑是我总结出来的方法,业务事实是系统里客观存在的数据。如果把它们混在一起,改一个业务字段还要在一堆规则里翻,很容易改错。
所以我把它们彻底分开:改写逻辑变了,就改 SKILL.md / core-rules.md;业务数据变了,就改 references/ 下对应的文件,互不干扰。 这让整个 Skill 后期维护起来非常轻。
第二条:按需读取,不全量加载。
references/ 里的文件越堆越多,如果每次改写都让 Agent 把所有文件读一遍,既浪费上下文,也容易被无关信息干扰。所以我在 SKILL.md 里明确写了按需读取策略:涉及节点就读 SOP 文件,涉及提醒文案就读 scene-mapping,涉及 SQL 就读 db-schema,不相关的文件一概不读。
这里还有个细节:有些业务文件(比如工具清单、SOP 定义)很大,直接读整个文件不现实。所以我写了 scripts/search_reference.py,让 Agent 用关键词先检索、再定点读取。
最后我想强调架构里一个对复用 很关键的判断:这三层里,哪些是通用的 、哪些是业务特有的 。SKILL.md 的骨架、core-rules.md 的规则组织方式、profile-defaults.json / tool_catalog.json / db-schema.json 这三个文件的结构,都是通用的,换个模块改改内容就能用;而 SOP 数据、场景文案这些是审核平台特有的,不能直接搬。想清楚这条线,迁移的时候就知道什么该抄、什么该重写。
3. SKILL.md:Agent 的入口,只做宏观引导
SKILL.md 是 Agent 读到的第一个文件,也是整个 Skill 的入口。我给它的定位很克制:只做整体引导,不写细节。 细节全部下沉到 references/core-rules.md。这样做的好处是,SKILL.md 足够短、足够稳定,几乎可以整份复用到别的模块。
它主要包含两部分。
一是六步工作流。 我把改写这件事拆成六个有序步骤,明确告诉 Agent 每一步该干什么------从读取规则、判断测试点、按需读参考资料,到套用模板改写、输出前自检。这里我只写宏观步骤,不写"具体怎么改",是为了方便后续不断往里填细节而不动骨架。
二是改写要求。 比如工具调用要用什么格式、变量只能来自哪里、UI 操作必须精确定位到具体对象。这些是所有改写都要遵守的通用约束。
在设计的时候我特意想清楚了一件事:这份文件里,到底哪些是"通用的、别人能直接抄",哪些是"审核平台特有的、必须改"。 结论是------真正需要改的只有两处:
- 工作流第 2--4 步里的文件引用路径(换成新模块自己的参考文件);
- 改写要求里的业务特定规则(比如"节点名称必须来自参考资料"这种审核平台专属约束)。
其余的------六步工作流的结构、"按需读取不全量加载"的原则、"输出前执行质量检查"、交付前的检查清单------都是通用设计,新模块直接保留就行。我把这个"改哪、留哪"直接写进了文档,等于给未来接手的人(也包括未来的我)留了一份复用说明书。
4. core-rules.md:把"我的经验"落成"Agent 的规则"
如果说 SKILL.md 是骨架,core-rules.md 就是这个 Skill 真正的血肉。它是 SKILL.md 的展开------我之所以把它拆成单独一个文件,纯粹是因为它会不断被修正和补充,独立出来改起来方便。
我把它按"从宏观到细节"组织成了十节,但如果归一下类,其实就三块:
- 怎么写(格式类,4 节):三阶段结构(前置 / 核心验证 / 后置)、按需读取规则、工具调用格式、变量来源规则。
- 可复用模板(4 节):标准前置 12 步、标准 UI 步骤骨架、期望编写规则、后置清理 4 步。
- 防错机制(2 节):质量门禁 checklist、常见坑(每条记"错误现象 + 原因 + 正确写法")。
这里我想重点讲两条规则,因为它们最能说明"规则是从 Agent 的约束里逼出来的"------这也是我在这段工作里最深的一点体会。
规则一:变量必须提前声明。
前面说过,这个执行 Agent 逐行执行、不持有整份用例的上下文。这意味着什么?意味着如果第 9 步的期望里用到了一个变量 {``{max_msg_id_base}},那这个变量必须在第 9 步之前的某一步里已经显式记录过,否则 Agent 执行到断言时,左边根本找不到比较对象,直接就报错了。
对"前后对比"这种断言,坑更深:你得在操作之前先把基准值记下来。比如验证"某个操作不会新增数据库记录",正确的写法是:
步骤 6(操作前):执行 SQL,SELECT MAX(id) ... ,记录结果为 {{max_id_base}}
步骤 9(操作后):执行 SQL,SELECT MAX(id) ... ,记录结果为 {{max_id_after}}
期望 9:{{max_id_after}} = {{max_id_base}} (说明该阶段未新增记录)
如果你按人的直觉,只在操作后查一次,那"操作前"的基准就永远丢了。这条规则看着简单,但它是我在一次次报错里换来的。
规则二:UI 操作必须写到足够具体。
同样是因为 Agent 不持有上下文,而且它每一步是以当前界面截图为输入 去判断的。一句"点击发送"对人来说毫无歧义,但对 Agent 来说,界面上可能有好几个"发送",它不知道点哪个。所以每一条 UI 操作,都必须用已经记录过的变量或具体内容,把操作对象唯一定位出来。
我在 core-rules.md 里放了一张对照表,效果非常直观:
| 场景 | 模糊写法 | 具体写法 |
|---|---|---|
| 点击发送 | 点击"发送"按钮 | 点击当前审核内容为 {``{reply_node}} 的信息的"发送"按钮 |
| 选择节点 | 选择节点 D | 在节点列表中选择节点"安慰客户",生成话术但暂不发送;记录输入框内容为 {``{reply_node}} |
| 查看状态 | 查看节点状态 | 执行 SQL,SELECT todo_status FROM ... WHERE ... |
说句实话,即便写到这么细,Agent 执行仍然会有不稳定。我印象最深的一个案例:有个用例需要"节点矫正后,点击发送自动填充的内容"。不管我怎么写------"点击发送按钮""发送第一条信息"------Agent 都会先把矫正后的内容发出去,然后等界面刷新几秒,再义无反顾地去点了原始那条消息的发送。因为它每一步都以刷新后的新截图为输入,界面变了,行为就跟着变了。
这个案例让我彻底明白:面对一个能力有限、执行不稳定的 Agent,"把规则写清楚"比"期待模型更聪明"更早、更能解决问题。 我能做的,就是把每一处可能的歧义,都用规则提前堵死。
至于最后的"常见坑"一节,它是整个规则库里我最看重的部分------每次执行发现新问题,我就在末尾补一条"现象 + 原因 + 正确写法"。它是活的,会随着踩坑越来越厚。
5. references/:业务知识库怎么分文件
references/ 装的是"业务事实"。设计这一层时,我给自己定的规矩是:按语义拆文件,Agent 按测试点判断读哪个,绝不混在一起。
这里面有三个文件,我特别想说,因为它们的结构是通用的,换个模块改改内容就能复用:
profile-defaults.json------ 默认常量表。 存每次改写都要用到的固定常量:租户 ID、账号 ID、客户 ID 等。Agent 在固定步骤里读它,不用每次重填。新模块要用,把里面的值批量换掉就行。tool_catalog.json------ 工具目录。 存所有可用工具的名字、参数、说明。它存在的唯一目的,就是防止 Agent 凭空编造工具名或参数名------这是我早期踩过的坑,Agent 会很自信地"发明"一个不存在的工具。db-schema.json------ 库表结构。 存表名、字段名、枚举值和常用查询模板,供需要 SQL 断言时读取。它还有个我很得意的设计:额外记一个common_mistakes字段,把过去真实遇到的字段/表名错误和修复方法记进去,防止 Agent 重复犯同类错。
剩下的文件------场景文案映射、SOP 节点数据、各类用例模板------是审核平台业务特有的 ,不能直接复用。但我觉得它们的分类思路 值得借鉴:场景映射单独一个文件、业务数据单独一个文件、用例模板单独一个文件,全部按需读取。别人做新模块时,照着这个"一类事实一个文件"的思路去拆就行。
所以,新模块搭 references/ 的起手式很清晰:先把上面三个结构文件复制过来改内容,再把自己的业务数据文件按语义放进去。
6. 一个完整例子:架构是怎么串起来的
讲了这么多模块,不如看一遍它们怎么协作。下面是一个真实用例的改写前后(已脱敏)。
改写前(手工用例,写给人看):
前置:当前会话展示知识答案 K,需用目标节点 D 的话术回复。
步骤:1. 点"话术校准";2. 选节点 D 和模板;3. 确认生成;4. 点发送并查 audit 记录。
期望:输入框替换为 D 节点话术;不新增消息记录;发送后 audit 的 correction_type=1。
后置:无。
前置是业务态描述、步骤粗略、期望模糊、没有后置。
改写后(Agent 输出,写给 Agent 执行):
- 前置:套用标准 12 步链路,从"创建租户 → 导入并发布 SOP → 初始化虚拟客户 → 进入托管"一步步用工具调用搭出真实环境;
- 步骤 :登录、进入审核界面、操作前先查 MAX(id) 记为基准、精确点击"话术校准"、选真实节点"安慰客户"并记录输入框内容、操作后再查一次、查 audit 表记录关键字段;
- 期望 :全部改成可判定的断言(
{``{max_id_after}} = {``{max_id_base}}、correction_type = 1等); - 后置:套用标准 4 步清理,把租户、方案、虚拟数据、SOP 全部收拾干净。
关键是中间这张改写决策表 ------它证明了 Agent 的每个改动都不是自由发挥,而是来自某个具体的模块文件:
| 原始表述 | Agent 改写 | 依据来源 |
|---|---|---|
| "前置:业务状态描述" | 标准 12 步工具调用 | core-rules.md 标准前置链路 |
| "节点 D"(抽象代号) | "安慰客户"(真实节点名) | SOP 数据文件查得 |
| "不新增消息记录" | 操作前记 MAX(id),操作后对比 | core-rules.md 对比断言规则 |
| "send_content"(猜测字段) | content(真实字段名) | db-schema.json 验证 |
| "点击发送" | 点击内容为 {``{reply_node}} 的信息的发送按钮 |
SKILL.md UI 精确定位规则 |
| "后置:无" | 标准 4 步清理 | core-rules.md 后置模板 |
这张表是我最想给别人看的东西:它把前面所有模块------SKILL.md、core-rules.md、db-schema.json、SOP 数据------全部串了起来。架构不是画在图上的方框,而是能追溯到每一个改动的决策依据。
7. 迁移到新模块,以及它为什么能长期跑
设计这个 Skill 的时候,我一直提醒自己:它不能是一次性的。所以我总结了一套新建 Skill 的路径 和维护方式。
新写一个模块的 Skill,我推荐这四步:
- 先写最小可用模板:只包含这个模块最核心的前置链路、UI 步骤骨架、后置清理,够跑就行;
- 实际改几条用例跑一跑:看哪里报错、哪里有歧义、哪里 Agent 理解有偏差;
- 把每次踩坑写进"常见坑":积累到一定量,再归纳提炼成通用规则;
- 规则稳定后,才写格式规范和质量门禁:因为门禁要 cover 的点,在跑通之前你根本不知道是什么。
我特别想强调第 4 步的顺序。我一开始的冲动是先把规范和门禁定得漂漂亮亮,但很快发现------你没跑过,就不知道会错在哪,写出来的门禁全是想当然。先跑、先踩坑、再固化,才是对的。
维护上,我把变更分成两类,对应改不同的文件:
| 变更内容 | 更新哪个文件 |
|---|---|
| SQL 字段名/表名有误 | db-schema.json(补 columns 或 common_mistakes) |
| 工具名/参数名有误 | tool_catalog.json |
| SOP 节点信息变化 | SOP 数据文件 + profile-defaults.json |
| 新场景的提醒文案/按钮 | scene-mapping.md |
| 改写工作流或输出要求调整 | SKILL.md |
这张表其实就是第 2 节"规则与知识分离"原则的落地:是经验规律变了,改规则文件;是业务事实变了,改知识文件。 分得清清楚楚。
而这套架构最让我满意的一点是:改完,下次运行立刻生效,不需要任何部署。 它就是一堆 Markdown 和 JSON,改哪生效哪。
8. 写在最后:一点可迁移的方法论
回头看,这个 Skill 的本质,是把人脑子里的隐性经验,结构化成 Agent 可执行、可维护、可复用的规则与知识。
如果要把这套东西抽出来给别人用,核心就三样:
- 一个定义工作流的
SKILL.md; - 一个细化规则的
core-rules.md; - 一组按需读取的业务知识文件。
而支撑它的两条原则------规则与知识分离 、按需读取不全量加载------不限于审核平台,我相信对任何"要把人的经验交给 Agent 去执行"的场景都成立。
这段工作给我最大的收获,其实不是写了个 Skill,而是想通了一件事:当你面对的 Agent 能力有限、执行还不稳定时,与其等一个更聪明的模型,不如把约束摸清楚,用一套清晰的架构把经验固化下来。 好的架构,往往比更强的模型更早解决问题。