Agent Skill开发实战:用三件套打造高效测试Skill
发布时间:2026/9/8 7:20:11 锦皓数字建站

我第一次正式给Agent写Skill是在一个单测覆盖率跌到40%的项目里。当时按网上的教程飞快地写了一个SKILL.md让AI“运行项目测试并输出优化报告”。结果它跑完pytest之后把终端输出原样贴回来再煞有介事地总结一句“测试全部通过”。覆盖率没提失败用例没归类优化建议全是通用话术。那一刻我意识到一个Skill绝不是一个markdown文件的事。后来我把同一个需求重写了一遍用上了SKILL.md scripts references三件套效果完全不一样。AI会自动收集测试范围、执行脚本拿到结构化结果、对照模板生成报告、遇到失败还会翻参考资料里的案例库。整个过程像把一个只会嘴上说说的实习生训练成了能独立干活且懂规矩的正式员工。这篇实战指南就是围绕这个“测试Skill”从0到1的完整过程展开的。我会拆解三件套各自的职责边界给出SKILL.md、scripts、references每一步具体怎么写再带你走一遍联调测试和迭代维护的全流程。适合刚接触Agent Skill开发、写过但总跑不通以及想让Skill从“能用”变“好用”的开发者。1. Skill的底层逻辑三件套不是随便凑出来的组合很多人在接触Skill开发时会问既然SKILL.md本身就能写清楚“该怎么做”为什么还要单独搞scripts和references两个目录这个问题如果没想明白后面写出来的Skill大概率是四不像。1.1 Skill不是插件也不是Prompt模板Skill这个概念在Claude Code、Codex等Agent开发环境中越来越常见但很多人对它的理解还停留在“一个长一点的Prompt”或“一个轻量插件”。这两种理解都不准确。插件通常常驻运行有明确的切入点和生命周期Skill则是按需加载的。AI根据当前任务判断“这个场景好像有对应的Skill”然后主动读取Skill目录里的内容按照里面的约定去执行。Prompt模板则只有文字Skill除了文字还可以包含可执行脚本和结构化参考资料。我更愿意用一个类比来解释Skill像是一个“带说明书、工具包和参考资料的外包师傅”。说明书告诉师傅什么时候该接活、按什么流程干工具包装着电钻、水平仪这些真正干活的工具参考资料则是师傅过去积累的施工图纸和验收标准。只有说明书没有工具师傅只能纸上谈兵只有工具没有图纸师傅容易干出野路子。1.2 三件套的分工认知、能力、知识SKILL.md、scripts、references这三个部分正好对应了一个专业岗位的三个核心要素认知判断、操作能力、经验知识。组成部分对应要素AI什么时候用到写不好会怎样SKILL.md认知与流程任务刚开始AI判断要不要用这个Skill、按什么步骤执行时AI不会调用或调用后流程混乱、边界失守scripts操作能力需要执行多步命令、处理数据、生成结构化结果时AI只能“假装”执行输出不可验证容易编造结果references经验与标准需要产出符合规范的报告、排查历史问题、套用模板时输出质量看运气每次生成的东西风格不一致表格里“写不好会怎样”这一列是我在大量实际案例里反复验证过的。尤其是scripts缺失的问题隐蔽性最强。因为AI非常擅长“用文字模拟执行”你要是没给它真能跑的脚本它会在报告里写“经分析建议优化xxx”但实际上什么都没分析。1.3 为什么“只写SKILL.md”是最常见的翻车方式大部分人的第一个Skill都是只写了一个SKILL.md包括我自己。原因很简单网上教程多数只展示这个文件而且它写起来门槛最低感觉像在写Markdown文档。但只写SKILL.md会带来几个连锁问题。第一AI没有“抓手”。你让它“运行测试并分析覆盖率”它可能会选择自己构造一条shell命令去执行也可能选择不执行、直接根据项目里的代码结构凭空分析。不执行的时候输出就是幻觉。第二输出格式不可控。没有脚本统一生成结构化结果AI每次返回的报告样式都不同。今天用表格明天用列表后天可能变成一段散文你根本没法在后续流程里自动化处理。第三边界无法约束。Scripts里可以写严格的参数校验、超时处理、退出码约定纯文本的SKILL.md很难做到这点。没有这些工程化约束AI的“自由发挥空间”就太大了。所以我的判断是一个合格的Skill必须是三件套协同工作。SKILL.md负责“知道做什么、按什么顺序做”scripts负责“真正把事情做成”references负责“保证做出来的东西符合标准”。2. 第一步SKILL.md是给Agent看的“上岗说明书”SKILL.md是整个Skill的入口文件也是Agent决定“要不要调用你”“调用你之后听你指挥”的关键。它的写法跟写产品文档完全是两回事核心目标只有一个让一个拥有很强推理能力但缺乏业务语境的模型在几秒内理解你的Skill适合什么场景、应该怎么执行。2.1 frontmatter里的description才是真正的调用开关SKILL.md顶部的YAML frontmatter看起来只是一个格式要求但字段设计的颗粒度直接决定了Skill能不能被正确触发。以我做的auto-test-skill为例--- name: auto-test-skill description: 在Python项目中自动收集、运行测试用例并生成可读的测试报告。当用户要求“检查测试”“跑一下单测”“验证代码没有引入回归”或CI失败需要分析时使用。输入为项目路径或目标测试范围输出为结构化测试报告。 ---这里最关键的不是name而是description。AI判断是否加载这个Skill时主要就是拿用户当前的需求跟所有Skill的description做语义匹配。description写得越具体匹配越精准。反例是很多人喜欢写“A skill for running tests and generating reports”这种描述太泛AI面对稍微特殊一点的需求就不会想起来用它。正例应该是把“触发场景”和“不触发场景”都揉进去比如“当用户只是想聊测试概念时不要用当用户要求实际执行测试时使用”。2.2 正文结构触发条件、工作流程、执行规则写完frontmatter之后SKILL.md的正文才是重头戏。我见过很多人的SKILL.md就是一篇项目文档把所有功能罗列一遍AI看完依然不知道从哪里下手。我的推荐结构是以下五段式这个Skill解决什么问题一段话说明边界“它负责什么、不负责什么”。什么时候使用比description更详细的触发场景列表给出正例和反例。工作流程编号列表每一步做什么、这一步的输出是什么。执行规则明确允许做什么、禁止做什么包括异常情况的兜底策略。输入与输出约定告诉AI当前任务的输入怎么读取最终成果物以什么形式交付。以auto-test-skill为例工作流程段我这样写## 工作流程 1. 用 scripts/collect_tests.py 收集项目中的测试文件清单确认本次测试范围。 2. 用 scripts/run_tests.py 执行测试传入目标路径和超时秒数获得结构化结果。 3. 如果测试失败先读取 references/failure_playbook.md按其中的常见案例进行定位。 4. 用 references/report_template.md 生成最终报告报告需包含通过率、失败用例明细、耗时、修复建议。注意第3步和第4步中references的引用方式。不要写“参考相关资料”要写清楚“去哪个文件里查”。AI对显式路径的敏感度远高于模糊指引这是实测出来的结论。2.3 边界条件告诉AI什么不能做比告诉它能做什么更重要没有边界的Skill是危险的。一个测试Skill如果不加约束AI可能在“修复失败用例”时顺手改掉业务源码或者在项目没有安装依赖时自作主张执行pip install甚至为了通过率好看而跳过失败用例。所以我在SKILL.md里单独列出“规则”一节## 规则 - 不要修改任何源码和测试文件除非用户明确要求。 - 不要安装依赖除非用户明确要求。依赖缺失时在报告中标注即可。 - 测试结果以脚本解析出的数据为准不要自行判断“应该没问题”。 - 如果执行超时或命令不存在停止操作并在报告中说明原因。 - 报告必须按 references/report_template.md 的格式输出禁止自由发挥。这几条规则本质上是在限制AI的“动作空间”。动作空间越小出幺蛾子的概率越低。你要理解一个事实模型天然倾向于“把事办了”哪怕事办得粗糙它也会想办。你如果不划定红线它会用各种意想不到的方式帮你“优化”。我个人的经验是每一版SKILL.md改完都花10分钟自我拷问一遍——“如果让一个有点能力但不太懂规矩的新人来执行这份说明书他会钻哪些空子”所有能想到的空子都值得写进规则里。3. 第二步scripts要能让Agent像调用工具一样放心调用SKILL.md写得再好也代替不了真正干活的脚本。scripts目录是整个Skill的“手和脚”它的设计原则跟普通工程项目不太一样核心指标是可预测性。Agent在调用时必须能预判脚本输入什么、输出什么、什么情况下会挂。3.1 什么时候必须写脚本什么时候可以不写不是所有Skill都需要scripts。比如一个“代码审查规范Skill”主要给AI提供审查规则清单强调判断标准那不一定非要有脚本。但如果涉及执行类任务比如运行测试、统计代码量、检查接口状态、批量处理文件不写脚本就是给自己挖坑。判断标准很简单如果这个任务的执行结果需要“被验证”就必须有脚本。所谓被验证就是AI不能自己说了算必须有一个外部程序生成一个确定性的结果。测试场景恰好是典型代表——AI说“测试通过了”不算数pytest退出码是0才算数。一个常被忽略的点是脚本数量不必多但要保证“原子性”。每个脚本只干一件事名字用动词开头参数含义要明确。我见过有人把一个测试Skill做成一个巨大的pipeline脚本接收十几个参数AI调用时经常拼错一个参数然后整体报错。这种设计是反可预测性的。3.2 脚本与SKILL.md的调用约定脚本写完之后SKILL.md必须明确告诉AI怎么调用。这里有个容易犯的错只写“用scripts里的脚本执行测试”却不写具体命令。AI遇到这种情况会自己去翻目录猜脚本名猜错的概率不低。正确的做法是给出完整命令模板执行测试 !bash scripts/run_tests.py --target 目标路径 --timeout 秒数把参数项、参数含义、输出格式都写清楚。AI看到一个规范到位的命令模板它就知道这不是让你自由发挥的接口而是必须按约定调用。输入输出契约同样重要。我在所有scripts里都坚持两个原则输入只用命令行参数和标准输入不用环境变量传递关键业务参数因为AI在执行脚本时环境变量不可控。输出必须是可解析的纯文本或JSON。运行结果要能一眼看出“成功/失败/跳过/错误”的状态分类不能把一堆无关日志混在结果里。3.3 实战写一个测试执行脚本这里分享一个我在auto-test-skill里真正用到的核心脚本。它的任务不是把pytest跑一遍就完而是要拿到一份让AI能直接用来生成报告的结构化结果。#!/usr/bin/env python3 运行测试并输出结构化摘要。 import argparse import json import subprocess import sys from pathlib import Path def parse_args(): parser argparse.ArgumentParser(descriptionRun tests and summarize result) parser.add_argument(--target, requiredTrue, helptarget directory or file) parser.add_argument(--timeout, typeint, default120, helptimeout seconds) return parser.parse_args() def run_pytest(target: str, timeout: int) - dict: cmd [python, -m, pytest, target, -q, --tbshort] try: proc subprocess.run( cmd, capture_outputTrue, textTrue, timeouttimeout, ) return { exit_code: proc.returncode, stdout_tail: proc.stdout[-2000:], stderr_tail: proc.stderr[-1000:], } except subprocess.TimeoutExpired: return { exit_code: 124, stdout_tail: , stderr_tail: timeout after %d seconds % timeout, } def main(): args parse_args() result run_pytest(args.target, args.timeout) # 输出JSON避免AI解析长文本失败 print(json.dumps(result, ensure_asciiFalse, indent2)) sys.exit(0 if result[exit_code] 0 else result[exit_code]) if __name__ __main__: main()这个脚本有几个设计细节值得说。第一stdout_tail截取了输出末尾2000个字符。原因很简单模型的上下文窗口很宝贵一个塞满几千行测试日志的输出会把AI“冲昏头脑”。只保留尾部关键信息足够判断错误原因。第二超时时间由调用方传入脚本本身不做死配置。不同项目测试规模差异巨大SKILL.md里要求AI根据目标目录大小调整超时参数而不是所有项目都用同一个值。第三输出JSON格式。这样AI读到结果后能直接按字段提取信息生成报告不需要在一大坨文本里人肉找数据。3.4 脚本的容错与“哑弹”处理脚本还有一个容易被低估的点容错。Agent执行脚本时环境可能跟你想的完全不一样。比如项目用的依赖管理器不是pip是poetry或pytest根本没有安装甚至python命令本身指向了Python 2。这些情况脚本都要有明确输出不能直接崩溃留一堆traceback。我一般会在脚本开头做环境自检def check_environment(): missing [] try: import pytest # noqa: F401 except ImportError: missing.append(pytest) if missing: print(json.dumps({ exit_code: 127, error: missing dependencies: , .join(missing) })) sys.exit(127)注意这里设置了一个特殊的退出码127语义是“命令不存在”。AI拿到这个退出码后会知道这是环境问题不是测试本身失败从而在报告中如实说明而不是编造一个“测试不通过”的假结论。4. 第三步references决定输出质量的下限SKILL.md给出了行动路径scripts提供了执行能力但还有一个问题没解决AI产出的东西“像不像样”。这正是references要负责的事。很多人写Skill不重视references结果AI每次输出的报告格式都不一样细节经常丢失。references的本质是给AI一份“照抄都不会抄错”的底稿。4.1 references里到底该放什么references目录的核心原则是放那些AI在生成结果时“应该照着做”的东西而不是放“AI需要阅读理解的背景资料”。以auto-test-skill为例我的references目录长这样references/ ├── report_template.md ├── coverage_policy.md └── failure_playbook.mdreport_template.md是测试报告的固定模板里面有章节结构、表格格式、填表说明。coverage_policy.md定义了覆盖率评判标准比如新增代码覆盖率低于80%要标记为风险。failure_playbook.md记录了过去项目里出现过的典型失败案例及对应处理方式。这三类文件分别对应三个不同的用途输出风格约束、判断标准约束、异常处理经验。一个有经验的开发者看到这里应该能感觉到这其实就是把“隐性知识”显性化的过程。4.2 实战给测试Skill准备references模板report_template.md是我最看重的一个文件它决定了AI最终交出来的报告长什么样。我的模板大概长这样# 测试报告 ## 1. 概览 - 测试范围目标路径 - 执行时间日期 - 总用例数N - 通过/失败/跳过N/N/N - 通过率百分比 ## 2. 失败用例明细 | 用例名 | 失败原因 | 涉及文件 | 建议操作 | | --- | --- | --- | --- | ## 3. 覆盖率分析 - 总覆盖率百分比 - 关键模块覆盖率模块名 百分比 - 风险提示是否低于阈值 ## 4. 修复建议 按优先级列出每条建议需说明依据关键在“建议操作”那一列。我要求AI必须填写不能留空。因为AI特别容易在“失败原因”里写得很详细到“建议操作”就含糊起来。有了模板约束它至少会写一条可执行的下一步动作。SKILL.md里必须明确写最终报告必须按这个模板生成不能自行调整结构。不这么写的话AI会觉得模板只是参考信息自己改得更“好”。4.3 references的维护纪律少而精随Skill演进references不是知识库不能贪多。模型每次调用Skill时只会读取SKILL.md中显式提到的文件。放太多杂七杂八的东西进去一方面浪费上下文另一方面会让AI抓不住重点。我自己定的维护纪律有三条每个Skill的references文件数量控制在5个以内。文件之间不要有重复内容。如果两个文件都对“覆盖率阈值”做了定义AI会困惑以哪个为准。每次修改SKILL.md都要检查references是否需要同步更新。还有一条实战建议给references文件加“最后更新日期”和“适用版本”标注。这样当AI发现某个策略过时时能在报告中主动提示而不是默默按旧标准执行。这个小细节能给你争取很多后续维护的主动权。5. 从零联调一个“测试Skill”的完整过程三件套都写完并不意味着Skill已经能用了。联调测试是真正的试金石。很多Skill在纸上看起来很完美一放到真实对话场景里就露馅要么触发不精准要么工作流程走不通要么脚本在特定环境下直接崩掉。5.1 标准目录结构长什么样一个完整可发布的skill目录至少要包含以下内容auto-test-skill/ ├── SKILL.md ├── scripts/ │ ├── collect_tests.py │ └── run_tests.py └── references/ ├── report_template.md ├── coverage_policy.md └── failure_playbook.md如果你是直接从网上或从模板仓库clone下来的Skill第一件事不是急着改SKILL.md而是先确认scripts的权限和可执行性。很多Skill不能运行纯粹是脚本没有执行权限或者依赖没有安装。5.2 三种测试姿势手动触发、自动触发、调试模式我把Skill的测试分为三种跑法对应不同的验证目标。第一种是手动触发。明确对AI说“使用auto-test-skill检查当前项目的测试状态”。这种跑法用来验证SKILL.md里的工作流程是否顺畅、脚本有没有bug、报告模板是否合理。优点是可以快速暴露问题缺点是绕过了“触发机制”无法验证description写得好不好。第二种是自动触发。不给AI任何提示直接说“我刚改了src/utils下面的代码你帮我确认一下有没有引入回归”。如果AI能自己想起来加载auto-test-skill说明description写得足够好。如果它选择自己乱跑命令说明触发描述还需要优化。第三种是调试模式。开发环境一般都有Skill调试开关可以强制让AI只读取某个Skill、忽略其他Skill。当你的Skill比较多时这种隔离测试很有用。5.3 联调时的验证清单我每次测试一个Skill都会记录以下维度的表现而不是只看“最终对不对”验证维度具体问题我的判断标准触发准确性该用的时候是否主动用不该用的时候是否保持沉默10次场景测试中至少8次触发正确流程完整性是否严格按SKILL.md的工作流程走有没有跳步除特殊情况外不可跳步脚本健壮性在依赖缺失、路径不存在、超时情况下是否正常报错必须有明确错误信息不能直接崩溃输出稳定性同一场景跑3次报告格式和内容是否一致格式必须一致数据不能有随机差异第四个维度非常重要。AI本身就带有随机性如果你发现同一份测试报告两次生成结果不一样通常不是随机性的问题而是SKILL.md的规则不够强或者references模板没有被严格执行。6. 让Skill长期可用的迭代纪律与常见翻车修复Skill不是写完就结束了它跟普通代码一样需要持续维护。尤其是AI Agent生态发展很快工具链一变之前精心设计的调用方式可能就失效了。这节分享一些我踩过的坑和处理方法。6.1 常见翻车点与修正方案我整理了五个出现频率最高的问题基本可以覆盖90%的Skill翻车场景。现象根因修正方案AI从不主动调用Skilldescription里没有覆盖真实触发场景回顾历史对话把用户实际说法提炼进description调用了但执行流程混乱SKILL.md只给了目标没给步骤用编号列表把工作流程写死每一步都有输出物脚本执行报错、AI不会处理脚本文档没写清失败语义和退出码在SKILL.md里补充每个退出码对应的处理动作报告结构每次都不一样references模板没有被强制执行SKILL.md中加强“必须按模板输出”之类的强约束语句边界被突破、乱改文件禁止项列表缺失或太模糊明确列出允许操作和禁止操作用词要绝对绝对化其中第二和第四类问题值得多说两句。流程混乱的根因往往是SKILL.md把“目标”和“步骤”混在一起了。AI看得到“要做出一份测试报告”这个目标但看不到“先收集用例、再执行、再分析、最后出报告”的强顺序于是它自己发明了路径。第四类问题则要反思references模板某段描述是否有多义性。比如模板里写“按优先级列出建议”但没定义什么叫“优先级”AI每次理解的优先级自然不一样。解决办法是再加一句“优先级定义为阻塞性高中低”。6.2 用执行日志和AI自反馈优化Skill迭代Skill最有效的信息来源是Agent执行Skill时产生的日志。我看日志时特别关注三处AI首次读到SKILL.md的决策路径、调用脚本前有没有按照模板组织命令、生成报告时有没有偏离模板。另一个技巧是在SKILL.md里内置“执行回顾”环节。让AI完成任务后额外输出一段自我回顾本次执行有没有跳步哪些规则产生了歧义如果再来一次会怎么优化流程这些都是真实的模型视角反馈比你自己猜测有效得多。6.3 我的迭代节奏经过几轮项目实践我形成了这样的迭代节奏新Skill先在小范围项目里试用三天期间每天看日志、记录异常三天后集中优化一轮SKILL.md和references之后进入稳定期每周只花十分钟看一次使用频次和不触发率。迭代时严格遵守一个原则一次只改一个变量。如果同时改了SKILL.md和脚本再重新测试出问题时很难定位是哪个改动导致的。我把这个原则写进了自己的开发规范里实测下来能节省大量排查时间。最后分享两个小习惯。第一每次改完SKILL.md我都会用同一批测试场景完整跑一遍再和上一版的结果对比防止改了A规则损坏了B行为。第二给references里的每个文件加上更新时间戳这样AI能感知到资料的新旧你也能在日志里看出哪个模板已经很久没人用了。Skill开发本质上不是“写一个文件”而是“设计一套让Agent稳定复现专业行为的工作系统”。三件套各司其职配合迭代纪律你的Skill才能真正从演示品变成每天都能用、用了不操心的生产力工具。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。