
这段时间我在给AI Agent做技能扩展被问得最多的问题不是“怎么写代码”而是“Skill到底跟Prompt有什么区别”。很多人习惯写一个大而全的Prompt然后把它命名为“Skill”结果模型要么想不起来调用要么调用起来做不对。实际上从Agent Skills社区沉淀下来的主流实践几乎都围绕三个组成部分展开SKILL.md、scripts、references。这套结构被Claude Code、Codex、OpenClaw等工具普遍接受慢慢形成了一种事实规范。说白了SKILL.md是模型的岗位说明书scripts是模型的工具箱references是模型的资料库。这篇指南会把这套“三步走”的方法完整拆开为什么是这三样东西、每一步怎么落地、怎么测试自己的Skill能不能被模型自动用起来。不管你是给现成的AI编程工具加技能还是给自研Agent做能力封装这套方法都可以直接套用。我尽量多说实操细节和踩过的坑少说空话。1. Skill到底是什么——先搞懂三个目录各自解决什么问题1.1 Skill和Prompt不是一回事很多人以为Skill就是“长一点的Prompt”。这个理解不算错但会严重限制你的设计思路。Prompt是一次性的每次对话都要把它贴在上下文里模型读一遍、用一遍用完就消失。它是“说给模型听的话”。Skill不一样它是预制好的、可复用的“能力包”模型在合适的场景下会主动发现它、加载它、按里面的流程执行。Skill的交付物不是一段话而是一套带结构的文件系统。我习惯用一个类比Prompt像是你口头交代新同事“今天帮我把这份Excel整理一下”Skill则是你递给新同事一本岗位手册里面写着“遇到Excel整理任务时先看流程说明再调用脚本A碰到异常查参考B”手册旁边还放着已经装好依赖的工具箱。后者才是能被长期复用、跨会话迁移的东西。1.2 三个组成部分的分工一个标准的Skill目录通常长这样my-skill/ ├── SKILL.md ├── scripts/ │ └── my_tool.py └── references/ └── domain_notes.md三个部分的定位完全不一样组成部分核心职责通俗类比SKILL.md定义技能触发条件、执行流程、注意事项岗位说明书scripts/放可执行的脚本、工具代码帮模型“动手”工具箱references/放领域知识、模板、参考资料帮模型“懂行”外置资料库三者并不要求同时存在。有些纯知识型Skill可能只有SKILL.md加references有些动作型Skill可能只有SKILL.md加scripts。但一个完整的、能应对复杂任务的Skill通常三者都有因为模型既要“知道该做什么”也要“能做出来”。1.3 Skill和Agent、插件到底什么关系这个问题在社区里被反复问起。我按自己的理解说一下。Agent是一个“能自主规划并调用工具完成任务”的系统它是执行主体。Skill则是这个主体可以装备的“专项能力”。一个Agent可以装十个Skill日志分析、PPT生成、数据库查询、财报解读……每次任务来临时Agent看哪个Skill匹配就加载哪个。Skill反而更像“插件”吗也不完全一样。传统插件偏向外部系统集成比如给IDE装一个插件是为了连某个服务Skill更像“经验和工具的打包”核心是给模型一套做某件事的方法论脚本只是其中一部分。举例来说一个“日志分析Skill”里装着分析流程、统计脚本、常见报错对照表Agent在排查线上问题时加载它能按流程把活干完。如果没有这个SkillAgent可能也知道“分析日志”这个概念但不知道用什么脚本、参照什么经验、按什么顺序排除只能现场瞎试。2. 第一步SKILL.md——写给模型看的“岗位说明书”2.1 为什么第一步必须先定SKILL.md我见过不少开发者一上来就闷头写脚本写了个挺厉害的Python工具最后不知道怎么让模型“知道该用”。其实顺序反过来才对先写SKILL.md让模型的调用行为有了锚点脚本和参考资料才有意义。打个比方你给新人准备了一堆高级工具但没告诉他什么情况下用哪把工具再高端也是白搭。SKILL.md就是这份“什么情况用什么、怎么用”的说明。它的另一个作用是给你自己理思路如果连说明书都写不清楚这件任务怎么做那脚本大概率也没法把流程串起来。2.2 Frontmatter决定模型什么时候“想起你”现在的Agent在读取Skill时会优先扫描SKILL.md头部的YAML Frontmatter尤其是description字段。模型基本是靠这个字段来判断“当前用户需求是否匹配这个技能”它的重要性远远被低估了。我自己的经验是description要覆盖三件事触发场景用户在什么场景下说出什么话适合用这个技能输入形式技能接收什么类型的信息比如文件路径、URL、域名产出形式技能最终交付什么比如报告、修复建议、SQL语句正例--- name: log-analysis description: Analyze log files when the user asks to summarize errors, count log levels, extract stack traces, or troubleshoot service exceptions from logs. ---反例--- name: log-analysis description: For log analysis tasks. ---第一种描述给模型的检索信号足够强任务该用Skill时几乎不会被漏掉。第二种太泛模型会迷茫到底哪些任务算“日志分析”是让我读文件、还是看API返回结构这个模糊性会导致该用时不用不该用时反而误触发。2.3 正文流程比术语重要SKILL.md正文是写给模型看的“操作流程”不是写给人类看的文档。要使用明确步骤、可验证的动作、具体的检查项。重点是把一件任务的执行路径说清楚而不是堆砌领域名词。一个结构良好的SKILL.md正文通常按这个顺序组织Overview一句话说清这个技能解决什么问题When to Use适合/不适合使用的场景明确边界Workflow带上序号的执行步骤每步说明输入、动作、输出Scripts列出scripts目录下有哪些脚本、各怎么调用References说明references目录里有哪些参考资料何时查阅Troubleshooting常见失败情况和应对方式我见过最好的SKILL.md都是“可执行”的每步指令都能直接或间接转为模型的动作几乎没有修饰性废话。比如“统计日志级别分布”这句可执行而“深入了解日志的结构与特征”这句就是废话模型不知道做完什么样才算完成。2.4 一份可直接参考的SKILL.md模板--- name: log-analysis description: Analyze log files and generate a structured incident report when users need to troubleshoot exceptions, count error levels, or extract stack traces. --- # Log Analysis Skill ## Overview Parses application log files, aggregates log-level statistics, extracts stack traces, and maps errors to known patterns. ## When to Use - User supplies a log file path and asks for error analysis. - User wants to know why a service failed, based on logs. - User needs a quick summary of log severity distribution. ## Workflow 1. Confirm log file path exists. If not, ask the user. 2. Run the analyzer script: python scripts/analyze_log.py log_file 3. Read the JSON output. 4. If ERROR/FATAL entries exist, open references/error_patterns.md and match the top stacks against known causes. 5. Draft a short report using references/example_report.md as the template. ## Scripts - scripts/analyze_log.py: counts levels, extracts top stack traces, outputs JSON. ## References - references/error_patterns.md: mapping of common exceptions to causes and fixes. - references/example_report.md: markdown template for incident summary. ## Troubleshooting - If the script fails due to a missing file, verify the path. - For encoding issues, the script already falls back to utf-8 with replacement; if output looks dirty, check source encoding.这份模板可以直接替换成你自己的业务内容。写完SKILL.md第一步就完成了。3. 第二步scripts目录——把模型算不准的事交给脚本3.1 模型擅长什么不擅长什么大模型擅长的是语义理解、文本生成、逻辑推理但在精确计算、批量文件处理、可靠数据解析这些方面并不可靠。比如让模型逐行统计一个10万行日志文件里ERROR出现了几次它大概率会编出一个接近但不准确的结果。原因是模型天生是概率生成器不是计算器。这也是scripts存在的根本意义凡是“必须精确”或“重复劳动”的任务都应该写成脚本交给代码去执行。模型只负责“判断该用哪个脚本”“解释脚本输出”“根据输出做决策”。说白了脚本是模型的手脚模型是脚本的指挥官。3.2 一个可被模型调用的脚本都有这4个共性我写过的Skill脚本不少摸出了几个共性。如果一个脚本能同时满足这四条模型调用它时会非常顺入口明确有main函数支持命令行调用运行方式一眼可见。输入通过命令行参数传递不要写死在代码里模型要通过sys.argv注入用户提供的文件路径或参数。输出是结构化JSON模型擅长读JSON解析方便。纯文本输出会迫使模型再做一层总结容易失真。异常处理完备文件不存在、编码不对、参数缺失都要有明确报错和退出码否则模型只知道“出错了”但不知道为什么。这四条里我尤其强调第4条。很多人不理解“Skill脚本是给模型用的”它不像给人类用的CLI工具人类看到乱码还能猜个大概模型一旦收到模糊的错误信息只能编理由。脚本的所有提示信息都要面向“机器可读”优化。3.3 让模型知道“有工具可用”有了脚本还不够你必须在SKILL.md里显式告诉模型脚本的存在。可以在Workflow步骤里直接写命令也可以在专门的Scripts小节做索引。模型并不会主动去浏览scripts目录里有什么文件它只读SKILL.md。我之前踩过一次坑scripts目录里放了三个很实用的脚本但SKILL.md只写了流程、没提脚本名结果模型全程用“伪代码假装执行”的方式输出了一堆模拟统计结果。后来我把每个脚本的用途、调用方式写进SKILL.md模型立刻开始老老实实跑脚本。脚本内部的文档字符串也值得写清楚#!/usr/bin/env python3 analyze_log.py — 日志统计与栈提取 Usage: python analyze_log.py log_file Output: JSON with level_count and top_stacks. Exit codes: 0: success 1: file not found 2: missing argument 模型在调用脚本前通常会先扫一眼脚本内容确认接口清晰的docstring能帮它更快确认“这个脚本就是我需要的”。3.4 依赖与环境最容易被卡住的一环如果你的Skill脚本依赖第三方库就要考虑“模型执行环境”这个不可控因素。跟自用脚本不同Skill脚本可能在用户本机、CI环境、远程容器里被模型调用依赖缺失是常态。我建议按优先级做这几件事优先用Python标准库能少装依赖就少装。在SKILL.md的Troubleshooting里写明依赖安装命令。scripts/目录下放一个requirements.txt如果依赖多再配合一份install.sh。还有一个细节值得单独拿出来说在任何包管理器里依赖安装都可能有“构建脚本被忽略”的警告。比如用pnpm装包时常见的[err_pnpm_ignored_builds] ignored build scripts: core-js3.45.1, esbuild0.2这是pnpm出于安全考虑默认不执行依赖包里的postinstall等构建脚本。如果core-js、esbuild这类带原生构建步骤的包被忽略可能在模型侧跑起来表现不稳定。自己开发Skill时不要依赖这种“侥幸安装”要么锁定版本并预先验证要么干脆不用原生模块零依赖的方案在Agent场景反而最稳。4. 第三步references目录——给模型加一个外置资料库4.1 为什么不能把参考资料全塞进SKILL.md模型上下文窗口是有限的哪怕窗口大塞进去的内容也会稀释关键指令。如果把所有领域知识、样例、常见问题都写进SKILL.md模型会抓不住重点加载速度也变慢。这就是references目录的价值把该“常驻”的流程放SKILL.md把该“按需查阅”的知识放references。类比一下就是你办公桌上不会放一整柜子的法规原文只会放一本常用手册。遇到具体疑问才去翻对应章节翻完放回去。references就是这本手册而SKILL.md告诉你“什么时候该去翻”。4.2 什么内容该进references适合放references目录的内容包括领域知识说明比如错误码对照表、协议文档、业务规则示例输出比如一份标准报告模板、一份标准配置文件常见问题的判别步骤比如“KeyError出现时优先检查哪些环节”历史案例比如“上次遇到过类似故障的处理记录”不适合放references的内容更值得注意大段重复SKILL.md里已有的流程说明超大日志原文、超长数据库导出结果更新频率极高、跟具体任务强绑定的临时数据命名也要讲规矩我通常用“场景/主题”来组织references/ ├── error_patterns.md ├── deployment_checklist.md └── examples/ └── incident_report_template.md文件名要一眼能看出用途。千万别出现1.md、2.md这种让模型猜内容的命名。4.3 在SKILL.md里建立清晰的引用索引references目录放得再好模型不知道什么时候该看也白搭。所以在SKILL.md的对应步骤里要明确写清楚“什么情况下打开哪个文件”。比如在日志分析Skill里我不会笼统地说“查一下参考资料”而是写4. If ERROR/FATAL entries exist, open references/error_patterns.md and match the top stacks against known causes.这样模型在执行到第4步时会明确知道“现在需要打开error_patterns.md”。不是每个Skill都需要一次把所有参考资料读完触发式阅读才是references的正确用法。4.4 references和scripts的分工怎么拿捏又回到scripts因为很多人会把scripts和references搞混。我的原则很简单数据、规则、经验、模板需要被“理解”的内容进references计算、解析、转换、访问外部接口需要被“执行”的内容进scripts比如日志分析里“ERROR级别常见的几种根因”是知识进references“统计文件里每行日志的级别并汇总”是动作进scripts。模型读参考资料得出结论靠脚本拿数据作为证据两者配合技能才完整。5. 完整实战从零开发一个日志分析Skill的全过程5.1 需求与目录设计假设我们经常要排查测试环境服务报错每次都得人工用grep统计日志级别、翻堆栈。那我干脆做一个“日志分析Skill”让模型加载后能自动完成统计ERROR、WARN、INFO、DEBUG各自有多少条提取出现频率最高的前20个堆栈片段对照已知错误模式给出可能原因和建议先设计目录结构log-analysis/ ├── SKILL.md ├── scripts/ │ ├── analyze_log.py │ └── requirements.txt └── references/ ├── error_patterns.md └── example_report.md5.2 编写SKILL.md我把第2节里的模板微调一下就是正式版本重点是description覆盖到“总结错误”“统计级别”“提取堆栈”“排查异常”这几种用户说法正文步骤明确到脚本路径和参考文件路径。实际写的时候我还要加一段适用范围边界比如“只适用于文本日志不适合二进制日志”避免模型乱套。5.3 scripts实现analyze_log.py的核心逻辑是读文件、逐行识别级别、累计计数、识别栈帧行、输出JSON。我不会做得太复杂够用且稳是第一原则。#!/usr/bin/env python3 analyze_log.py — 日志级别统计与堆栈提取 Usage: python analyze_log.py log_file Output: JSON with total_lines, level_count, top_stacks. import sys import json import re from collections import Counter LEVELS (DEBUG, INFO, WARN, ERROR, FATAL) def analyze(path: str) - dict: level_counter Counter() current_stack [] stacks [] with open(path, r, encodingutf-8, errorsreplace) as f: for raw in f: line raw.rstrip(\n) matched_level None for level in LEVELS: if line.startswith(level) or f {level} in line: level_counter[level] 1 matched_level level break # 如果当前行是栈帧就累积到 current_stack if re.match(r\sat |^\sFile \, line) or re.match(r^\s(at|com\.|org\.|java\.|Caused by), line): current_stack.append(line.strip()) else: if matched_level is None and not current_stack: continue if current_stack: stacks.append( | .join(current_stack[:10])) current_stack [] if current_stack: stacks.append( | .join(current_stack[:10])) # 取出现次数最多的20个堆栈片段 stack_counter Counter(stacks) top_stacks [s for s, _ in stack_counter.most_common(20)] return { total_lines: sum(level_counter.values()), level_count: dict(level_counter), top_stacks: top_stacks, } if __name__ __main__: if len(sys.argv) 2: sys.stderr.write(error: missing log file argument\n) sys.exit(2) try: result analyze(sys.argv[1]) print(json.dumps(result, ensure_asciiFalse, indent2)) except FileNotFoundError: sys.stderr.write(error: log file not found\n) sys.exit(1)这个脚本只依赖标准库模型在任何环境都能直接跑完全绕开依赖安装问题。设计上特意把“输入日志路径”“输出JSON”两个接口做得很干净模型一看就知道怎么调用。5.4 references填充error_patterns.md内容大概长这样# 常见错误模式 ## Connection refused - 可能原因目标端口未监听、网络策略拦截、服务未启动完成 - 排查建议先看服务进程状态再检查防火墙规则 ## OutOfMemoryError - 可能原因堆内存不足、存在内存泄漏、单次请求数据量过大 - 排查建议查看GC日志保留堆转储后再重启 ## TimeoutException - 可能原因下游接口响应慢、连接池耗尽、慢SQL - 排查建议检查调用链耗时分布优先看数据库慢查询example_report.md则是一份事故报告的Markdown模板包含现象、影响范围、日志统计、根因分析、处理建议五段。模型在生成最终报告时直接套模板输出风格统一看起来很专业。5.5 安装到Agent并做三组测试Skill写完后不是直接扔进目录就结束了要专门做三轮测试。第一轮目录能被正确发现。把log-analysis放进Agent的skills目录用一句模糊的话测试触发“帮我看看这个日志文件有什么问题”。观察Agent是否加载了这个Skill。如果没加载多半是description写得不匹配调整关键词再测。第二轮脚本能被正确执行。准备好一个样例日志里面故意放几个ERROR和堆栈让模型全流程跑一遍。重点观察它是否真的去调用了scripts/analyze_log.py还是凭“记忆”编造统计结果。如果没调用回SKILL.md检查Workflow步骤是否写清楚了脚本路径。第三轮输出是否达到预期。看模型最终报告有没有用上references/error_patterns.md里的内容有没有按照example_report.md的结构输出。如果知识没有被引用试试把SKILL.md里的引用指令写得更明确比如直接写“必须使用references/example_report.md的格式”。不同工具对Skill目录的约定略有差异Claude Code、Codex、OpenClaw的加载方式我都试过基本思路一致只是放置路径和命名规则要看各自文档。如果你用的是自研Agent那就自己实现一个“扫描skills目录、读取SKILL.md、拼接进上下文”的加载器这套目录规范照搬就行。6. 踩坑清单Skill开发中常见的误区和调试方法6.1 description写得太泛模型乱调用或漏调用这是最高频的问题。我见过有人把description写成“Tools for analyzing logs”结果用户问“怎么打开日志文件”这种纯操作问题时模型也硬要加载这个Skill。反过来如果用户说“服务报了OOM帮我排查一下”这个描述反而匹配不上。解决办法是描述动词化、场景化。把触发场景、输入、产出都放进去多试几个用户真实说法再调整关键词。注意不要堆砌同义词而是覆盖不同的“意图表达方式”。6.2 脚本异常路径不处理模型一路黑盒脚本崩了以后模型收到的错误只有一行Traceback它不知道该不该修、怎么修。最好在脚本里做两层防护参数缺失时输出usage并退出码为2文件不存在时输出明确错误并退出码为1我还建议脚本对“大文件”做保护比如超过200MB直接提示“文件过大建议先切割”而不是让模型读到一半内存撑爆。模型看到这类明确提示才知道下一步该做什么。6.3 references组织混乱上下文被塞爆有些Skill把10个参考文件堆在references里模型一次性全读了光参考资料就占了上万token真正的指令反而被淹没。要控制“一次阅读量”。我的做法是单文件控制在100行以内SKILL.md里只引导模型按需读指定文件在references/examples子目录里单独放模板避免模型把模板当成主流程。6.4 依赖安装翻车尤其是pnpm的ignored build scripts警告这节值得单独说。很多JavaScript生态的Skill附件依赖pnpm安装但pnpm默认会忽略依赖包里的构建脚本于是常看到[err_pnpm_ignored_builds] ignored build scripts: core-js3.45.1, esbuild0.2这不是pnpm装错了而是它出于安全考虑默认不执行postinstall这类脚本。对Skill来说这种“装着装着少了点东西”的状态最坑人因为模型跑起来可能时好时坏你很难立刻意识到是构建脚本被忽略了。规避思路有三个Skill脚本优先选纯Python、纯Node标准库彻底绕开原生依赖。如果一定要依赖把版本锁死在SKILL.md里写清楚安装命令。在调试阶段先用一个干净环境跑一遍完整安装确认输出里没有ignored build scripts警告。对自研Agent服务的Skill也可以配置允许列表只对信任的包放开构建脚本。但作为Skill作者最稳的还是让脚本尽量零依赖。6.5 默认模型“看得懂”你的目录结构Skill目录树再清晰模型默认也是“瞎子”它只看得到SKILL.md里写了什么。你不写scripts路径它就找不到脚本你不写references文件名它就不知道还有参考资料可查。所以每次写完Skill我都要做一个“新手测试”假装自己是第一次看到这个SKILL.md闭着眼睛按里面的指令走一遍看能不能顺下来。如果哪一步需要“猜”那就是没写清楚。6.6 Skill也需要版本管理Skill不是一次性Prompt它是会进化的。今天加了新脚本明天补了新的错误模式后天改了报告模板如果不做版本管理过两个月没人知道这个Skill为什么变成这样子。我现在给每个Skill都单独建git仓库至少保存SKILL.md、scripts、references和一个CHANGELOG.md。改一次记一条。测试通过后打一个轻量标签比如v1.0.0。以后模型调用出问题还能快速回退到上一版本。我自己的体会是Skill开发的真正门槛不是写代码而是把“模型该做什么、用什么做、查什么资料”这三件事理清楚。SKILL.md决定模型何时做、怎么做scripts决定它做不做得到references决定它做得准不准。三个部分用“三步走”的方式逐层推进每走一步都可以通过测试来验证整个开发过程会变得非常可控。另外分享一个小技巧如果你刚开始接触Skill建议从自己最痛的一个重复劳动做起比如日志分析、脚本生成、周报汇总。不要一上来就追求“大而全”的技能包先做一个能在命令行跑通的极简版本再慢慢往references里加经验。Skill的价值是在长期使用中复利增长的做得越贴近自己真实工作流用起来越顺手。