
先交代一下背景。最近团队在折腾Agent落地我负责给测试组搭一个可复用的测试Skill。前后试了几版方案最后稳定在标题里写的那套结构上SKILL.md scripts references。这三个月踩了不少坑也把测试场景从接口冒烟测到回归执行全跑了一遍。这篇文章就是把整个开发过程和思考整理出来适合正在做Agent技能封装、或者想让模型更稳定地替自己跑测试的人。先说结论Skill这事儿本质不是让模型“记住”你的业务而是把人的工作套路拆成三份——给模型看的操作手册、给模型调的工具脚本、给模型查的参考资料。测试这个领域尤其适合这么拆因为测试本身就是高重复、强规范、知识分散的工作。1. 为什么要专门做测试Skill先把问题定义清楚1.1 Agent替人做测试缺的不是工具而是“套路”我最早让Agent直接干测试时效果特别不稳定。比如让它在本地写个脚本去调接口前几次还能跑通换成复杂的业务链路就开始胡说八道要么参数乱猜要么断言写得像摆设要么报告输出格式一会儿JSON一会儿Markdown。后来我发现问题不在模型能力而在于我什么都没给它定。人做测试是有流程的先看接口文档明确入参和边界再写用例执行断言最后汇总报告。这串动作里既有“知识”比如常见的边界值法、等价类划分也有“执行路径”比如怎么发请求、怎么处理token还有“经验沉淀”比如哪些字段最容易出问题。如果这些全丢给模型临场发挥它每次都会重新发明一遍轮子而且经常发明歪。测试Skill要解决的就是这个问题把常规测试任务里那些不变的东西固化下来模型拿到Skill只需要按步骤走稳定性和效率都会明显上来。1.2 SKILL.md scripts references 这套结构到底在拆解什么这套结构本质上是把测试能力拆成三个层次SKILL.md模型的“大脑皮层”——告诉模型这个技能管什么场景、分几步做、每一步做到什么程度算完。它是模型的工作指令不是给人看的文档。scripts/模型的“手”——把可复用的执行逻辑写成脚本模型需要跑任务时直接调脚本而不是自己现场敲代码。这样能保证执行结果稳定也能复用已有的测试框架。references/模型的“记忆库”——放测试模板、接口文档样例、规范等参考内容。模型只有在需要时才会读取避免把所有东西都塞进上下文里造成过载。打个比方这就像你给新来的测试同事三样东西一份SOP操作流程、一套现成的自动化测试脚本、一抽屉的测试模板和参考文档。他照SOP走用工具干活遇到特殊情况翻抽屉。这套结构的价值就是让Agent按照同样的套路工作。1.3 哪些人适合用这套结构团队在用Claude Code、Codex这类支持技能扩展的Agent想让它稳定输出测试结果自己做自动化测试想把手头脚本组织成Agent可调用的工具链测试负责人想把团队的测试规范和经验沉淀下来让新人和Agent都能复用2. 三个核心模块怎么设计细节决定成败2.1 SKILL.md给模型写的“操作手册”很多人在这一步就翻车了。最常见的问题是把SKILL.md写成了项目说明文档全是“本技能用于测试”、“支持哪些功能”唯独没写模型接到任务后到底该做什么。模型读完一脸茫然最后还是自由发挥。我在实际测试项目里摸索出来一份能用的SKILL.md至少要包含四块内容Frontmatter 元信息最前面用YAML格式写清技能的基本信息。name是技能名description最关键它决定了模型什么时候会想到调用这个Skill。description里必须写清触发条件不是“一个测试技能”这种废话而是“当用户要求对某个HTTP接口进行自动化测试、验证接口返回是否符合预期、生成接口测试报告时使用本技能”。要让模型在决策时能精准匹配。适用场景与不适用场景明确写清楚这个Skill能干什么、不能干什么能帮模型省下大量试错时间。比如我的接口测试Skill会写明“适用RESTful API的功能测试、参数校验、返回字段断言。不适用性能压测、UI自动化。”模型读到“不适用”字样就会主动拒绝跑偏的任务。工作流程Step这是SKILL.md的灵魂。要把测试过程拆成固定步骤比如读取接口文档优先从references获取梳理接口参数设计边界值用例调用scripts下的脚本执行请求并采集结果对返回结果做断言汇总失败项生成测试报告标记遗留风险每一步都要给出清晰的输入和产出让模型像照着菜谱做菜一样。输出约定与注意事项测试和写代码不同Agent经常把输出格式写飘。所以SKILL.md里要明确输出格式报告用Markdown表格还是JSON失败用例怎么标注日志放哪里。我还会加一条“严禁事项”比如“严禁直接吞异常要把堆栈信息完整保留到report目录下”。这些细节决定了Agent产出的东西能不能直接用于后续流程。下面是一个精简版的SKILL.md骨架给新手参考--- name: api-test-skill description: 当用户要求对HTTP接口做自动化测试、验证接口返回是否符合预期、生成测试报告时使用。 --- ## 适用场景 - RESTful API 功能测试 - 参数边界校验 - 返回字段断言 ## 不适用场景 - 性能压测 - UI 自动化 ## 工作流程 1. 读取 references/api-doc-sample.md 了解接口文档结构 2. 根据接口入参设计测试用例覆盖正常值、边界值、异常值 3. 运行 scripts/run_api_test.py 执行测试 4. 解析输出结果整理失败项 5. 按 references/report-template.md 生成测试报告 ## 输出约定 - 报告使用 Markdown 表格 - 每个失败用例需包含用例编号、预期结果、实际结果、失败原因 - 完整日志存放在 logs/ 目录下写完以后有个检验标准你把SKILL.md里的说明复制给一个没接触过这个项目的测试工程师看他能不能照着完成整个测试并生成报告。能说明模型大概率也能。2.2 scripts给模型调用的“工具箱”scripts目录里的脚本和普通脚本最大的区别在于它们的使用者是模型不是人。所以脚本设计的核心不是“人看着方便”而是“机器和模型都好理解”。我总结出三条原则第一脚本粒度要细。不要写一个“run_all_tests.py”包办所有事情要拆成单步操作。比如解析接口文档用parse_api.py执行测试用run_api_test.py生成报告用build_report.py。粒度细的好处是模型可以根据实际需求自由组合而不是被迫跑完一整条链路。第二输入输出必须结构化。脚本的参数要从命令行接收结果要输出成JSON格式。模型最擅长解析JSON你让它从一大段终端日志里找失败原因它也能做但容易误判。直接给它结构化的JSON准确率会高一个档次。第三容错要做得特别厚。脚本使用者是模型它很有可能会传进来一些奇怪的参数。比如把数值类型传成了字符串或者把中文标点当成分隔符。所以脚本里要对参数做严格的类型校验并且错误信息要输出得足够直白方便模型理解后自行修正。一个典型的脚本入口长这样#!/usr/bin/env python3 执行单接口的测试用例输出JSON结果。 import argparse import json import requests def parse_args(): parser argparse.ArgumentParser(descriptionAPI test runner) parser.add_argument(--url, requiredTrue, help接口地址) parser.add_argument(--method, defaultGET, helpHTTP方法) parser.add_argument(--params, default{}, helpJSON格式的请求参数) parser.add_argument(--expected, requiredTrue, helpJSON格式的预期结果如 {\code\: 0}) return parser.parse_args() def main(): args parse_args() try: params json.loads(args.params) except json.JSONDecodeError as e: # 错误信息要输出得足够清晰模型才能自助修复 print(json.dumps({ok: False, error: fparams不是合法JSON: {e}})) return # ... 执行请求、断言 result {ok: True, response: resp.text, case_id: demo} print(json.dumps(result, ensure_asciiFalse)) if __name__ __main__: main()2.3 references给模型查阅的“记忆库”references目录是很多人忽略的地方但它恰恰决定了模型输出的质量上限。模型的知识是通用化的而测试场景往往是项目特有的——你们公司的接口返回格式是{code:0,data:{}}还是{status:success,result:{}}模型不可能提前知道。references就是用来补充这部分项目知识的。那么references里到底该放什么放三类就够1. 接口文档样例。不用把全部接口都放进去放一份有代表性的文档作为格式样板并告诉模型“项目接口文档遵循此格式”。模型看到样例就知道字段说明、参数表、响应结构该怎么理解。2. 测试用例模板。这是我强烈建议放的。很多模型生成的测试用例停留在“验证接口能通”这个层面缺少边界值、异常值、参数组合这类测试思维。放一份好的用例模板相当于给模型补了测试方法论。3. 报告输出模板。固定报告格式让所有输出风格统一。这里有个关键技巧references里放的是“引用”而不是“全文”。不要把几十个接口文档的完整内容全塞进去模型读取时会把它们全部拉进上下文很快就把窗口塞满了。正确做法是放精简的核心模板同时把完整文档放在项目其他位置在SKILL.md里写明“完整文档位于docs/目录下需要时可读取”。3. 从零实战构建一个可用的测试Skill3.1 初始化目录与环境我习惯用一套固定的目录结构来组织Skillapi-test-skill/ ├── SKILL.md ├── scripts/ │ ├── run_api_test.py │ └── build_report.py └── references/ ├── api-doc-sample.md ├── test-case-template.md └── report-template.md创建完目录后先搭Python虚拟环境把requests、pytest这些依赖装好。记得在SKILL.md里写清楚运行环境否则模型执行的时机一长可能就把环境信息弄丢。mkdir -p api-test-skill/{scripts,references} cd api-test-skill python3 -m venv .venv source .venv/bin/activate pip install requests pytest这里有一个经验scripts目录下的脚本要写成可以直接用绝对路径运行的。因为Agent执行脚本时当前工作目录不一定是项目根目录如果脚本里用了相对路径很容易报找不到文件。3.2 编写SKILL.md确定工作流程我以“对一个REST API接口做冒烟测试”这个典型需求为例。SKILL.md里把工作流定义为四步读取接口文档提取请求URL、方法、必填参数、关键返回字段用references里的测试用例模板设计一组基础用例正常请求、缺参数、参数类型异常、空值调用scripts/run_api_test.py逐个执行收集返回结果用scripts/build_report.py生成报告输出到report目录工作流定义好后我习惯在SKILL.md里加入一段“决策提示”告诉模型什么时候直接跑脚本、什么时候要先看references。比如当用户只要求“测一下登录接口通不通”时直接执行脚本不需要完整走用例设计流程。当用户要求“详细验证登录接口”时先读test-case-template.md再走完整流程。这能显著提升执行效率也能防止模型每次一上来就写一堆用例把简单任务搞得臃肿。3.3 scripts目录下的核心脚本实现核心脚本我一般会分两个执行脚本和报告脚本职责分离不容易出错。run_api_test.py负责接收接口信息执行请求输出JSON结果。关键点是对请求参数做类型转换因为模型从文本里提取参数时经常传成字符串。脚本里我会用类型判断做一次清洗def parse_params(raw_params): if isinstance(raw_params, str): raw_params json.loads(raw_params) return raw_params or {}build_report.py读取测试结果生成Markdown报告。它要同时支持从文件和标准输入读取结果因为模型可能已经把结果拿在手里不想先写文件再读文件。python scripts/build_report.py --input results.json --output report.md脚本输出格式很重要。我在run_api_test.py里固定输出以下JSON结构方便模型解析{ case_id: LOGIN-001, status: pass | fail, expected: {code: 0, msg: success}, actual: {code: 1001, msg: 参数错误}, error: 字段code匹配失败预期0实际1001, duration_ms: 125 }模型拿到这个结构几乎不用思考就能汇总出测试结论。3.4 references里的模板设计references目录下我放了三个文件。其中test-case-template.md是最关键的它教模型怎么设计用例。模板里我明确规定了用例的格式## 用例模板 | 用例编号 | 测试项 | 输入参数 | 预期结果 | 优先级 | |---------|--------|---------|---------|--------| | XXX-001 | 正常请求 | 完整入参 | code0, 返回业务数据 | P0 | | XXX-002 | 缺少必填参数 | 不传name | code1001, 提示参数缺失 | P1 | | XXX-003 | 参数类型异常 | name123 | code1002, 提示类型错误 | P1 | | XXX-004 | 参数超长 | name256个字符 | code1003, 提示超长 | P2 |模型看到这个模板就明白“用例不光是验证通的场景还要考虑异常和边界”而不是每次都只会发一个GET请求然后说“接口正常”。api-doc-sample.md则放一份接口文档的样例。我会特别注明“项目接口返回统一为{code: int, msg: string, data: object}code为0时表示成功。” 模型有了这个信息在设计断言时就知道该断言什么字段。3.5 接入Agent流程与调试目录、脚本、文档都准备好了剩下就是把它放到Agent能识别的位置。不同Agent的Skill加载机制不太一样有的是放到特定目录有的是在配置里声明。我以常见的做法为例把整个api-test-skill目录放到Agent的skills目录下确认Agent能读取到SKILL.md的frontmatter用一个简单任务测试触发“请用api-test-skill测试登录接口 http://localhost:8080/login”第一次跑的时候大概率会有问题。我最常见到的问题有两种一是模型忽略了SKILL.md里的步骤自己另起炉灶二是脚本报错后模型不知道怎么处理。这两种问题的排查思路我在下一节详细说。调试阶段有个小技巧在SKILL.md里加一段“debug模式”要求模型在执行每步时打印当前行为。跑完一轮之后把调试信息从SKILL.md里去掉再正式用。这样能快速定位是流程问题还是脚本问题。4. 实战中的常见问题与排查技巧4.1 Agent不按SKILL.md走怎么办这是最让人抓狂的问题SKILL.md写得清清楚楚模型偏不照做非要自己发挥。我排查过很多次90%的原因出在SKILL.md的description和正文的可执行性上。场景一description写得过于宽泛模型无法判断该不该调用这个Skill。解决办法是把description写成“触发式”明确包含“当用户要求...时使用”并且把触发条件写具体。比如“当用户要求对接口进行自动化测试并输出报告时使用”比“接口测试工具”触发率高得多。场景二SKILL.md的工作流太抽象。模型看完不知道第一步该打开哪个文件。解决办法是要把命令写全比如“运行python scripts/run_api_test.py --url http://localhost:8080/login --method POST执行用例”。模型是命令友好型的生物你给它具体的命令它就跑得很稳你给它抽象描述它就自由发挥。如果前面都改好了模型还是不理那就检查一下这个Agent版本是不是完整支持SKILL.md的加载。有些框架只识别特定文件名比如必须叫SKILL.md全大写改成skill.md就识别不了。这个在接入前一定要确认。4.2 脚本输出不够结构化模型解析失败早期我把脚本结果用print格式化输出模型解析时经常出错尤其当返回内容里有大段嵌套JSON时模型会自己“脑补”字段。后来我改成强制JSON输出并且在SKILL.md里明确写了“解析scripts输出的JSON对象不要自行推测字段”问题就基本消失了。如果你的脚本必须输出大量日志建议把日志写到文件里标准输出只保留JSON结果。这样模型拿到的永远是干净的结构化数据不会被无关日志干扰。# 结果写到日志stdout只输出JSON import json, logging logging.basicConfig(filenamelogs/runner.log, levellogging.INFO) result {ok: True, response: resp.text} print(json.dumps(result, ensure_asciiFalse))4.3 references内容太多导致上下文爆炸很多人喜欢把公司所有的接口文档、测试规范全部塞进references结果模型加载Skill时上下文被占满后面的对话越来越笨。我在实战中吃过这个亏。后来定的规矩是references只放精简模板和样例完整文档放在项目docs目录在SKILL.md里用一句话指引模型需要时再读取。比如SKILL.md里写references/api-doc-sample.md 仅为格式样例。完整接口文档位于 /docs/api-docs/ 目录下实际测试时请从该目录读取对应接口文档。这样模型既知道了文档长什么样又不会一开始就把所有文档读进上下文。这个设计理念非常重要Skill做大了以后references的管理直接决定了Agent的使用体验。4.4 常见问题速查表问题现象原因解决办法Agent不调用Skilldescription没有触发词改成“当用户要求...时使用”的句式Agent跳过步骤乱来工作流太抽象每一步给出明确命令和产出要求脚本被模型调用时报参数错误模型传参不规范脚本内做类型清洗和错误提示解析结果总是错输出格式不固定统一JSON输出日志写入文件Agent变笨references内容过载只留模板完整文档外部引用依赖频繁缺失环境没有固化用requirements.txt锁定依赖版本5. 结束前的几个实用建议最后再分享两个我踩坑踩出来的经验。第一个经验Skill开发不要一上来就做大而全。我第一次把测试Skill设计得特别完整包含了接口测试、UI测试、性能测试、安全测试结果模型经常混淆反而什么都不精。后来拆成独立Skill每个只做一件事效果立刻好了很多。做Skill可以遵循一条原则先从一个高频场景切进去跑通了再复制扩展。第二个经验SKILL.md里建议留一个“测试验证”章节里面写上一个最小可执行用例及期望输出。每改一次Skill就把这个用例跑一遍看是否仍然符合预期。相当于给Skill做回归测试防止越改越跑偏。这套结构看起来简单但真正用好的人不多。核心不在于你会不会写Markdown和Python脚本而在于你能不能把自己的测试思维拆解清楚。当我们把测试经验真正拆成操作手册、工具脚本和参考模板三块时Agent就不再是纸上谈兵的聊天机器人而是个能稳定交付活儿的测试助手。