ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AI编程助手Skill开发实战:从SKILL.md到scripts与references

AI编程助手Skill开发实战:从SKILL.md到scripts与references Skill 这个名字最近在 AI 圈子里出现频率特别高尤其是在 Codex、Claude 这类编程助手陆续支持之后。很多人第一次接触 Skill是从下载一个别人做好的 Skill开始的真正自己动手写一个的人其实不多。这个事儿看起来神秘拆开之后无非是三个部分一份 SKILL.md 说明文档、一串 scripts 脚本、一堆 references 参考资料。这篇博文就围绕这三件套把从零开发一个测试 Skill 的完整思路和步骤讲清楚适合想给 AI 编程助手定制专属能力的开发者、做智能体落地的工程师以及所有对如何把零散经验固化成 AI 可复用的能力包这件事感兴趣的人。1. Skill 的本质认知它不是提示词也不是 Agent1.1 Skill、Prompt、Agent 三者到底差在哪网上关于 Skill 是什么 的问题特别多很多人拿它跟 Prompt 比又拿它跟 Agent 比。我的理解是Prompt 是一段文字告诉 AI这一次该怎么做Agent 是一个完整的决策和执行循环能自己规划多步动作而 Skill 介于两者之间它是一套结构化的能力包核心作用是让 AI 在需要的时候能像翻开一本操作手册一样快速获得某一类任务的标准处理流程、脚本工具和领域知识。打个比方Prompt 相当于你口头跟同事说帮我修个 bugAgent 相当于你把整个修 bug 的流程、工具链、决策逻辑全部固化成一个自动化系统而 Skill 相当于你递给同事一份图文并茂的《故障排查 SOP 手册》他按照手册里写的步骤、调用手册里附带的脚本就能完成工作。这个类比能解释为什么 Skill 值得单独搞一个文件结构而不是直接写在 System Prompt 里——因为它要承载的东西太多了包括可执行的代码、可查询的参考资料、可复用的步骤模板这些东西塞在 Prompt 里既混乱又浪费 token。1.2 为什么说三步走是最合理的开发路线我最初写 Skill 的时候也走过弯路一股脑把所有东西塞进一个 SKILL.md结果文档冗长、AI 加载慢、执行准确率也不高。后来参考社区的成熟项目发现几乎所有好用的 Skill 都遵循同一种结构用 SKILL.md 描述怎么做用 scripts 解决做什么用 references 提供依据什么。三者各司其职刚好对应开发一个能力包最自然的三个阶段。SKILL.md 是入口AI 首先读它来决定这个场景该不该用、用的话按什么顺序操作。scripts 是执行层凡是需要确定性计算、文件读写、网络请求、数据解析的环节都不应该让 AI 自由发挥而应该由预制的脚本兜底。references 是知识底座存放领域术语表、API 文档、历史案例、设计模式等供 AI 在操作过程中查阅。这样拆分的直接好处是每一部分都能独立维护、独立测试SKILL.md 更新了不影响 scripts 的稳定性scripts 修了 bug 也不需要改动文档references 增删资料更是不动核心逻辑。2. SKILL.md 编写实战一份让 AI 能稳定执行的文档怎么写2.1 核心结构设计SKILL.md 本质上是给 AI 看的开发文档它跟给人看的 README 有本质区别。人看 README 喜欢看原理和背景AI 执行文档则更看重什么时候启用、按什么顺序做什么、触发条件是什么。我建议采用五段式结构元信息frontmatter、场景定义、操作步骤、输入输出规范、注意事项。frontmatter 部分用 YAML 格式至少包含 name 和 description 两个字段。description 非常关键它是 Skill 被检索和触发的依据要写得像搜索引擎的索引词把触发场景、任务类型、典型用户请求都覆盖进去。比如一个日志分析 Skill 的描述不要只写analysis log要写成使用 Python 脚本分析 Nginx/Java 应用日志定位 5xx 错误与慢查询输出统计报告。场景定义部分要写清楚什么时候不适用这往往是新手最容易漏掉的AI 没有明确的否定条件就容易乱触发。2.2 步骤设计的颗粒度把握SKILL.md 的主体是操作步骤这里的关键问题是步骤写到多细才合适写太粗AI 拿到文档不知道具体怎么落地写太细文档冗长不说还容易把 AI 的灵活性锁死。我的经验是用三步法划分颗粒度。顶层只写 3 到 5 个大步骤比如数据采集 → 数据清洗 → 统计分析 → 报告生成每个大步骤下面再用简短清单描述关键操作和信息来源。真正的执行细节要么放到 scripts 里封装要么放到 references 里备份SKILL.md 本身只保留索引性质的内容。这样做的好处是AI 读完 SKILL.md 能在脑海或者说上下文窗口中快速建立任务地图需要细节时按图索骥去 references 查而不是被文档里铺天盖地的细节淹没。2.3 让注意事项真正发挥作用SKILL.md 里的注意事项不是给人看的是给 AI 看的执行约束。写的时候要具体到不要做什么、必须做什么、遇到什么情况停下来。以脚本开发为例我会在原稿中写清不要使用未声明的第三方库路径读取必须基于项目根目录不能使用绝对路径输入文件不存在时立即报错而非自动创建空文件。这些边界条件如果用自然语言堆在正文里AI 很容易混淆优先级把它们提炼到独立的执行约束小节效果明显更好。3. scripts 脚本的准备把确定性交给代码3.1 什么时候该写脚本什么时候该让 AI 自由发挥Skill 里放 scripts核心目的是兜住 AI 的幻觉区间。AI 在生成代码、计算结果、处理复杂逻辑时存在不确定性凡是结果必须精确的环节比如读取 JSON 文件中的特定字段、计算两个时间戳之间的差值、调用 API 解析响应都应该提前写好 Python 脚本让 AI 通过命令行调用来完成任务而不是让 AI 现场手写代码。技能目标是一个测试 Skill那么测试用例生成、结果比对、覆盖统计这类环节就是天然的脚本化场景。从工程习惯的角度我把 scripts 分成三类核心工具脚本完成主要计算或处理、辅助工具脚本准备环境、格式化数据、生成测试报告、适配器脚本对接外部系统或 API。在目录结构上分别放在 scripts/ 根目录、scripts/utils/ 和 scripts/adapters/ 下避免一锅烩。3.2 脚本设计的关键原则输入输出必须标准化Skill 中的脚本跟普通脚本最大的区别在于调用者不是人而是 AI所以脚本的输入输出设计要尽量直白。输入方面用命令行参数传递而不是交互式 input()参数顺序要固定、短参数和长参数尽量同时支持能使用环境变量的就不要在脚本里硬编码。输出方面执行成功时直接输出简洁的结构化结果JSON 或纯文本表格不要打印多余的日志失败时必须输出非零退出码和一行明确的错误提示这样 AI 才能根据退出码判断后续操作分支。以我写的这个测试 Skill 为例核心脚本run_tests.py接收一个测试模块路径作为入参返回 JSON 格式的测试统计结果总用例数、通过数、失败数、失败列表这样 AI 拿到输出后无需二次解析就能组织汇报内容。对比一下如果脚本输出的是大段文本日志AI 还要想办法从里面抠信息既浪费 token 又容易出错。3.3 依赖管理宁缺毋滥Skill 是分发给不同用户、不同环境使用的脚本依赖越重安装门槛越高失败概率也越大。我第一次分发 Skill 的时候直接在 requirements.txt 里列了十几个依赖库结果用户环境装不上报错一堆严重影响体验。后来学到的经验是能用 Python 标准库解决的绝不用第三方库必须用第三方库的场景比如请求网络、解析复杂格式也要在 SKILL.md 里写清楚安装命令和版本要求。用户热搜词里那个 [err_pnpm_ignored_builds] ignored build scripts: core-js3.45.1 就是典型例子——现代包管理器npm/pnpm出于安全考虑默认阻止第三方依赖执行 postinstall 脚本这在安装 node-sass 这类依赖时必然报错同样的问题在 Python 生态里就是系统级依赖缺失。所以在 Skill 的脚本初始化部分我不推荐用包管理器自动安装依赖而是提供一个check_deps.py或setup.sh脚本来检测当前环境缺什么并给出手动安装指引让每个人在自己环境里可控地补齐依赖。4. references 参考资料AI 工作时的知识底座4.1 什么时候用 references而不是直接写在 SKILL.md 里references 目录里放的一定是引用频率高、但没必要每次执行都完整读一遍的资料。如果一段信息在每次执行 Skill 时都必须用到那它应该放在 SKILL.md 正文里如果只是特定分支才会用到或者内容很长、AI 只在需要时才去翻阅就适合放 references。判断标准很简单这段内容你希望 AI 每次执行任务时都从头到尾读一遍吗如果是放 SKILL.md如果不是放 references。实际开发中我经常用 references 存放领域术语对照表、API 接口文档片段、历史问题复盘、典型测试数据样例、代码模板。以测试 Skill 为例references 目录下可以放 pytest 常用断言速查手册、一套标准化的测试报告模板、一个典型的被测函数样例文件这些内容不需要每次执行都读一遍但当 AI 遇到具体问题时按需查一下马上就能进入工作状态。4.2 资料组织与格式选型references 里文件的格式建议优先使用 Markdown 和纯文本。Markdown 适合结构化文档AI 解析效率高纯文本适合配置文件、日志样例。要避免使用 PDF、Word、HTML 等格式AI 读取这些格式的代价更高容易乱码或解析失败。每个文件内部要有清晰的小标题和索引目录方便 AI 在长文档中快速定位需要的内容。目录结构上我习惯在 references 下按主题建一层子目录并在根目录放一个 README.md 全局索引说明每个子目录里有什么资料、什么情况下该去查哪个文件。这个 README 相当于给 AI 用的图书索引能显著减少 AI 在 references 目录里东翻西找的次数。4.3 定期复盘删除无效信息参考资料最忌讳只加不减。运行一段时间后Skill 的调用记录能告诉你哪些资料被频繁访问哪些资料从未被读取那些长期冷门的文件说明它要么没有被触达要么本身就不该放在 Skill 里。我一般每两三个月从代码里调取一次AI 执行日志统计 references 目录下每个文件的打开频率高频文档持续维护低频文档要么精简进 SKILL.md、要么直接归档掉。这个习惯能避免 Skill 体积无限膨胀始终保持轻量高效。5. 完整实操从零搭建一个测试 Skill5.1 工程目录设计与初始化开发一个新的 Skill首先是搭目录骨架。我按照 Skill 社区比较通行的结构来组织这里给出可直接抄的目录树my-test-skill/ ├── SKILL.md ├── scripts/ │ ├── run_tests.py │ ├── check_deps.py │ └── utils/ │ └── html_report.py └── references/ ├── README.md └── pytest-cheatsheet.md初始化时先在根目录创建SKILL.md把 frontmatter 写好然后为两个空目录分别创建布局说明。这里有个经验不要等所有内容都准备齐全才开始写 SKILL.md而是先写一版最简可用版本把结构跑通再逐步往里面补充内容。原因很简单Skill 是一个需要实测迭代的产物前期写得太完整反而会让 AI 对文档的理解负担变重影响测试验证。5.2 编写核心脚本 run_tests.py测试 Skill 的核心是自动化测试执行脚本。我用 Python 标准库 pytest 实现一个精简版关键代码结构如下Run pytest and generate structured result output. import argparse import json import sys import tempfile import subprocess from pathlib import Path def parse_args(): parser argparse.ArgumentParser(descriptionRun pytest for a target directory.) parser.add_argument(target, typestr, helpPath to test target directory or file) parser.add_argument(--format, defaultjson, choices[json, text], helpOutput format) return parser.parse_args() def run_pytest(path: str) - dict: Execute pytest with json report plugin if available, otherwise fallback to junitxml. if not Path(path).exists(): return {ok: False, error: fTarget path does not exist: {path}} result_template {target: path, total: 0, passed: 0, failed: 0, errors: [], duration: 0.0} with tempfile.TemporaryDirectory() as tmpdir: junit_xml_path str(Path(tmpdir) / result.xml) cmd [sys.executable, -m, pytest, path, -q, --junitxml, junit_xml_path, --disable-warnings, --no-header] try: completed subprocess.run(cmd, capture_outputTrue, textTrue, timeout60) except subprocess.TimeoutExpired: return {ok: False, error: pytest timed out after 60s} # parse junit xml (simplified) if Path(junit_xml_path).exists(): import xml.etree.ElementTree as ET root ET.parse(junit_xml_path).getroot() result_template[total] int(root.attrib.get(tests, 0)) result_template[passed] int(root.attrib.get(passed, 0)) result_template[failed] int(root.attrib.get(failures, 0)) for tc in root.iter(testcase): for failure in tc.findall(failure): result_template[errors].append({ name: tc.attrib.get(name, ), message: (failure.attrib.get(message, ) or )[:200] }) result_template[ok] completed.returncode 0 return result_template def main(): args parse_args() result run_pytest(args.target) if args.format json: print(json.dumps(result, ensure_asciiFalse, indent2)) else: print(ftarget: {result[target]}) print(ftotal: {result[total]}, passed: {result[passed]}, failed: {result[failed]}) if result[errors]: print(errors:) for e in result[errors]: print(f - {e[name]}: {e[message]}) sys.exit(0 if result.get(ok) else 1) if __name__ __main__: main()这个脚本设计上有几个细节值得展开说明。脚本不直接调用 pytest 的 Python API而是用subprocess调起新的 pytest 进程并解析 JUnit XML 报告。这么做的好处是测试环境中的 pytest 插件配置、faulthandler、覆盖率钩子都不会干扰主进程的状态隔离性更好。如果直接import pytest后在主进程内执行一旦被测模块抛出 SystemExit 或修改了全局环境变量Skill 的宿主进程也会跟着遭殃。另一个细节是--junitxml临时目录的使用。pytest 的 JUnit XML 输出结构非常稳定即使没有额外插件也能解析出总用例数、失败列表等核心信息缺点是没有详细的堆栈跟踪但足够满足 Skill 场景下的快速判断成败 定位失败用例需求。如果想获得更丰富的 JSON 输出建议在 dependencies 里加入 pytest-json-report 插件但这里为了降低依赖门槛我用标准库 xml.etree 解析轻量且零额外安装。5.3 编写 SKILL.md 主文件SKILL.md的内容是灵魂部分。我写了一个精简但结构完整的版本各位可以参考--- name: run-python-tests description: Run pytest tests for python project modules, collect pass/fail statistics and error messages. Use when user requests test execution, test result reporting, or test failure diagnosis. --- # Run Python Tests Run pytest against a Python module or test directory, and summarize execution results for the user. ## When to Use - User asks to run tests or check test status for a Python project. - User wants to verify a recently changed module still passes existing tests. - User reports a test failure and asks to identify failing test cases. When not to use: if user only wants to write a new test case but not execute it; if the project is not Python. ## Workflow 1. Verify the current directory contains a Python project (has *.py or pyproject.toml). 2. Check Python environment dependencies using python scripts/check_deps.py --name pytest. 3. If dependencies are missing, install pytest first; if installation fails, stop and report the error. 4. Run python scripts/run_tests.py target --format json. 5. Parse the JSON output. Report total/passed/failed numbers to the user. 6. If failures exist, list failing test names and error messages. Optionally re-run the failed tests with pytest target --tbshort -x. ## Execution Constraints - Do not modify user source code while running tests. - Do not send test targets to any external service. - If the target path does not exist, state so explicitly and do not attempt to create it. - Use relative paths consistently; never use absolute paths outside the project root. ## Input / Output - Input: path to a test directory or test file (relative to project root). - Output: JSON object with fields: target, total, passed, failed, errors, duration.几个关键点说一下description 字段写得非常触发友好它把用户什么请求可能触发这个技能直接铺开表述这样无论用户说run tests还是帮我看看测试挂了没AI 都能识别到这个 Skill 与当前需求匹配。场景定义部分单独列了When not to use这是很多早期 Skill 缺失的部分没有它 AI 就可能在用户只想写测试的时候误触发执行。执行约束部分特意补充了不发外部服务不自动创建不存在的目标这层边界意义重大。Skill 会运行在用户本地环境开发者无法预知终端用户的工程上下文这种可能涉及安全边界的约束需要在 SKILL.md 里明确写出来。5.4 补齐 references 资料并测试references 目录的内容比较灵活一个好的起点是准备三样东西pytest 常用命令速查、测试报告模板、项目说明样例。references/README.md写成这样# References Index This directory provides additional context for running and diagnosing Python tests. ## Files - pytest-cheatsheet.md: common pytest command line options and assertion tips. - report-template.md: a reusable test report template for summarizing results to the user. ## When to Consult - Consult pytest-cheatsheet when user asks about specific pytest options or flags. - Consult report-template when preparing a formal test report for delivery.首次写完后强烈建议走一遍完整的三层验证流程。第一层手动在终端依次执行 SKILL.md 里描述的所有命令确认脚本在干净环境下能跑通。这一步看似简单实际能过滤掉大量路径错、依赖缺失、权限问题。第二层在支持 Skill 的 AI 编程助手中加载整个 Skill 目录问它帮我运行项目中 tests 目录下的测试观察 AI 是否成功触发并走完流程。第三层故意设计一个测试失败场景比如在测试文件中插入一个必然失败的断言然后让 AI 执行 Skill看它能否准确识别失败用例并给出有用的错误信息。我当时开发时第三层就抓到过一个问题脚本输出的 JSON 中failed 字段来源于 JUnit XML 的 failures 属性但如果被测项目配置了--continue-on-collection-errors实际失败数可能与 failures 不同。针对这类边界情况我后续在脚本里加了双重校验一个是从 XML 汇总失败数一个是从错误列表长度反推两个不一致时优先输出 error 列表并标记ok: false。6. 常见问题与排查技巧实录6.1 Skill 加载不生效AI 完全无视它这是新手遇到最频繁也最容易困惑的问题。大多数情况不是 SKILL.md 写错了而是 description 没写好AI 在意图匹配时根本没意识到这个 Skill 跟用户问题相关。排查顺序建议为第一步确认 SKILL.md 是否放在 Skill 目录根路径模型系统对文件路径有严格期待放错一层就不会被加载第二步检查 description 里的触发词是否覆盖用户可能的问法对比用户原话跑一下测试和执行 pytest都该被覆盖第三步在终端里手动测试 Skill 是否正常工作排除加载问题后看执行环节本身是否有 bug。有些平台要求 Skill 目录里只能有 SKILL.md 作为入口描述其他文件必须放到子目录如果 SKILL.md 意外放到 scripts 或 references 里同样不会被识别到。6.2 pnpm ignored build scripts 报错用户热搜词里频繁出现的[err_pnpm_ignored_builds] ignored build scripts: core-js3.45.1也在 Skill 工程里遇到过。pnpm 从 v9 开始默认阻止第三方依赖执行 postinstall 脚本npm 生态里多个库常见如 esbuild、core-js、cloudflared依赖 postinstall 下载二进制或打补丁一旦被阻止就会在运行时崩掉。这不是 Skill 开发本身的问题而是 Skill 分发到用户环境后由用户项目自身的 pnpm 版本导致的。解决办法分两步如果项目是自己的在.npmrc里加enable-pre-post-scriptstrue临时放行如果是别人项目的报错在 SKILL.md 的依赖检查环节加上对这种报错的识别输出检测到 pnpm ignored build scripts建议运行 pnpm rebuild 或调整 .npmrc这类修复指引。实际处理中pnpm rebuild 包名能解决大部分已阻止依赖的场景但要记得在 SKILL.md 里把这一分支写好免得 AI 遇到没见过的问题就卡死。6.3 脚本执行路径错误Skill 在用户机器上执行时脚本的工作目录不一定是 Skill 目录所在位置。比如用户从终端命令中心发起任务时当前目录可能是任意位置脚本用相对路径读 references 或写报告就会失败。我在开发初期反复踩这个坑后续统一在脚本开头自动定位 Skill 根目录SKILL_ROOT Path(__file__).resolve().parent.parent这样无论从哪个目录调用脚本都能正确找到 Skill 下的文件和目录。另外与用户项目交互时脚本应该把用户项目根作为工作目录避免把报告文件写到 Skill 目录内造成环境污染。我在编写 Skill 的 scripts 时规定临时文件和报告文件一律输出到用户项目下的.skill-output/目录Skill 本体只读可执行不写任何状态。这样既保证 Skill 的可重复分发也避免了多任务并行时文件互相覆盖。6.4 AI 在执行 Skill 时跳过 references不少人在 Skill 里放了大量参考资料结果 AI 从不主动去查最后看了一眼 SKILL.md 就开始自由发挥。问题往往出在 SKILL.md 里的工作流没有明确指出步骤 X 需要先查阅 references/Y.md。给 AI 的指令要像指挥新手员工一样不写清楚该查手册时查手册你就别指望他自觉查。所以我会在 SKILL.md 的每个工作流步骤后面加一个斜体提示比如见 references/report-template.md或Consult pytest-cheatsheet for options让 AI 在对应节点有一个自然的动作指向。这种显式指引对触发率提升非常明显。6.5 依赖冲突与 Python 版本兼容性Skill 脚本运行在用户环境中最常见的问题就是 Python 版本差异。如果脚本用了 3.10 的语法如match语句、|类型联合操作符而用户环境还是 3.8那脚本直接报 SyntaxErrorAI 看到错误连怎么修都无从下手。我建议脚本内部尽量兼容 Python 3.8。使用新特性的地方通过sys.version_info判断来代替而不是直接覆盖。另外脚本内部不要尝试创建虚拟环境后再跑这会显著增加执行时间和不确定性直接在当前解释器下运行代码层面做好版本兼容。如果确实需要指定依赖版本在 SKILL.md 的依赖检查环节明确校验并终止后续步骤给出修复指引而不是带病执行。7. 关于 Skill 开发的延伸思考写 Skill 说到底是在做知识工程本质是把人的经验、流程封装成 AI 能随时调用的结构化资产。它跟写普通代码的区别在于代码的调用方也是代码但 Skill 的调用方是一个大模型模型的执行习惯、信息偏好、错误恢复能力跟传统程序完全不同所以开发调试思路也要跟着调整。我在维护自己那批 Skill 的过程中最重要的一个经验是小步快跑、持续迭代。第一版永远不可能完美你只要把主流程能跑通、失败时能报错、信息组织清晰这三个底限守好就能先发布给真实用户用起来。后续根据使用日志和用户反馈一点点打磨比闭门造车试图一次性写出完美版本高效得多。最后分享一个小技巧开发完 Skill 后建议在 references 里放一份CHANGELOG.md记录每次版本的改动点和动机。这个文件对用户来说价值不大但对你自己三个月后回来看代码时非常有帮助。Skill 这种项目太容易越改越乱有了一份清晰的变更记录你才能大胆重构而不必担心改坏以前验证通过的内容。
返回列表