
1. 从一次 Agent 失控说起SKILL.md 到底是什么如果你正在做 AI Agent 项目大概率遇到过这种场景在系统提示里写了几千字的规则Agent 该跑偏还是跑偏接了一堆 MCP 工具问它能不能用它回你一句“我没有可用的工具”同一个任务换个说法执行流程就完全不一样。问题不在于模型不够强而在于你把“知识、流程、工具”全塞进了一个上下文里模型分不清什么时候该用哪一段。SKILL.md 就是来解决这个问题的。它本质上是一个用自然语言写的“技能说明书”放在一个独立文件夹里配合脚本和参考文件构成一个可被 Agent 按需加载的能力单元。你可以把它理解成给 Agent 准备的一本操作手册平时只记住书名和一句话简介真正需要做这件事的时候才翻开细看需要动手时才去调用里面的工具脚本。这套机制最早由 Anthropic 在 Claude 的能力扩展体系中提出现在已经被大多数主流 Agent 开发框架接受为一种标准扩展方式。它的核心价值有三个第一是可复用写一次可以在不同会话、不同项目里反复触发第二是可控行为边界写在文档里减少模型自由发挥带来的幻觉第三是可组合多个 Skill 可以串成一条完整工作流。适合读这篇文章的人很明确你已经在用 Claude Code、Cline、Cursor 或者自建的 Agent 框架想让 Agent 稳定执行某类特定任务比如“按固定格式生成周报”“把日志按规则归类”“调用内部 API 做数据校验”但又不希望每次都把完整指令塞进系统提示。接下来我会从文件结构、加载机制、MCP 协作方式一路讲到可复制的模板和本地验证步骤你跟着做就能跑通第一个自定义 Skill。2. SKILL.md 文件结构与三级加载机制详解2.1 一个 Skill 文件夹里到底放什么一个标准的 Skill 以文件夹形式存在名字通常就是技能标识比如weekly-report。文件夹内部至少包含一个SKILL.md其余是可选的脚本和资源。典型结构如下weekly-report/ ├── SKILL.md # 必需元数据 说明文档 ├── scripts/ │ └── fetch_data.py # 可选可执行脚本 ├── references/ │ └── format.md # 可选参考文档、模板 └── assets/ └── template.xlsx # 可选静态资源SKILL.md本身分两段开头是 YAML front matter写元数据下面是 Markdown 正文写详细说明。元数据部分至少要有name和description这两个字段决定了 Agent 能不能在第一时间“想起”这个技能。--- name: weekly-report description: 当用户需要生成周报、汇总本周工作、按固定模板输出进度时使用。输入为原始工作记录输出为 Markdown 格式周报。 ---description的写法直接决定触发准确率。不要写“一个用于处理周报的技能”这种废话要写清楚“什么时候用、输入是什么、输出是什么”。Agent 在意图匹配阶段只看这段元数据它就像名片上的职务和专长写模糊了别人就不知道找你干什么。2.2 三级渐进式加载Token 效率的关键Skill 最巧妙的设计是三级加载它在“让 Agent 知道有这个能力”和“不把上下文撑爆”之间找到了平衡。Level 1 元数据始终加载。Agent 启动时会把所有已安装 Skill 的name和description读进上下文。这部分非常轻一个技能大约 100 Token 左右所以你装几十个技能也不会明显挤占窗口。这一步解决的是“Agent 知道有哪些技能可用”。Level 2 说明文档触发时加载。只有当用户请求和某个 Skill 的 description 匹配上Agent 才会去读SKILL.md的正文。正文建议控制在 5000 Token 以内写清楚步骤、注意事项、输入输出格式。这一步解决的是“Agent 知道这个技能具体怎么做”。Level 3 资源与代码按需加载。脚本、参考文档、模板这些不会自动进上下文。Agent 在执行过程中需要时才通过 bash 读取或运行。脚本代码本身不进入上下文窗口只把执行结果拿回来。这一步解决的是“Agent 能拿到大量辅助信息但不占上下文”。把这三层串起来完整调用链路是用户提出请求 → Agent 扫一遍所有 Skill 的元数据做意图匹配 → 命中后读取对应 SKILL.md 正文 → 按正文指引决定是否调用脚本或读取参考文件 → 执行并返回结果。整个过程里只有命中的那个技能会消耗较多 Token其余技能只占元数据那一点点空间。2.3 Skill 和 Command、MCP、Rules 的边界很多人会把这几个概念混在一起我用一张表说清楚。概念触发方式核心作用典型场景Command用户主动输入执行固定动作/commit、/reviewMCP模型按需调用提供外部工具能力查数据库、调 APIRules启动时全量加载项目级固定约束代码风格、目录规范Skill模型自动匹配封装流程与知识周报生成、日志归类关键区别在于Command 是你按的按钮MCP 是工具箱里的工具Rules 是贴在墙上的规章而 Skill 是一本菜谱——它告诉你做这道菜需要哪些工具、按什么步骤、注意什么火候。MCP 提供“能做什么”Skill 提供“什么时候做、怎么做”。两者不是替代关系而是协作关系下一节会具体讲怎么配合。3. 可复制配置SKILL.md 模板与 MCP 协作配置3.1 一份可直接用的 SKILL.md 模板下面这份模板你可以直接复制改掉 name、description 和正文步骤就能用。我把它设计成“日志归类”场景因为这类任务流程明确、边界清晰最适合作为第一个 Skill 练手。--- name: log-classifier description: 当用户提供原始日志文本、要求按错误级别归类、提取关键异常或生成日志摘要时使用。输入为纯文本日志输出为 Markdown 表格。 --- # 日志归类技能 ## 角色 你是一名资深 SRE擅长从杂乱日志中快速定位异常模式。 ## 输入 用户提供的原始日志文本可能包含多行、多种级别。 ## 执行步骤 1. 逐行扫描日志识别级别标记ERROR、WARN、INFO、DEBUG。 2. 对 ERROR 和 WARN 行提取时间戳、模块名、异常关键词。 3. 按级别分组统计每类数量。 4. 输出 Markdown 表格列为级别、数量、典型样例。 ## 输出格式 固定输出如下结构不要添加额外解释 | 级别 | 数量 | 典型样例 | |------|------|----------| | ERROR | 3 | ... | | WARN | 5 | ... | ## 红线 - 不要编造日志中不存在的行。 - 不要对异常原因做推测只做归类。 - 如果日志为空直接返回“无有效日志”。这份模板体现了几个原则单一职责只做归类、给了明确输出格式Few-Shot 思路、立了红线防止幻觉、定义了输入输出接口。你写自己的 Skill 时把这四点对齐触发准确率和执行稳定性都会明显提升。3.2 MCP 协作配置让 Skill 能调用外部工具Skill 本身不提供工具能力它负责“指挥”真正干活的是 MCP。假设你已经有一个查询内部工单系统的 MCP Server现在要让上面的日志归类 Skill 在遇到未知异常时去查工单需要在 Agent 的配置里同时声明 MCP Server 和 Skill 目录。以 Claude Code 风格的配置为例settings.json里这样写{ mcpServers: { ticket-system: { command: npx, args: [-y, your-org/ticket-mcp], env: { TICKET_API_BASE: https://taotoken.net/api, TICKET_API_KEY: your-key-here } } }, skills: { directories: [./skills] } }这里有三件套必须对齐Base URL、Key、Model ID。如果你用的是兼容 Anthropic 协议的自建 AgentBase URL 填https://taotoken.net/apiKey 在控制台生成Model ID 按你实际调用的模型填。三者缺一请求就会在鉴权或路由阶段失败。配置完成后Skill 的正文里可以这样引用 MCP 工具## 可用工具 - ticket-system.query根据异常关键词查询历史工单。 当 ERROR 行中出现未在 references/known-errors.md 中记录的异常时 调用此工具查询是否有相似工单并把工单号附在样例后面。注意Skill 正文里只写“什么时候调用、传什么参数”不写工具的具体实现。工具实现归 MCP Server 管职责分离才能让两边都稳定。3.3 目录放置与加载顺序Skill 目录的放置位置取决于你的 Agent 框架。常见约定是项目根目录下的skills/文件夹每个子文件夹一个技能。Agent 启动时会扫描这个目录读取每个SKILL.md的 front matter。如果你有多个技能目录在配置里按优先级排列靠前的先被扫描。加载顺序上元数据永远最先加载正文在触发时加载脚本在调用时加载。这意味着你可以在skills/下放几十个技能而不用担心启动变慢或上下文被占满。真正需要控制的只有每个技能 description 的精准度因为那是唯一始终在上下文里的部分。4. 本地验证跑通第一个自定义 Skill4.1 准备一个最小可验证环境先建目录和文件mkdir -p skills/log-classifier cd skills/log-classifier touch SKILL.md把 3.1 节的模板内容写进SKILL.md。然后准备一份测试日志cat test.log EOF 2024-01-15 10:23:11 ERROR [order-service] Connection timeout to payment-gateway 2024-01-15 10:23:12 INFO [order-service] Retry attempt 1 2024-01-15 10:23:15 WARN [inventory-service] Stock level below threshold for SKU-8821 2024-01-15 10:23:18 ERROR [order-service] Connection timeout to payment-gateway 2024-01-15 10:23:20 INFO [order-service] Order 10023 created EOF4.2 触发 Skill 并观察加载行为在你的 Agent 对话里输入“帮我把 test.log 里的日志按级别归类输出表格。”如果 description 写得准确Agent 应该自动匹配到log-classifier读取正文然后按模板输出。验证成功的标志有三个第一Agent 没有反问你“用什么格式”说明正文里的输出格式生效了第二输出是 Markdown 表格列名和模板一致第三ERROR 和 WARN 的样例是从日志里摘的没有编造。如果 Agent 没有触发 Skill先检查 description 是否包含用户可能说的关键词。比如用户说“归类日志”你的 description 里只有“日志摘要”就可能匹配不上。把常见说法都覆盖进去但不要堆砌无关词。4.3 用 API 直接验证 Skill 加载链路如果你想脱离对话界面直接用 API 验证可以发一个带 Skill 元数据的请求。下面是一个 curl 示例Base URL 用https://taotoken.net/apicurl -X POST https://taotoken.net/api/v1/messages \ -H Content-Type: application/json \ -H x-api-key: $TAOTOKEN_API_KEY \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet-4-20250514, max_tokens: 1024, system: 你有一个可用技能log-classifier描述为当用户提供原始日志文本、要求按错误级别归类时使用。, messages: [ {role: user, content: 把这段日志归类ERROR timeout; WARN low stock; ERROR timeout} ] }这个请求把 Skill 的元数据放进 system模拟 Level 1 加载。如果模型返回表格结构说明元数据被正确识别。再进一步你可以把 SKILL.md 正文也放进 system模拟 Level 2观察输出是否更稳定。4.4 验证 MCP 工具调用是否打通在 Skill 正文里加上工具引用后用一条会触发工具调用的输入测试。比如日志里出现一个known-errors.md里没有的异常关键词看 Agent 是否会去调ticket-system.query。如果调用成功返回结果里会带工单号如果失败通常会看到工具名报错或超时。这一步的关键是确认三件套都对Base URL 指向https://taotoken.net/apiKey 有效Model ID 和你的账号权限匹配。任何一项不对工具调用都会在鉴权阶段被拦下。5. 常见报错排查401、local proxy failed 与 choices 解析失败5.1 401 UnauthorizedKey 或 Base URL 不匹配这是最常见的报错。表现是请求直接返回 401日志里写invalid api key或authentication failed。原因通常有三个Key 复制时带了空格或换行Base URL 写成了带路径的完整地址而不是根地址Key 对应的账号没有开通对应模型权限。排查顺序先用echo $TAOTOKEN_API_KEY | wc -c确认长度合理没有多余字符再确认 Base URL 是https://taotoken.net/api不要自己拼/v1/messages到配置里SDK 通常会自己加最后去控制台确认 Key 状态和模型权限。三件套里 Base URL、Key、Model ID 必须来自同一个账号体系混用就会 401。5.2 local proxy failed本地代理配置冲突这个报错通常出现在你本机设置了 HTTP 代理但 Agent 或 SDK 不走代理或者代理地址失效。表现是连接被拒绝或超时日志里写local proxy failed或connect ECONNREFUSED。处理方式检查环境变量HTTP_PROXY、HTTPS_PROXY是否指向一个已经关闭的本地端口。如果是清掉这两个变量再试。如果你确实需要走网络中间层确保地址和端口正确并且该中间层允许访问taotoken.net。注意这里说的是正常的网络配置不要使用任何非正规的网络工具。5.3 reading choices 解析失败响应结构不符合预期这个报错说明 SDK 在解析响应时找不到choices字段。常见原因是 Base URL 指向了一个兼容 OpenAI 协议的端点但你用的 SDK 是按 Anthropic 协议解析的或者反过来。两种协议的响应结构不同混用就会解析失败。解决方法是确认你的 SDK 和 Base URL 协议一致。如果你用的是 Anthropic SDKBase URL 用https://taotoken.net/api请求路径和响应字段都按 Anthropic 格式来。如果你用的是 OpenAI SDK需要确认端点是否支持 OpenAI 格式。不要在一个请求里混用两套协议。5.4 OAuth 相关报错令牌过期或权限不足如果你用的是 OAuth 方式接入可能会遇到token expired或insufficient scope。前者是令牌过期重新走一次授权流程即可后者是授权范围不包含你要调用的模型或工具需要在授权时勾选对应权限。排查时先看报错里的 scope 字段确认你申请的权限和实际调用的是否一致。如果 Skill 里引用了 MCP 工具而 OAuth 令牌没有该工具的权限也会在工具调用阶段报权限错误。这时候要么重新授权要么在 MCP Server 侧调整权限配置。5.5 Skill 不触发description 匹配失败这不是报错但比报错更让人困惑。Agent 正常回复只是没用你的 Skill。原因几乎总是 description 写得太泛或太窄。太泛比如“处理数据”Agent 不知道什么时候该用太窄比如只写了“归类 ERROR 日志”用户说“整理日志”就匹配不上。改法是把用户可能说的动词和名词都覆盖进去同时保持一句话说清输入输出。你可以把 description 当成搜索关键词来写用户会用什么词来描述这个任务你就把这些词放进去。改完重启 Agent 让元数据重新加载。6. 把 Skill 接入你的 Agent 工作流走到这里你已经有了一个能触发的 Skill、一份可复制的模板、一套 MCP 协作配置以及一份排错清单。接下来要做的不是继续加功能而是把它放进真实工作流里跑一周观察哪些输入触发了、哪些没触发、哪些输出需要调整。我自己的习惯是给每个 Skill 建一个bad-cases.md把没触发或输出不对的例子记下来每周把共性问题补进 description 或正文红线。Skill 不是一次写完就完事的它更像一个需要迭代的产品触发准确率和输出稳定性都是调出来的。如果你还没有可用的 API Key可以去控制台生成一个然后按第 4 节的 curl 示例先验证链路通不通。链路通了再把 Skill 目录挂到你的 Agent 配置里。接入文档里有不同框架的配置示例照着改 Base URL 和目录路径就行。需要长期跑编码或 Agent 任务的可以看下 Coding Plan它更适合高频调用场景。先把第一个 Skill 跑通后面加技能就是复制文件夹改内容的事了。