
1. 项目概述SKILL.md 不是文档是 Agent 的“上岗说明书”你有没有遇到过这种情况团队里新来了一个测试同学刚配好环境、装完工具对着一堆自动化脚本和接口文档发懵——“这个用例跑不通是环境问题参数写错了还是根本没调对服务”更常见的是开发扔过来一个“已自测通过”的模块测试人点开代码仓库看到满屏的 Python 脚本、YAML 配置、Dockerfile 和 README.md却找不到一句能直接告诉ta“这个模块该测什么、怎么测、测出问题往哪报”的人话。不是代码不规范而是执行意图缺失。而“一个 SKILL.md 就能让 Agent 干活”这句话乍听像营销话术实则是当前测试工程化演进中一个非常实在的落地切口。它背后指向的不是某个神秘框架或黑盒模型而是一套轻量、可读、可维护、可验证的能力契约Capability Contract表达范式。SKILL.md 的本质是把过去散落在会议纪要、口头约定、Jira 描述、Postman Collection 注释、甚至开发者脑中的“测试常识”结构化地沉淀为一份机器可解析、人类可理解、新人可速查的技能说明书。我带过的几个测试团队在落地 CI/CD 流水线时都卡在同一个环节自动化用例能跑通但一旦上游接口变更、下游依赖升级、或环境配置微调整个流水线就频繁红灯排查时间远超修复时间。后来我们反向梳理发现83% 的失败用例根源不在代码逻辑而在执行上下文丢失——没人明确记录“这个用例依赖 mock-server 的 v2.3.1 版本且必须开启 strict-mode若切换为 v3.x则需同步更新 payload 中的 timestamp 字段校验规则”。这些细节从来不会出现在单元测试注释里也不会被 pytest 的 markers 捕获但恰恰是 Agent无论是 Jenkins Pipeline、GitHub Action 还是自研调度引擎真正需要的“上岗条件”。SKILL.md 就是来补上这一环的。它不替代测试用例本身也不取代自动化框架而是站在“谁来干、怎么干、干到什么程度才算合格”的视角定义一个最小可执行单元的能力边界。比如一份真实的 SKILL.md 可能长这样# 【接口健康巡检】SKILL-007 ## 能力目标 每5分钟自动调用订单服务 /v1/order/status 接口验证响应状态码200、body 中 status 字段值为 processing 或 completed超时阈值≤800ms。 ## 执行前提 - 环境变量 ORDER_SERVICE_URL 已注入格式https://api.order-svc.staging.example.com - Token 由 auth-service 提供有效期≥15分钟需每12分钟刷新一次 - 依赖 mock-order-db 容器已启动且数据快照版本为 snapshot-20240520 ## 输入参数 | 参数名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | order_id | string | 是 | — | 需为16位十六进制字符串如 a1b2c3d4e5f67890 | | timeout_ms | integer | 否 | 800 | 建议不修改仅调试时临时下调 | ## 输出结果 - SUCCESS返回 JSON含 status, latency_ms, check_time 字段 - FAILURE返回错误码ERR_TIMEOUT / ERR_INVALID_RESP / ERR_AUTH_EXPIRED及上下文快照含请求头、原始响应体截断 ## 验证方式 - 本地执行make skill-run SKILLSKILL-007 ORDER_IDa1b2c3d4e5f67890 - 流水线集成触发 test-skill-007 job日志中应出现 [SKILL-007] ✅ PASS (latency321ms)你看它没有一行代码却比任何一段 Python 脚本更能告诉一个新人“你现在要做的就是确保这个检查永远绿着如果红了先看是不是 token 过期再查 mock-db 快照版本最后才动代码。”这正是测试人入门最需要的——从“不知道该干什么”到“知道第一步该查什么”的确定性跃迁。所以这不是教你怎么写 Markdown而是教你如何用一种极简、无歧义、带约束的语言把模糊的“测试职责”翻译成精确的“执行指令”。它面向的不是 AI 大模型而是每一个需要快速上手、独立交付、减少沟通成本的测试工程师。接下来我们就一层层拆开这份薄薄的 SKILL.md 文件到底在系统里扮演什么角色、怎么设计才不翻车、实际落地时哪些坑我踩过三次以上。2. 核心设计逻辑为什么是 .md为什么必须结构化很多人第一反应是“不就是个文档吗我们早就有测试用例表、有 Confluence 页面何必另起炉灶”这个问题问到了根子上。关键不在“有没有文档”而在“文档能否被机器消费、能否被流程驱动、能否被新人秒懂”。我们来对比三种常见形态的缺陷Confluence / Notion 页面信息丰富但耦合度高。一个页面里混着背景介绍、操作截图、历史变更、负责人名单、关联需求链接……Agent比如一个定时巡检脚本无法从中提取出“超时阈值是多少”“token 怎么刷新”这种原子级指令。它需要的是结构化字段不是段落文本。JSON/YAML 配置文件机器友好人类痛苦。比如把上述 SKILL-007 写成 YAMLskill_id: SKILL-007 target_endpoint: /v1/order/status timeout_ms: 800 auth: service: auth-service refresh_interval_min: 12 dependencies: - name: mock-order-db snapshot: snapshot-20240520看起来很干净但问题来了当测试新人第一次看到这个 YAML他怎么知道refresh_interval_min: 12是指 token 刷新频率而不是数据库连接池回收间隔YAML 没有语义注释能力所有字段含义必须靠外部文档或团队默契来传递一旦人员流动配置就变成天书。纯代码注释如 Python docstring最危险。注释和代码极易脱节。“这个函数支持重试”——但实际代码里 retry0“默认超时 1s”——但某次重构把 timeout 参数删了注释却忘了删。代码注释的权威性永远低于代码本身而 SKILL.md 的定位恰恰是高于代码的契约层它定义“应该怎样”代码负责“如何实现”。那么为什么最终选中.md不是因为它多高级而是它在“人类可读性”和“机器可解析性”之间找到了一个极其务实的平衡点。Markdown 本身是纯文本没有任何运行时依赖它的语法极简#表示标题|表示表格 表示代码块主流语言都有成熟解析库Python 的markdown-it-pyNode.js 的markedGo 的blackfriday更重要的是它天然支持语义分层——你可以用## 能力目标明确声明意图用### 执行前提强制约束上下文用表格固化参数契约用代码块嵌入可执行命令。这种分层不是为了好看而是为了让解析器能稳定地提取出结构化数据。我们团队实测过用正则硬解析 Confluence HTML 导出内容失败率高达 47%因编辑器自动插入 span 标签、换行符不一致用 YAML 解析器加载带中文注释的配置30% 的 case 会因编码或缩进空格报错而用markdown-it-py解析 SKILL.md配合预定义的 heading 标签规则如匹配## 执行前提后的所有段落提取准确率稳定在 99.2%且解析耗时平均仅 12ms。但这不意味着随便写个.md就行。真正的设计难点在于结构化边界的划定。我们反复迭代了 5 个版本最终锁定 6 个强制区块其余为可选每个区块解决一个明确问题区块名称解决的核心问题为什么不能合并实测教训## 能力目标定义“这件事的本质是什么”若与“输入参数”混写易导致目标漂移如把“验证接口可用性”写成“验证返回 JSON 格式”曾有团队将“兼容 IE11”写进目标结果自动化脚本真去启 IE 驱动拖垮整个流水线## 执行前提声明“不做这些这事必败”若并入“环境准备”章节会被当成一次性初始化任务忽略其持续有效性如 token 过期需刷新某次压测中mock-db 快照未更新但前提区块被误认为“只在部署时检查”导致连续 3 小时误报## 输入参数约束“用户能控制什么”若用自然语言描述如“传一个订单号”无法生成 CLI 参数校验或 API 文档新人传入ORDER_ID123非十六进制脚本静默失败日志只显示ERR_INVALID_RESP无具体原因## 输出结果定义“成功/失败的唯一判据”若与“验证方式”合并会混淆“结果形态”和“验证动作”如把“返回 JSON”和“用 jq 校验字段”混为一谈某次故障中输出定义为{code:0}但实际接口返回{code:0}字符串因未明确定义类型校验逻辑漏掉## 验证方式提供“立刻上手的最小路径”若写成“参考 Wiki 链接”新人需跳转 4 个页面才能找到命令85% 的新人首次执行失败源于找不到make skill-run这条命令而非逻辑错误## 版本与变更锁定“这份契约何时生效”若无此区块多人协作时无法判断“我改的这个参数是否已被下游流水线拉取”A 同学更新 timeout 为 1000msB 同学的流水线仍用旧版导致误判超时你会发现这 6 个区块本质上是在模拟一个“最小可行测试用例”的全生命周期它要做什么目标、需要什么前提、接受什么输入参数、产出什么结果、怎么确认对错验证、以及何时生效版本。没有一个区块是装饰性的每一个都对应着自动化执行链路上的一个真实断点。这也是为什么我们坚持“一个 SKILL.md 对应一个可独立执行、可独立监控、可独立告警的最小能力单元”而不是把它做成“测试用例集合文档”。提示不要试图在一个 SKILL.md 里塞进多个能力。曾有团队写了一份《全链路支付测试 SKILL》包含 12 个子场景结果每次修改一个子项整个文件的 Git diff 就长达 200 行Code Review 彻底失效。正确做法是拆成SKILL-PAY-001-下单,SKILL-PAY-002-支付回调,SKILL-PAY-003-退款……每个文件专注一件事。3. 实操要点拆解从零开始写一份可落地的 SKILL.md现在我们进入最干货的部分手把手带你写出第一份真正能驱动 Agent 的 SKILL.md。别担心不需要背语法我们按“目标→前提→参数→结果→验证→版本”这条主线逐块填充并告诉你每一处背后的实操考量。3.1 能力目标用“动词宾语约束”句式杜绝模糊表述这是整份文档的“宪法”必须一句话说清。我们不用“确保”“保证”“验证”这类弱动词而用强动作动词 明确宾语 可量化约束。例如❌ 错误示范“检查订单接口是否正常工作。”问题在哪“正常工作”是主观判断没有客观标准没说明检查频次、超时阈值、数据范围未定义“异常”的具体表现是 500 错误还是 200 但 body 为空。✅ 正确写法“每5分钟调用/v1/order/status?order_id{order_id}要求 HTTP 状态码为 200响应 body 中status字段值为processing或completed端到端延迟 ≤800ms。”这里拆解三个关键点动作明确“调用”比“检查”更精准指定了 HTTP 方法默认 GET和完整路径约束量化≤800ms是可测量的 SLA不是“尽快”processing或completed是枚举值不是“非失败状态”上下文完整{order_id}是占位符暗示参数来源避免写死如order_idabc123否则无法复用。实操技巧写完目标句立刻问自己三个问题如果只看这一句话一个完全不懂业务的人能否写出对应的 curl 命令如果这个目标失败监控系统能否根据这句话自动生成告警标题如“订单状态接口延迟超 800ms”如果半年后回看能否不查代码就判断这次失败是否属于该目标范畴如果任一答案是否定的就重写。3.2 执行前提列出“不做就必然失败”的硬性依赖这不是环境搭建指南而是运行时守门员。它回答“在技能执行的每一毫秒以下条件必须为真”。我们只列必要项且每项必须满足两个条件可检测、可修复。常见误区是把“安装 Python 3.9”这种一次性操作写进来。这是基础设施管理范畴不属于 SKILL.md。真正该写的是那些动态、易变、影响执行成败的依赖。✅ 正确示例## 执行前提 - ORDER_SERVICE_URL 环境变量已设置且值以 https:// 开头末尾无 /如 https://api.order-svc.prod.example.com - AUTH_TOKEN 环境变量存在且为 JWT 格式含 . 分隔的三段 Base64 字符串 - mock-order-db 容器处于 running 状态且 docker exec mock-order-db pg_isready -U postgres 返回 accepting connections为什么这样写可检测每一条都给出了明确的检测命令pg_isready或校验规则JWT 格式、URL 格式可修复如果pg_isready失败运维可立即执行docker restart mock-order-db如果AUTH_TOKEN格式不对测试人可重新执行./refresh-token.sh动态性容器状态、token 有效期、URL 配置都是运行时可能变化的。注意所有前提项必须在 Agent 执行前自动校验。我们在调度器里嵌入了一段通用校验逻辑读取 SKILL.md 的## 执行前提区块提取每行开头的-后的描述匹配预设的正则模式如.*pg_isready.*→ 执行该命令.*JWT.*→ 用 Python 脚本校验 token 结构任一失败则终止执行并上报具体原因。这比让脚本自己处理“token 过期”逻辑清晰 10 倍。3.3 输入参数用表格定义契约拒绝自然语言描述这是最容易被轻视也最常引发线上事故的部分。很多团队用一句话描述“传一个订单 ID”结果新人传了12345数字而接口要求字符串\12345\或者传了ABC-123带横杠而校验逻辑只认纯数字。SKILL.md 的表格就是来堵死这种歧义的。✅ 正确表格结构参数名类型必填默认值说明order_idstring是—16位小写十六进制字符串不含0x前缀如a1b2c3d4e5f67890timeout_msinteger否800允许范围500~2000单位毫秒关键设计点类型必须精确string不等于textinteger不等于number。我们禁用any、object等模糊类型因为 Agent 无法据此生成参数校验默认值写—而非nullnull在某些语言里是合法值—表示“无默认必须传”说明栏是技术规格不是使用提示不写“请务必传入”而写“16位小写十六进制字符串”后者可直接转为正则校验^[a-f0-9]{16}$范围约束显式化500~2000比“建议值”更有约束力Agent 可在执行前做范围检查。实操心得我们给所有参数加了“校验钩子”。当 Agent 解析到order_id参数时会自动调用validate_hex16(value)函数解析到timeout_ms时调用validate_range(value, 500, 2000)。这些函数是全局共享的一处修复全部生效。新人即使不懂正则只要按表格填就不会传错。3.4 输出结果定义“成功”与“失败”的唯一真理这是自动化决策的基石。很多团队的失败日志只写ERROR: request failed然后测试人要花 20 分钟翻网络包、查服务日志。SKILL.md 的输出区块就是要让 Agent自己说出失败原因。✅ 正确写法## 输出结果 - SUCCESS返回 JSON 对象包含 statusstring、latency_msinteger、check_timeISO8601 时间字符串三个字段 - FAILURE返回标准错误码ERR_TIMEOUT / ERR_INVALID_RESP / ERR_AUTH_EXPIRED及上下文快照含请求头、原始响应体前 512 字节为什么强调“标准错误码”它让监控系统能做聚合分析ERR_TIMEOUT高发 → 查网络或服务性能ERR_AUTH_EXPIRED高发 → 查 token 刷新逻辑它让告警消息可读钉钉机器人不再发“技能执行失败”而是“【SKILL-007】ERR_AUTH_EXPIREDtoken 已过期请检查 refresh-token.sh 执行频率”它让问题定位加速新人看到ERR_INVALID_RESP立刻知道要去比对响应体结构而不是怀疑网络或代码。提示错误码必须全局唯一且语义清晰。我们禁用ERROR_001这类编号强制用ERR_ 动词过去分词TIMEOUT,EXPIRED,INVALID 名词RESP,TOKEN,DB。这样光看名字就能猜出问题域。3.5 验证方式提供“30 秒内可执行”的最小路径这是新人上手的第一道门槛。我们绝不写“详见 Wiki”或“联系负责人”而是给出绝对路径、绝对命令、绝对预期输出。✅ 正确写法## 验证方式 - 本地执行在项目根目录下运行 bash make skill-run SKILLSKILL-007 ORDER_IDa1b2c3d4e5f67890预期输出[SKILL-007] ✅ PASS (latency321ms)或[SKILL-007] ❌ ERR_AUTH_EXPIRED (token expired at 2024-05-21T08:15:00Z)流水线集成在 GitHub Actions 中触发test-skill-007job查看Run Skill Test步骤日志搜索✅ PASS或❌ ERR_*关键细节 - **命令可复制粘贴**make skill-run ... 是真实存在的 Makefile 目标不是示意 - **预期输出可 grep**✅ PASS 和 ❌ ERR_* 是固定前缀CI 日志可直接用 grep -E (✅ PASS|❌ ERR_) 提取结果 - **环境明确**“在项目根目录下”消除了路径困惑“GitHub Actions”指明平台避免新人去 Jenkins 找。 我们甚至为每个 SKILL.md 自动生成了 verify.sh 脚本。当你写完文档运行 ./gen-verify.sh SKILL-007它会 1. 从文档中提取 make skill-run ... 命令 2. 构造一个临时 Docker 容器预装所有依赖curl、jq、python3 3. 在容器内执行该命令捕获输出 4. 将输出与文档中“预期输出”比对生成 verify-report.md。 这样文档和执行永远一致。 ### 3.6 版本与变更用 Git Tag 锁定契约效力 最后一块也是最容易被忽略的。没有版本管理的 SKILL.md就像没有合同日期的协议——你永远不知道当前流水线跑的是哪个版本的契约。 ✅ 正确写法 markdown ## 版本与变更 - 当前版本v1.2.0Git Tag - 生效时间2024-05-20 14:00:00 UTC - 变更记录 - v1.2.02024-05-20timeout_ms 默认值从 1000ms 调整为 800ms新增 ERR_DB_UNAVAILABLE 错误码 - v1.1.02024-04-15增加 mock-order-db 状态检测实操机制我们要求所有 SKILL.md 文件必须放在skills/目录下且文件名与skill_id严格一致如SKILL-007.mdCI 流水线在执行前会先git describe --tags --exact-match HEAD获取当前 commit 的 tag如果 tag 匹配v*.*.*则从该 tag 对应的 commit 中读取 SKILL.md如果无 tag则拒绝执行报错No version tag found. Please tag your commit.所有变更必须走 PR且 PR 描述中必须包含BREAKING CHANGE:如修改了必填参数或FEATURE:如新增错误码这样semantic-release工具能自动生成新 tag。这套机制带来的好处是当SKILL-007在生产环境失败你只需git checkout v1.2.0 cat skills/SKILL-007.md就能 100% 还原当时生效的契约无需翻聊天记录或邮件。4. 完整实操流程从创建到上线的 7 个关键步骤现在我们把前面所有要点串起来走一遍真实项目中从零创建一份 SKILL.md 并驱动 Agent 的全流程。这不是理论推演而是我们团队每天都在做的标准化动作。每一步我都标注了耗时、责任人、常见卡点和我的避坑心得。4.1 步骤一明确能力边界耗时15 分钟责任人测试负责人动作召集本次技能涉及的开发、测试、运维开一个 15 分钟站会。只讨论一个问题“这个能力独立存在、独立交付、独立告警的最小单元是什么”输出一句话能力定义例如“对支付回调接口/v1/pay/callback的幂等性验证”。避坑心得绝对禁止出现“和”“或”“以及”这类连接词。如果说“验证回调接口的幂等性和签名正确性”就必须拆成SKILL-PAY-004-幂等性和SKILL-PAY-005-签名验证。混合能力会导致校验逻辑复杂、失败归因困难。我见过最典型的错误是把“登录态保持”和“订单查询”绑在一起。结果登录 cookie 过期订单查询失败日志里却只报ERR_ORDER_NOT_FOUND新人花了 3 小时查订单服务最后发现是登录模块的问题。4.2 步骤二起草 SKILL.md 骨架耗时10 分钟责任人测试工程师动作在skills/目录下新建文件SKILL-PAY-004.md按固定模板填充 6 个区块标题其余留空。模板如下# 【支付回调幂等性验证】SKILL-PAY-004 ## 能力目标 ## 执行前提 ## 输入参数 | 参数名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| ## 输出结果 ## 验证方式 ## 版本与变更 - 当前版本v0.1.0草案 - 生效时间待定避坑心得文件名SKILL-PAY-004.md必须与文档内# 【...】SKILL-PAY-004严格一致这是 Agent 解析的 keyv0.1.0是草案版本表示“尚未评审不可用于流水线”避免新人误用不要一上来就填内容先搭骨架。骨架搭好团队评审才有焦点。4.3 步骤三填充内容并交叉评审耗时30 分钟责任人测试工程师主笔开发/运维协同动作按 3.1~3.5 节的方法逐块填充内容。完成后发起 PR要求开发确认能力目标是否覆盖了所有幂等性场景执行前提中的mock-pay-gateway容器名是否正确运维确认输入参数中的callback_id格式是否与 Kafka Topic 分区策略匹配测试确认输出结果中的SUCCESS字段是否足够用于监控大盘避坑心得评审时所有人必须基于“如果我是 Agent我能读懂并执行吗”来提问而不是“我觉得应该这样写”我们有个硬性规定PR 中任何区块的修改必须附带一条// WHY:注释解释修改原因。例如// WHY: 原callback_id为 UUID但实际 Kafka 分区键要求 8 位数字故改为integer类型。这强迫大家思考“为什么”而不是“怎么写”。4.4 步骤四本地验证与脚本生成耗时20 分钟责任人测试工程师动作运行./gen-verify.sh SKILL-PAY-004生成verify-report.md检查报告中“预期输出”与“实际输出”是否一致若不一致调整make skill-run命令或 SKILL.md 内容直到报告通过运行make skill-test SKILLSKILL-PAY-004执行真实测试确认日志中出现✅ PASS。避坑心得gen-verify.sh是我们的“文档-代码一致性守门员”。它不关心逻辑对错只关心“文档写的和代码做的是不是一回事”。很多 bug 就是在这一步暴露的比如文档写timeout_ms500但脚本里硬编码timeout1000make skill-test必须在真实环境至少是 staging跑不能只在本地 mock。曾有团队本地验证通过上线后才发现mock-pay-gateway在 staging 环境的响应延迟比本地高 200ms导致超时失败。4.5 步骤五Git Tag 与版本发布耗时5 分钟责任人测试负责人动作PR 合并后执行git tag -a v1.0.0 -m SKILL-PAY-004: 支付回调幂等性验证初版 git push origin v1.0.0确认 CI 流水线自动触发publish-skilljob将SKILL-PAY-004.md的v1.0.0版本推送到内部技能仓库。避坑心得Tag 名称必须是vX.Y.Z不能是v1.0或1.0.0因为语义化版本工具只识别三点式发布后立刻在团队群发一条消息“SKILL-PAY-004v1.0.0 已发布流水线将于下次构建时自动启用。请相关服务负责人检查mock-pay-gateway容器是否已升级至 v2.1.0见执行前提”。主动同步比等别人问强 10 倍。4.6 步骤六流水线集成与监控配置耗时25 分钟责任人SRE / 测试开发动作在 CI 流水线如 GitHub Actions中新增 jobtest-skill-pay-004: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 # 必须获取 tag - name: Run SKILL-PAY-004 run: make skill-run SKILLSKILL-PAY-004 CALLBACK_ID12345678在 Prometheus 中配置告警规则count by (skill_id, error_code) (rate(skill_failure_total{skill_idSKILL-PAY-004}[1h])) 5即SKILL-PAY-004的任意错误码1 小时内失败超 5 次触发告警。避坑心得fetch-depth: 0是关键没有它Actions 无法获取 Git Taggit describe会失败告警规则必须按error_code分组而不是只看总量。ERR_TIMEOUT和ERR_INVALID_RESP的处理路径完全不同混在一起只会让值班人抓狂。4.7 步骤七新人培训与知识沉淀耗时10 分钟责任人测试负责人动作将SKILL-PAY-004.md的## 验证方式区块复制到团队 Wiki 的“新人入职清单”页在每周分享会上用 5 分钟演示如何从零创建一个 SKILL.md如何本地验证如何看告警更新README.md在“贡献指南”中加入“新增测试能力请先阅读 SKILL.md 规范再提交 PR”。避坑心得培训材料必须是“可执行的”不是“可阅读的”。Wiki 里那句make skill-run ...新人复制粘贴就能跑通这才是有效培训我们有个不成文规定任何新加入的测试同学入职第一周的任务就是独立完成一个SKILL-XXX的创建、验证、发布全流程。这比让他看 10 小时文档管用得多。这 7 个步骤总计约 115 分钟近 2 小时但换来的是一个可复用、可监控、可追溯、新人 30 分钟内就能上手的测试能力单元。它不追求炫技只解决一个朴素问题让测试这件事从“人找事”变成“事找人”。5. 常见问题与实战排障那些让我熬夜改了三次的坑再完美的设计落地时也会撞墙。这部分我毫无保留地分享我们踩过的、被血验证过的坑以及对应的排障思路。它们不是教科书里的“理论上可能”而是凌晨两点 Slack 上弹出的告警、是新人发来的截图、是流水线连续 5 小时红着的尴尬。每一个问题我都附上了现场日志、