编写Skill实现自动化用例生成:从提示词到Agent技能包
发布时间:2026/9/19 2:46:33来源:尧图网络
我一直觉得AI 写代码这事儿最尴尬的阶段不是“写不出来”而是“每次都要把同样的背景讲一遍”。我今天想让它给某个模块补测试明天想让它按同一个规范生成接口用例结果每次开头都要写一大段“你是测试专家请按以下风格输出……”烦不烦。后来我开始用 Skill 这个东西把“怎么生成用例”的整套流程固化成文件丢给 Agent它拿到就能直接干活代码风格、文件路径、输出格式全都不用我重复交代。这篇文章就围绕“如何编写 Skill 实现自动化用例生成”展开我会从 Skill 到底是什么、目录怎么设计、SKILL.md 怎么写、脚本和模板怎么配套一路讲到调试和踩坑。适合在 Codex、OpenCode、Claude Code 这类支持 Skill 的 Agent 环境里做开发测试的人看不管你是刚接触 Skill 的新手还是已经写过几个 Skill 想提升质量的老手应该都能捞到点干货。1. Skill 到底是个什么东西先把它和“提示词”“Agent”分清楚1.1 Skill 不是提示词模板而是一套“能力包”很多人第一次接触 Skill第一反应是“这不就是预设提示词吗”。我刚开始也这么想后来发现完全不是一回事。提示词模板是你跟模型说“请怎么怎么做”模型每次都要靠上下文里的文字来理解你的要求文字越多、越模糊跑偏概率越高。Skill 则是一组结构化的文件里面有说明文档、可执行脚本、参考规范、示例模板Agent 在拿到任务后会自动去“读”这套文件再决定怎么干活。打个比方提示词像是你口头跟一个新同事说“帮我把测试写了”他能听懂但不知道你们项目的目录习惯、不知道你用 pytest 还是 unittest、不知道 mock 风格结果全是“自由发挥”。Skill 像是你直接给这个同事一本《团队测试手册》里面写清楚了几步走、什么格式、什么工具他照着做就行。这就是 Skill 和普通提示词之间最本质的区别提示词是“一句话要求”Skill 是“一套可执行、可复用的能力方案”。所以在自动化用例生成这个场景里Skill 的价值特别明显。用例生成不是一个“一句话就能说清”的任务它牵涉到被测代码的解析、接口的参数提取、断言怎么写、数据怎么构造、文件放哪、命名规范是什么。这些如果全塞在系统提示词里长度爆炸而且改起来麻烦把它们拆成 Skill 文件Agent 按需读取上下文干净行为还稳定。1.2 Skill 和 Agent 的关系一个是工具箱一个是用法热词里出现“skill和agent的区别”我在这里也说下我的理解。Agent 是一个能自主规划、调用工具、执行任务的智能体它负责理解目标、拆解步骤、决定什么时候调用什么。Skill 是给 Agent 准备的专业技能模块本质上是“某一类任务的标准化解法”。Agent 决定“我要生成用例”Skill 提供“生成用例的具体方法、脚本、规范和模板”。可以这么理解Agent 是那个干活的人Skill 是他的工种技能。一个人可以有多种技能一个 Agent 也能加载多个 Skill。Skill 本身不会主动跑它要被 Agent 触发、调度。你写 Skill 的时候表面是在写 Markdown 和脚本实际上是在定义“Agent 遇到这类任务时应该按什么标准流程来做”。想清楚这层关系后面设计 Skill 的时候就不会跑偏——你不是在写文档你是在给 Agent 编行为准则。还有一个常见的混淆是 Skill 和 MCP 的区别。MCP 解决的是 Agent“怎么连接外部工具和数据源”的问题比如让它能查数据库、调 APISkill 解决的是“某个任务用什么方法做”的问题。两者可以配合Skill 里可以说明“生成用例前先通过 MCP 读取接口定义”但 Skill 本身重点不在连接而在流程和规范。我说得夸张一点MCP 是手和脚Skill 是脑子里的方法论Agent 是那个指挥的人。1.3 自动化用例生成这个场景为什么特别适合用 Skill 来解决接口测试用例、单元测试用例、甚至安全测试用例这类工作有一个共同点流程高度标准化但细节高度依赖项目上下文。标准化的部分是“输入被测对象、提取关键信息、套模板、补断言、生成文件”依赖上下文的部分是“项目用什么框架、Mock 风格是什么、目录怎么放、数据怎么构造”。这种“标准化流程 定制化细节”的组合恰恰是 Skill 最擅长处理的。你把标准化流程写死在 SKILL.md 里把定制化细节通过 references 目录里的项目规范、examples 目录里的示例来补充Agent 每次生成用例时先读流程再按需读细节最后输出符合预期的文件。这比每次人工在 Prompt 里反复强调“请按照我们项目的习惯来”要靠谱得多。另外一个原因是可维护性。项目规范变了比如从 pytest 切到了 unittest你只需要改 Skill 里的模板和规范文件所有后续生成的用例自动跟着变。如果这些规范散落在每个人的 Prompt 里那就等着一个个去通知吧绝对改不动。2. 动手前先把框架搭清楚Skill 的标准目录与文件角色2.1 一套通用 Skill 的目录结构长什么样我写过的 Skill 不少从生成用例、写提交信息到做代码审查最后沉淀出来一套通用的目录结构基本能满足大部分场景my-skill/ ├── SKILL.md ├── scripts/ │ ├── extract_functions.py │ └── validate_cases.py ├── templates/ │ ├── pytest_unit.md │ └── api_contract.md ├── references/ │ ├── naming_guide.md │ └── project_structure.md └── examples/ ├── example_input.py └── example_output_test.py这套结构不是拍脑袋定的每个目录都有明确分工。SKILL.md 是入口Agent 接到任务后第一件事就是读它scripts 放可执行脚本用来做一些纯文字不好描述清楚的逻辑处理比如解析 AST、校验生成的用例能否编译templates 放代码骨架保证 Agent 输出的用例不是天马行空而是贴着你们的框架习惯来references 是参考资料Agent 只有在需要的时候才去读避免每次调用都灌一大堆上下文examples 是“参考答案”AI 看示例比看抽象描述要准得多。2.2 SKILL.md 的 Frontmatter 和正文各承担什么角色现在主流支持 Skill 的工具Codex、OpenCode 这些都认可一套约定SKILL.md 开头有一段 YAML 格式的 Frontmatter用来声明 Skill 的名称、描述信息。这段信息非常关键因为 Agent 的模型是根据描述来决定“要不要用这个 Skill”。描述写得太窄需要的时候不触发写得太泛不需要的时候乱触发两种情况都让人崩溃。我习惯在 Frontmatter 里放这几个字段name 是技能名description 要用“When I need to …”或“Use this skill when …”这种触发导向的写法把“什么场景下用”说清楚。比如对于用例生成 Skill我会写“Use this skill when generating unit tests, API contract tests, or parameterized test cases for Python or JavaScript code.”这样模型看到用户在让 Agent 生成测试时才有足够信号去加载它。正文部分才是核心。我不会在正文里把脚本内容贴一遍那样太蠢了正文应该写的是“工作流程”。我的习惯是分四块写先写这个 Skill 的目标和适用范围再写使用步骤告诉 Agent 第一步做什么、第二步做什么接着写必须遵守的输出规范比如文件放哪、命名规则最后写什么时候该去读 scripts、templates、references 里的哪个文件。这样 Agent 拿到 SKILL.md 后就知道整个活该怎么干了而不是靠猜。2.3 为什么 scripts 和 templates 能显著提升生成质量上限可能有人觉得既然 AI 本身就会写代码干嘛还要给它脚本和模板我实测下来的感受是纯靠模型自由发挥代码风格不稳定、边界情况容易漏给它一个解析脚本函数签名、参数默认值、返回类型这些信息就是从真实代码里提取出来喂进去的比模型自己去读源码再总结要准确得多。特别是 Python 这种有 AST 的语言脚本直接生成结构化的 JSON 输入给 Agent它下游写出来的用例质量立马上一个台阶。模板的作用是“兜底格式”。我的模板里会把 import 区、fixture 区、测试函数区、断言风格都框好Agent 要做的事情是在框架里填内容而不是重新发明一套风格。自动化用例生成这个场景里最重要的不是“用例写得有多聪明”而是“一百个用例看起来像同一个人写的”。模板就是保证这种一致性的最简单手段。3. 从零写一个“Python 接口单元测试用例生成”Skill 的完整实操3.1 明确输入输出和边界这是第一步我拿一个实际做过的项目举例目标是让 Agent 针对指定的 Python 文件或函数自动生成 pytest 单元测试用例。输入是两个参数source_file目标 Python 文件路径和 optional target_function可选只生成某个函数的用例。输出是生成test.py 文件放在与源码相同的目录命名规则是 test 原文件名。边界条件我也在 SKILL.md 里写死了不处理对外部服务真实调用的集成测试对于涉及数据库、网络、第三方 SDK 的代码自动生成 mock 桩而不是真的去连只做 pytest 风格不做 unittest如果源码文件无法通过 AST 解析直接报错退出并提示用户检查语法。这些边界看着琐碎但不定清楚Agent 就会“自由发挥”——该 mock 的去发真实请求该用 pytest 的给你整出 unittest 来回头你还得人工改那这个 Skill 就算白做了。3.2 逐个文件手把手写先从 SKILL.md 开始我直接贴一个精简但有代表性的 SKILL.md 示例你们感受下结构--- name: python-unit-test-generator description: Use this skill when generating pytest unit tests for Python source files. It extracts function signatures via AST, creates mock-based tests, and follows the projects pytest style. --- # Python Unit Test Generator ## Goal Produce production-quality pytest test files for given Python modules or functions. ## Inputs - source_file: path to the target Python file - target_function: optional function name; if omitted, generate for all public functions ## Workflow 1. Run python scripts/extract_functions.py source_file to get a JSON structure of functions, parameters, defaults, and return statements. 2. Read templates/pytest_unit.md to understand the output skeleton. 3. If project-specific conventions exist, read references/naming_guide.md and references/project_structure.md. 4. Generate the test file. Use unittest.mock for external calls. Preserve the original function name and parameter semantics. ## Output Rules - Output file name: test_basename of source_file - Place the file in the same directory as the source file, unless user specifies another. - Do not modify the original source file. - Each test function name must start with test_. - For every public function, include at least one normal case and one edge case (empty input, None, zero, etc.).注意几个设计细节。Frontmatter 的 description 里我特意把“触发点”写得很明确防止模型在用户只是闲聊测试时也乱加载这个 Skill。正文的 Workflow 用了“先跑脚本、再读模板、最后生成”的顺序给 Agent 一个明确的执行路径而不是让它一口气乱写。Output Rules 是硬性约束每条都是我在实际项目中踩过坑之后总结出来的比如“不要修改原文件”这条就是因为早期 Agent 真的干过把测试代码直接写进源码文件的蠢事。3.3 scripts/extract_functions.py让 Agent 拿到“准确的结构化输入”写这个脚本的核心思路是用 Python 自带 AST 模块解析源码把每个函数的签名、参数默认值、返回值类型能推断就推断、函数内部调用了哪些外部名字全部提取出来输出成 JSON。Agent 拿到的就是一份结构性极强的“被测对象画像”不用自己去通读全文再总结准确率和速度都会好很多。下面是我项目里这个脚本的关键逻辑做了简化方便看import ast import json import sys def extract_functions(source_path: str) - dict: with open(source_path, r, encodingutf-8) as f: tree ast.parse(f.read(), filenamesource_path) funcs [] for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): if node.name.startswith(_): continue func_info { name: node.name, args: [], returns: None, is_async: isinstance(node, ast.AsyncFunctionDef), } for arg in node.args.args: arg_info {name: arg.arg} if arg.annotation: arg_info[annotation] ast.unparse(arg.annotation) func_info[args].append(arg_info) if node.returns: func_info[returns] ast.unparse(node.returns) funcs.append(func_info) return {module: source_path, functions: funcs} if __name__ __main__: if len(sys.argv) ! 2: print(Usage: python extract_functions.py source_file) sys.exit(1) print(json.dumps(extract_functions(sys.argv[1]), indent2))这个脚本本身不复杂但它解决了几个真实痛点。第一它自动过滤掉了私有函数这样 Agent 不会对_helper这种内部方法生成一堆无谓的测试第二它把参数类型注解和返回值注解统一提取Agent 不需要自己从头文件里翻 import 去推断类型第三它把结果以 JSON 形式输出固定了结构Agent 后续生成用例时可以直接按字段引用不容易编造不存在的函数名。我实际用下来有脚本辅助的情况下生成的用例“调用不存在函数”这种低级错误明显变少了。如果被测代码涉及类型注解缺失、动态返回之类的情况脚本也能给出基本结构剩下的让模型根据函数体逻辑去判断。脚本不是万能的它的价值是把“确定的”信息精确传给 Agent把“不确定的”留给模型推理。3.4 templates/pytest_unit.md把风格一致性锁死在模板里模板文件的作用是把 pytest 的风格骨架固定住。我写的模板大致长这样import pytest from unittest.mock import MagicMock, patch from target_module import function_name class TestClassName: Test suite for function_name def setup_method(self): Initialize common fixtures before each test pass def test_function_name_normal(self): Test normal case pass def test_function_name_edge_empty(self): Test edge case: empty / zero / None pass def test_function_name_mock_external(self): Test external dependency is mocked pass模板里故意只保留占位符和空实现不让模型自己去想结构。为什么要这样因为模型一旦自由发挥同一个项目里就会出现三种风格有人喜欢用pytest.fixture有人喜欢setup_method有人偏爱直接平铺函数。三种风格单独看都对混在一起就是一场灾难。模板的作用就是强迫所有生成结果长一个样后续维护成本会低很多。我在模板里还会留一些注释性的提示比如在test_function_name_normal下面写“构造正常输入验证返回值是否符合预期若函数有异常分支使用pytest.raises覆盖”这些注释会被 Agent 当成“该写什么的提示”来理解比在 SKILL.md 里长篇大论讲断言怎么写更直接。模板这个东西本质上是“用结构代替指令”模型看到结构会自动往里面填内容这比抽象描述要省很多 token执行效果也更稳。3.5 references 和 examples不是所有知识都要一次性塞进上下文我在 references 里放的最多的是命名规范和项目目录说明。命名规范里写清楚“测试文件名必须是 test_ 开头”“断言优先用 pytest 原生风格不要用 unittest.TestCase”“mock 对象命名统一用 mock_xxx”。这些规则如果不写Agent 就会根据训练数据里的“常见做法”来猜而训练数据里的常见做法未必适配你们团队。写进 references 之后Agent 在生成前会主动去读之后输出的代码就贴着团队规范走。examples 目录我一般放一对“原始代码 期望测试代码”的示例。比如放一个example_input.py里面有个简单的add(a, b)函数再放一个example_output_test.py展示期望的测试写法。模型对“照着写”的把握比对“听规则”要强很多尤其当你发现 Agent 生成的断言风格总是不对味时给一段示例往往比写十条规则都管用。我现在做 Skill 时examples 几乎是必配的宁可少放点 references也要保证有一对高质量的示例。可能有朋友会问那 references 和 examples 会不会导致 Agent 读太多文件、上下文爆炸我的经验是不要让 Agent 一开始就全读。SKILL.md 里我会明确写“仅当项目有特殊约定时读取 references/naming_guide.md”让模型自己判断什么时候需要深读。这样平时生成用例只读 SKILL.md 跑脚本 读模板上下文可控响应也快。4. 接入 Agent 环境让 Codex / OpenCode 能正常发现并执行 Skill4.1 安装方式和目录命名不同工具加载 Skill 的方式有细微差别但思路是统一的把 Skill 目录放到 Agent 约定的技能目录下然后通过特定的操作或命令触发。以 Codex 为例一般是把整个python-unit-test-generator目录丢进技能目录然后在对话里提到“生成测试用例”这类意图Agent 就会根据 SKILL.md 的 description 自动加载。OpenCode 类似也是把 Skill 目录放到配置目录的 skill 子文件夹中。命名上我有个建议目录名和 SKILL.md 里的 name 字段保持一致避免工具在索引时出现歧义。目录名用短横线分隔的小写单词比如python-unit-test-generator比用驼峰或者带空格的目录名稳得多因为你不知道目标工具在解析路径时会不会对空格过敏。安装完第一件事是确认 Agent 能不能“感知”到这个 Skill。最简单的验证方式是在对话里问一句“你能使用哪些技能”看它列出的列表里有没有你刚装的。如果没出现大概率是 Frontmatter 格式有问题、目录没放对、或者工具需要重启加载配置。我早期经常犯一个错直接复制别人 Skill 的目录结构忘了改目录名和 name 字段结果 Agent 加载是加载了但描述对不上触发时机非常诡异。4.2 一条完整触发指令长什么样装好之后实际使用时的触发指令可以很简单。比如我一般会这么写“请使用 python-unit-test-generator 这个 skill为src/services/order_service.py生成 pytest 单元测试。”这句话里既有明确的 Skill 名称又有具体的输入参数。不过你不能指望所有 Agent 都百分百听话。我见过的情况是Agent 有时候会忽略“使用 xxx skill”的指令直接自己开干。这种时候你先别急着怀疑 Skill 写错了大概率是 SKILL.md 的 description 没写好模型无法理解“这个场景归这个 Skill 管”。把 description 里的触发条件写得更具体一点比如明确说“when the user asks to write tests for python files, use this skill”通常能解决。还有一种情况是 Agent 明明加载了 Skill但执行的时候没有跑 scripts 里的脚本而是直接自己看着源码写。这种情况通常是 Workflow 里步骤写得太模糊。我把 Workflow 第一条直接写成“Run the following command first”把命令原样贴出来模型照着执行的概率就会高很多。记住Skill 文件里的每句话都是在跟模型“对话”指令越具体行为越可控。4.3 用一个真实例子演示完整执行链路我拿src/services/order_service.py里的一个函数举例源码大概是这样的def calc_discount(price: float, coupon: int 0) - float: Apply coupon discount to price. if price 0: raise ValueError(price cannot be negative) if coupon 0 or coupon 100: raise ValueError(coupon must be between 0 and 100) discounted price * (1 - coupon / 100) return round(discounted, 2)Agent 加载 Skill 后第一步执行python scripts/extract_functions.py src/services/order_service.py拿到 JSON 片段{ module: src/services/order_service.py, functions: [ { name: calc_discount, args: [ {name: price, annotation: float}, {name: coupon, annotation: int, default: 0} ], returns: float } ] }接着 Agent 读取模板根据 JSON 里的函数信息补全占位符最终生成的测试文件大概会包含正常折扣计算用例、coupon 默认值用例、price 为负触发 ValueError 的用例、coupon 超范围触发 ValueError 的用例、以及保留两位小数精度的用例。整个链路里 Agent 没有自己发明函数签名所有参数信息都来自脚本输出没有重新发明测试结构所有布局都来自模板没有把命名写乱因为 references 里躺着命名规范。我在实际项目中用这套流程生成的测试人工 review 通过率能到八成以上剩下两成主要是业务边界条件需要补那些本来也不是纯代码层面能自动判断的东西。能做到这个程度对提效来说已经完全够了。5. 调试与避坑写 Skill 过程中最常见的问题和排查思路5.1 重点关注Skill 不触发、触发乱触发、脚本跑不起来第一个大坑是“Skill 完全不触发”。排查顺序我一般是这样先看 Frontmatter 的 name 和 description 有没有写对尤其 description 里是否点明了使用场景再看目录名和 name 是否一致接着确认工具是否真的扫到了这个目录有时候需要重启会话或手动刷新技能列表。这三个问题占了不触发原因的九成。第二个大坑是“乱触发”。你写的是用例生成 Skill结果用户让 Agent 写一段冒泡排序它也把 Skill 加载进去了。问题基本出在 description 写得太宽泛比如只写“generate tests”模型就会在很多时候联想到测试相关的东西。解决方法是把触发条件收窄比如“Only use this skill when the user explicitly asks to generate pytest unit tests for Python code, or when a task involves creating test files for .py modules”。描述写得越挑误触发概率越低。第三个大坑是脚本跑不起来。常见原因包括脚本依赖了第三方库但没在 Skill 里声明、路径参数传错了、Agent 执行命令的工作目录不在项目根目录。我现在会在 SKILL.md 里把执行命令写完整比如python scripts/extract_functions.py source_file并且在脚本里用sys.argv做参数个数校验缺参数就报清晰的中文错误提示。这样一旦有问题Agent 自己也能根据报错信息去调整命令。5.2 一个容易被忽略的问题Agent 生成的用例“编译都过不了”这个坑我踩得很深。早期我的 Skill 里没有“生成后校验”这一步结果 Agent 有时候会生成引用不存在模块、拼错变量名的用例看起来有模有样一跑全是 SyntaxError 和 NameError。后来我在 Skill 里加了一个可选的验证步骤生成用例文件后允许 Agent 自己执行python -m py_compile generated_file做语法检查如果报错就回头修改直到通过为止。加了这一步之后低级错误比例大幅下降。更进一步的校验是用 pytest 直接跑一次生成的用例看能不能过。不过这里要小心如果被测函数涉及外部依赖跑用例时可能因为 mock 不完整而失败这并不代表用例“写得差”。所以我的建议是语法校验是必做项完整跑测试是可选项目而且要在 SKILL.md 里写清楚“执行 pytest 前请先检查是否会造成副作用比如写数据库、发请求”。安全边界这种问题宁可多写几句也不要让 Agent 自由发挥。5.3 上下文与性能怎么避免 Skill 把窗口撑爆Skill 设计里有几个隐形性能坑。第一references 和 examples 里的文件不要塞太多模型在需要时会去读但如果文件很大它会花很多 token 去消化。我一般把 references 控制在每篇 100 行以内examples 控制在 50 行以内。第二SKILL.md 里不要贴大段代码脚本内容放在 scripts 里就好正文只留“调用方式”不要留“实现”。第三提醒 Agent 分步执行而不是一次性把所有信息都加载进来。我写 Workflow 时会刻意使用“先…再…最后…”的顺序性语言让模型一步步来而不是一开始就读完所有引用文件。实测下来这种设计不仅省 token还能让 Agent 输出更稳因为它每一步只用关注当前需要的信息不会被其他文件的细节干扰。6. 继续扩展方向从“用例生成”到更大范围的质量保障体系Skill 的价值在于复用和组合。写完一个“用例生成” Skill 之后你可以顺手再写几个配套的比如“覆盖率分析” Skill专门负责跑 coverage 并输出报告“测试数据构造” Skill专门产出一批符合边界条件的测试数据集“接口测试契约校验” Skill用于检查生成的用例和接口定义是否一致。这些 Skill 单独看都是一个小工具组合起来就是一个围绕测试质量的小型自动化体系。另外Skill 的设计思路是可以跨场景迁移的。今天你在写“自动化用例生成”的 Skill明天你想做“自动化代码 review”骨架完全可以复用SKILL.md 定义流程scripts 做静态分析提取信息references 放团队规范examples 放一份“好的 review”和“坏的 review”示例。我后面几个 Skill 基本都是在这套结构上改出来的越写越熟练产出速度也越来越快。最后分享一个我从实践中养成的习惯每个 Skill 写完我会立即用一个最小示例跑通全流程再逐步增加复杂度。这个“最小示例先行”的方法帮我避掉了无数坑——如果最简单的函数都生成不对就别指望它处理真实服务的复杂逻辑。Skill 就像工具刚磨出来的时候别急着上工地先在院子里试两下顺手了再带出去。
网站建设高端定制企业官网