←返回文章列表

Agent Skill 是什么:从 description 触发到 SKILL.md 渲染的完整链路

智能体AI 应用提示词工程
9 分钟-阅读

Skill 不是 prompt 的存储格式,而是一套"description 是触发面、body 是执行面、脚本是延伸面"的三层结构。理解这套机制,才能写出真正会被调用的 skill。

工程师常常陷入一种"复制粘贴疲劳":每次让 Claude 跑某段工作流,都要把一段几百字的 prompt 加上若干背景资料粘贴进对话。哪怕已经存到本地文档里,也得手动复制、手动维护版本。Anthropic 在 2025 年 10 月公开 Agent Skill 这个机制,把这种"反复在用、需要带上下文的工作流"打包成一种 agent 可以自己选用的单元。

读到这里很容易把 Skill 想成"prompt 模板的一种",这是最常见的误解。Skill 真正的关键不是"把 prompt 存盘",而是一套触发与上下文管理机制——它决定了模型怎么知道有这个能力、怎么决定调用、怎么把 SKILL.md 的内容拉进当前对话。

Skill 的物理结构

一个 Skill 是一个目录,根目录必须有一个 SKILL.md 文件,其他可以放脚本、参考资料、模板。一个最小可用的 Skill 长这样:

my-skill/
└── SKILL.md

SKILL.md 的开头是 YAML frontmatter,必须包含两个字段:

---
name: my-skill
description: 一段描述 skill 用途和触发条件的文字
---

# Skill body
具体的工作流说明、步骤、示例、约束。

name 限制是 64 字符以内、kebab-case(小写字母、数字、连字符)。description 限制是 1024 字符以内。可选的 frontmatter 字段包括 license、allowed-tools、metadata、compatibility。

Skill 在 Claude Code 里有三个存放位置:

层级 路径 适用范围
用户 ~/.claude/skills/<name>/SKILL.md 所有项目共享
项目 .claude/skills/<name>/SKILL.md 当前项目,可入仓
Plugin plugin 包内的 skills/ 目录 命名空间 plugin-name:skill-name

API 调用上每个请求最多挂载 8 个 skill,skill bundle 上限 8 MB。

渐进式披露:三级加载

Skill 之所以能数量多而不污染上下文,靠的是"渐进式披露"(progressive disclosure)这套三级加载机制。

第一级:会话 / agent 启动时,只有所有 skill 的 name + description 被注入系统提示。模型在对话过程中始终知道有哪些 skill 可用、各自负责什么,但 SKILL.md 的 body 还没有进入上下文。

第二级:当模型判断某个 skill 与当前任务相关,它会主动读取那个 SKILL.md 的完整内容。这一步对调用方而言是隐式的,类似于模型"打开一本手册查具体步骤"。

第三级:如果 SKILL.md 引用了脚本或资源,模型按需读取。重要的是——脚本的输出会进入上下文,源代码通常不会。这一点对工程影响很大:可以在 skill 里挂一个几百行的清理脚本,不必担心它占满模型的上下文窗口。

图 1:Skill 的三级加载

flowchart TD
  A[会话启动] --> B[第一级: 所有 skill 的 name + description 进入系统提示]
  B --> C{模型判断当前任务<br/>与某个 skill 相关?}
  C -- 否 --> D[继续对话]
  C -- 是 --> E[第二级: 加载该 skill 的 SKILL.md body]
  E --> F{需要脚本或资源?}
  F -- 否 --> G[按 skill 指导继续]
  F -- 是 --> H[第三级: 按需读取脚本/资源, 输出进上下文]
  H --> G

挂 50 个 skill 在三级机制下的真实代价:第一级是固定开销,描述写得越短越省;第二级和第三级只在被触发时才付,未触发的 skill 实际成本接近 0。这也是为什么 Anthropic 在 API 上把单次请求的 skill 数量上限定在 8 个——超过这个数量,第一级的描述总长会显著挤占上下文。

触发机制:模型判断,不是关键词匹配

很多人第一次看 Skill 会以为触发是关键词匹配——description 里写"PDF",用户说"帮我处理 PDF"就触发。事实并非如此。

触发是模型层面的判断:模型读了系统提示里那一长串 skill 的 description,根据当前用户消息和上下文决定"现在适合用哪一个"。Anthropic 没有公开它注入 skill metadata 的 wrapper 模板文本,但行为上能确认两点:

  • 模型可能不触发:description 写得太模糊、和用户表述对不上、或者任务太琐碎让模型觉得"不值得调用一个 skill",都会导致 skill 静默地被跳过。
  • 模型可能选错:两个 skill 的 description 重叠太多,模型会挑一个,未必是想要的那个。

Claude Code 在文件系统上做了 live reload——改完 SKILL.md 保存,下一轮对话就生效,不需要重启会话。这让 description 的迭代调试可以做到秒级反馈。

description 才是真正的接口

Skill 机制里有一个反直觉的设计:决定 skill 是否会被调用的不是 body 写得多好、脚本多强,而是 description 那 1024 字符里的措辞。模型在第一级只看 description;body 没被读出来之前,所有"工作流多专业""脚本多稳"都不影响触发决策。

skill-creator(Anthropic 自家的"用来写 skill 的 skill")给出的几条 description 设计准则,可以这样翻译成可操作的清单:

  • 第三人称祈使:"Use this skill when...",不要写"This skill does..."。前者是给模型的指令,后者是描述给人看。
  • 列出具体触发短语:明确写"whenever the user mentions ..."、"when the file contains ..."。不要让模型靠揣测。
  • 写 DO NOT TRIGGER:邻近但不该命中的场景要显式排除。Anthropic 官方的 claude-api skill 就是个例子——任何看起来像在调用 LLM 的代码都可能误触发,所以 description 里专门列了排除项(filename 像 *-openai.py、import openai 等)。
  • 稍微"激进"一点:模型在边界场景下倾向于不触发。如果 description 写得含蓄,使用率会比预想的低。
  • 不要超 1024 字符:超出会被静默截断,关键触发短语可能丢掉。

一个反例:

description: A helpful skill for working with markdown files.

这个 description 几乎不可能稳定触发——没有具体场景词、没有动作动词、没有 when to use。

改成可触发的写法:

description: Convert messy or pasted markdown into a clean Obsidian wiki page with frontmatter, tags, and wikilinks. Use this skill whenever the user wants to file content into their wiki, says "save this", "add to my notes", or pastes raw markdown and asks for cleanup. DO NOT TRIGGER for simple formatting questions or one-line edits.

后者在两个维度上更可触发:第一,列出了三个具体的用户表述模式;第二,划清了不该触发的反例边界。这两件事 body 里写得再清楚也帮不上忙,因为触发判定那一刻 body 还没被读出来。

Skill 和它的邻居

Skill 不取代它的几个邻居,而是和它们各管一段。

机制 谁来调 上下文处理 适合做的事
Prompt 模板 工程师手动拼接 拼一次进一次 一次性、不复用
Slash command 用户主动 /name 命令体进对话 用户知道要用、想要明确入口
Skill 模型判断 description 常驻,body 按需 反复出现、需要让模型"自己想起有这工具"
MCP server / tool 模型调用工具 工具签名进对话 接外部系统、需要类型化 IO
Subagent 主 agent 委派 独立 context window 需要并行、需要隔离、需要不同模型 / 工具集

几个常见的判断:

  • 想让"用户键入 /foo 就触发",用 slash command,不要用 skill。
  • 想让"对接 GitHub API",用 MCP,不要把 API 调用塞进 skill body。
  • 想让"主 agent 跑 RAG 时另一个 agent 并行做去重",用 subagent,不要让一个 skill 做两件事。
  • 想让"模型在合适的时候自己想起这是个能做的事",用 skill。

后期版本的 Claude Code 把 slash command 文件并入了 skill 体系:同名情况下 skill 优先。两套机制在物理上越来越接近,但触发语义仍然是"用户主动 vs 模型判断"。

一份完整 SKILL.md 解剖

以 Anthropic 官方仓库的 pdf-processing 为例做拆解。

---
name: pdf-processing
description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction.
---

# PDF Processing
... (body 略)

frontmatter 做了三件事:

  • 能力声明:第一句直接列出 skill 提供的动作(extract / fill / merge)。
  • 触发场景:第二句以 "Use when..." 起,给模型两个判断维度——操作对象是 PDF、用户提及 PDF 相关动词。
  • 没有冗余:没有 "This is a skill that..." 这种自我介绍,没有版本号、作者署名这些会消耗触发判断带宽的噪音。

body 里通常包含:步骤说明、调用外部脚本的位置、需要用户提供的输入、常见失败模式与处理方式。body 不影响触发,但影响"触发之后做得好不好"。这是两个独立的优化目标,不要把它们混在一起。

何时该写、何时不该写

写 skill 之前先问三个问题:

  1. 这件事一年内会再做多少次? 一次性需求写进 prompt 就好。
  2. 这件事是否需要带上下文(步骤、约束、示例、脚本)? 如果只是一句话能讲完的偏好("以后回复都用中文"),用 CLAUDE.md 或 memory 更合适,不需要一整个 skill。
  3. 模型是否需要在没人提醒的情况下"自己想起来"? 如果总是用户主动喊"帮我做 X",slash command 比 skill 更直接。

把这三个问题反过来用,就是不该写 skill 的信号——一次性、内容很短、用户每次都会主动触发的事情,写 skill 是过度工程。

一句话结尾

Skill 不是 prompt 的存储格式,而是一种"description 是触发面、body 是执行面、脚本是延伸面"的三层结构。把这三层各自当作独立的设计对象,skill 才能真正被用起来,而不是写完就成了仓库里没人调用的死代码。

参考资料

YL

我是姚磊

大模型应用开发者,关注 RAG、Agent 和产品化工程。沉淀实践中的判断、踩坑记录和可复用方法。联系我:yaolei_agent@163.com

LLM AppsRAGProductEngineering Notes