Skip to main content
这份指南会带你完成:如何为 AI agent 或开发工具添加 Agent Skills 支持。它覆盖完整生命周期,包括发现 skills、把它们告知模型、在需要时把内容加载进上下文,以及如何让这些内容在整个会话期间持续发挥作用。 无论你的 agent 架构是什么,核心集成思路都是一样的。真正会变化的是实现细节,而这些细节主要取决于两个因素:
  • skills 存放在哪里? 本地运行的 agent 可以扫描用户文件系统中的 skill 目录。云端托管或运行在沙箱中的 agent,则需要其他发现机制,例如 API、远程注册表或随部署打包的静态资源。
  • 模型如何访问 skill 内容? 如果模型具备读文件能力,它可以直接读取 SKILL.md。否则,你就需要提供专门的工具,或者由程序把 skill 内容注入到 prompt 中。
文中会指出这些差异在哪些地方会影响实现。你不需要一口气支持所有场景,只需要选择适合你 agent 的那条路径。 前置要求:需要熟悉 Agent Skills 规范。该规范定义了 SKILL.md 的文件格式、frontmatter 字段以及目录约定。

核心原则:渐进式披露

每一个兼容 skills 的 agent,最终都会遵循同一套三层加载策略:
层级加载什么何时加载token 成本
1. Catalog名称 + 描述会话开始时每个 skill 约 50-100 tokens
2. Instructions完整的 SKILL.md 正文skill 被激活时建议小于 5000 tokens
3. Resources脚本、参考资料、资源文件当指令引用到它们时视情况而定
模型从一开始就能看到 catalog,因此它知道有哪些 skills 可用。当它判断某个 skill 与当前任务相关时,再加载完整指令。如果这些指令又引用了支持文件,模型再按需逐个加载这些文件。 这样可以在保持基础上下文足够小的同时,让模型按需获得专业知识。一个装了 20 个 skills 的 agent,不需要在会话一开始就承担 20 份完整指令的 token 成本,只有真正被用到的 skill 才会付出这部分代价。

步骤 1:发现 skills

在会话启动时,找到所有可用的 skills,并加载它们的元数据。

扫描哪些位置

需要扫描哪些目录,取决于你的 agent 所处的环境。大多数本地运行的 agent 至少会扫描两个范围:
  • 项目级(相对于工作目录):某个项目或仓库专用的 skills。
  • 用户级(相对于 home 目录):某个用户在所有项目中都可用的 skills。
当然也可以有其他范围,比如管理员部署的组织级 skills,或者 agent 自带的内置 skills。应该扫描哪些范围,取决于你的 agent 部署模型。 在每个范围内,都可以同时考虑扫描 客户端专属目录.agents/skills/ 约定目录
范围路径目的
Project<project>/.<your-client>/skills/你的客户端原生位置
Project<project>/.agents/skills/跨客户端互操作
User~/.<your-client>/skills/你的客户端原生位置
User~/.agents/skills/跨客户端互操作
.agents/skills/ 这组路径已经逐渐成为跨客户端共享 skill 的通用约定。虽然 Agent Skills 规范并不强制规定 skill 目录必须放在哪里,它只定义 skill 目录里应该包含什么,但如果你扫描 .agents/skills/,那么其他兼容客户端安装的 skills 就能自动被你的客户端发现,反过来也成立。
有些实现还会为了务实兼容而扫描 .claude/skills/(包括项目级和用户级),因为很多已有 skills 就安装在那里。其他可能的补充位置还包括:一直向上扫描到 git 根目录的祖先目录(对 monorepo 很有用)、XDG 配置目录,以及用户自定义路径。

扫描什么内容

在每个 skills 目录中,寻找 包含名为 SKILL.md 文件的子目录
~/.agents/skills/
├── pdf-processing/
│   ├── SKILL.md          → discovered
│   └── scripts/
│       └── extract.py
├── data-analysis/
│   └── SKILL.md          → discovered
└── README.md             → ignored (not a skill directory)
实用的扫描规则包括:
  • 跳过那些明显不可能包含 skill 的目录,例如 .git/node_modules/
  • 可以选择遵守 .gitignore,避免扫描构建产物
  • 设定合理边界,例如最大深度 4 到 6 层、最多扫描 2000 个目录,以避免在大型目录树中失控

处理重名

当两个 skills 使用同一个 name 时,要应用一个确定性的优先级规则。 现有实现中几乎通用的约定是:项目级 skill 覆盖用户级 skill。 如果发生在同一层级内的重名(例如在 <project>/.agents/skills/<project>/.<your-client>/skills/ 下都找到名为 code-review 的 skill),那么“先发现的优先”或“后发现的优先”都可以,关键是要始终保持一致。发生冲突时记录一条警告,让用户知道有某个 skill 被遮蔽了。

信任问题

项目级 skills 来自当前正在操作的仓库,而这个仓库本身可能并不可信(例如刚克隆下来的开源项目)。你可以考虑在加载项目级 skills 之前做一次信任检查,只有当用户把该项目文件夹标记为可信时才加载。这样可以避免不受信任的仓库悄悄把指令注入 agent 的上下文。

云端托管和沙箱化 agent

如果你的 agent 跑在容器里或远程服务器上,它就无法直接访问用户本地文件系统。此时 skill 的发现方式需要根据 skill 所属范围有所区别:
  • 项目级 skills 往往是最容易处理的情况。如果 agent 是对着一份已克隆的仓库工作,哪怕这个仓库在沙箱内部,项目级 skills 也会跟着代码一起进入环境,因此仍然可以直接从仓库目录树中扫描。
  • 用户级和组织级 skills 在沙箱中通常不存在。你需要从外部来源把它们提供进来,比如克隆配置仓库、在设置界面中接受 skill URL 或 skill 包,或允许用户在 Web UI 中上传 skill 目录。
  • 内置 skills 可以作为静态资源打包进 agent 的部署产物中,这样每次会话都天然可用,无需额外拉取。
一旦这些 skills 对 agent 可见,后面的生命周期步骤,例如解析、披露和激活,处理方式就基本一致了。

步骤 2:解析 SKILL.md 文件

对每个发现到的 SKILL.md,提取它的元数据和正文内容。

提取 frontmatter

一个 SKILL.md 文件由两部分构成:--- 分隔符之间的 YAML frontmatter,以及其后面的 Markdown 正文。解析流程如下:
  1. 找到文件开头的 ---,以及后续对应的结束 ---
  2. 解析两者之间的 YAML 块,提取必需字段 namedescription,并读取其他可选字段。
  3. 结束 --- 之后的全部内容,去掉首尾空白后,就是 skill 的正文。
完整的 frontmatter 字段和约束,请查看 规范

处理格式错误的 YAML

为其他客户端编写的 skill 文件,可能包含技术上不合法、但它们自己的解析器碰巧能接受的 YAML。最常见的问题是:值里有冒号却没有加引号:
# Technically invalid YAML — the colon breaks parsing
description: Use this skill when: the user asks about PDFs
你可以考虑提供一个兜底策略:在重试解析前,自动为这类值补引号,或者把它们转成 YAML block scalar。这样可以用很低的代价提升跨客户端兼容性。

宽松校验

尽可能在有问题时给出警告,但只要还能加载,就继续加载:
  • name 和父目录名不一致 → 警告,但仍然加载
  • name 超过 64 个字符 → 警告,但仍然加载
  • description 缺失或为空 → 跳过该 skill(描述是做披露所必需的),并记录错误
  • YAML 完全无法解析 → 跳过该 skill,并记录错误
把这些诊断信息记录下来,便于之后展示给用户(比如通过调试命令、日志文件或 UI),但不要因为纯外观问题而阻塞 skill 加载。
规范name 字段给出了严格约束,例如必须匹配父目录名、允许的字符集和最大长度。这里提到的“宽松处理”是有意放宽这些要求,以提高对其他客户端所编写 skills 的兼容性。

需要存什么

至少要为每个 skill 记录三个字段:
字段说明
name来自 frontmatter
description来自 frontmatter
locationSKILL.md 文件的绝对路径
把这些记录进一个以 name 为键的内存映射中,便于激活时快速查找。 你也可以在发现阶段就把 正文内容(frontmatter 之后的 Markdown)一并存起来,或者等到激活时再根据 location 去读取。前者会让激活更快;后者整体内存占用更低,而且能在 skill 文件被修改后拿到最新内容。 skill 的 基础目录(也就是 location 的父目录)在后续会用来解析相对路径,以及枚举打包资源。需要时可以根据 location 推导出来。

步骤 3:向模型披露可用 skills

在不加载完整内容的前提下,把有哪些 skills 可用告知模型。这就是渐进式披露中的第 1 层。

构建 skill catalog

对每个发现到的 skill,都把 namedescription,以及可选的 location(即 SKILL.md 路径)放进适合你技术栈的结构化格式中。XML、JSON,甚至项目符号列表都可以:
<available_skills>
  <skill>
    <name>pdf-processing</name>
    <description>Extract PDF text, fill forms, merge files. Use when handling PDFs.</description>
    <location>/home/user/.agents/skills/pdf-processing/SKILL.md</location>
  </skill>
  <skill>
    <name>data-analysis</name>
    <description>Analyze datasets, generate charts, and create summary reports.</description>
    <location>/home/user/project/.agents/skills/data-analysis/SKILL.md</location>
  </skill>
</available_skills>
location 字段有两个作用:一是支持基于读文件的激活方式;二是为模型提供 skill 正文中相对引用的基准路径(例如 scripts/evaluate.py)。如果你的专用激活工具在返回结果时会额外提供 skill 目录路径,那么 catalog 里也可以省略 location。否则,建议保留。 每个 skill 在 catalog 中大约只增加 50 到 100 个 token。即便安装了几十个 skills,catalog 依然很紧凑。

把 catalog 放在哪里

常见做法有两种: System prompt 区块:把 catalog 作为 system prompt 中一个带标签的区块插入,并在前面加几句简短说明,告诉模型如何使用 skills。这是最简单的方式,任何具备文件读取工具的模型都适用。 工具描述:把 catalog 写进一个专用 skill 激活工具的描述里。这样可以让 system prompt 更干净,也能更自然地把“发现”和“激活”绑在一起。 两种方式都能工作。把它放进 system prompt 更简单,也更通用;如果你本来就有专用激活工具,那么把它嵌进工具描述会更整洁。

行为指令

在 catalog 旁边放一段简短指令,告诉模型在什么情况下、通过什么方式使用 skills。具体措辞取决于你支持哪种激活机制: 如果模型通过读文件来激活 skill:
The following skills provide specialized instructions for specific tasks.
When a task matches a skill's description, use your file-read tool to load
the SKILL.md at the listed location before proceeding.
When a skill references relative paths, resolve them against the skill's
directory (the parent of SKILL.md) and use absolute paths in tool calls.
如果模型通过专用工具来激活 skill:
The following skills provide specialized instructions for specific tasks.
When a task matches a skill's description, call the activate_skill tool
with the skill's name to load its full instructions.
这些指令要尽量简洁。目的只是告诉模型“skills 存在”以及“应该如何加载”,真正详细的执行说明会在 skill 内容被加载后由 skill 自己提供。

过滤

有些 skills 不应该出现在 catalog 中。常见原因包括:
  • 用户在设置里禁用了该 skill
  • 权限系统拒绝访问该 skill
  • 该 skill 明确选择不允许由模型主动激活(例如设置了 disable-model-invocation 标志)
对于被过滤的 skills,应该 直接从 catalog 中隐藏,而不是先列出来、等激活时再拦截。否则模型会白白浪费轮次去尝试加载它根本不能用的 skill。

当没有任何 skill 可用时

如果一个 skill 都没有发现到,就不要输出 catalog,也不要附带相关行为指令。不要展示空的 <available_skills/>,也不要注册一个没有任何有效选项的 skill 工具,否则只会让模型困惑。

步骤 4:激活 skills

当模型或用户选择了某个 skill,就把完整指令送进当前对话上下文。这就是渐进式披露中的第 2 层。

模型驱动激活

大多数实现都依赖模型自己的判断来触发激活,而不是在宿主层做关键词匹配或硬编码触发器。模型先读取 catalog,判断某个 skill 是否适合当前任务,然后自行加载它。 常见有两种实现模式: 读文件激活:模型调用它已有的文件读取工具,直接读取 catalog 中给出的 SKILL.md 路径。不需要额外基础设施,agent 现有的读文件能力就够了。模型会把文件内容作为工具结果拿回来。这是在模型拥有文件访问能力时最简单的做法。 专用工具激活:注册一个工具(例如 activate_skill),它接受 skill 名称并返回 skill 内容。当模型不能直接读文件时,这是必需的;即便模型可以读文件,这种方式也依然很有用。相比直接读原始文件,它的优势包括:
  • 可以控制返回什么内容,比如去掉 YAML frontmatter,或者完整保留
  • 可以用结构化标签包裹内容,方便后续上下文管理时识别
  • 可以在返回指令的同时列出打包资源(例如 references/*
  • 可以执行权限检查,或请求用户确认
  • 可以统计激活行为用于分析
如果你使用专用激活工具,最好把 name 参数限制为当前有效的 skill 名称集合,例如在工具 schema 里做成 enum。这样可以防止模型虚构一个并不存在的 skill 名称。如果当前没有任何可用 skill,就不要注册这个工具。

用户显式激活

用户也应该能够直接激活某个 skill,而不是只能等模型自己判断。最常见的做法是 slash command 或 mention 语法(例如 /skill-name$skill-name),由宿主层拦截并处理。具体语法你可以自行决定,关键点在于:查找和注入由宿主层完成,这样模型即使不主动采取激活动作,也能拿到对应的 skill 内容。 如果再加一个自动补全组件,让用户在输入时就能看到可用 skills,会更容易发现这项能力。

模型最终收到什么

当一个 skill 被激活时,模型会收到它的指令。具体收到什么内容,通常有两种选择: 完整文件:模型看到整个 SKILL.md,包括 YAML frontmatter。这在“读文件激活”模式下是自然结果,因为模型拿到的是原始文件;在专用工具模式下,也同样是合法选择。frontmatter 中可能包含在激活阶段仍然有用的信息,例如 compatibility 字段会描述环境要求,从而影响模型如何执行该 skill。 仅正文(去掉 frontmatter):由宿主层先解析并移除 YAML frontmatter,只返回 Markdown 指令正文。在现有使用专用激活工具的实现中,大多数都会这样做,也就是在发现阶段提取完 namedescription 后,把 frontmatter 去掉,只把正文发给模型。 这两种做法在实践中都可行。

结构化封装

如果你使用专用激活工具,可以考虑用可识别的标签来包裹 skill 内容。例如:
<skill_content name="pdf-processing">
# PDF Processing

## When to use this skill
Use this skill when the user needs to work with PDF files...

[rest of SKILL.md body]

Skill directory: /home/user/.agents/skills/pdf-processing
Relative paths in this skill are relative to the skill directory.

<skill_resources>
  <file>scripts/extract.py</file>
  <file>scripts/merge.py</file>
  <file>references/pdf-spec-summary.md</file>
</skill_resources>
</skill_content>
这样做有几个实际好处:
  • 模型可以清楚地区分 skill 指令和对话里的其他内容
  • 宿主层在做上下文压缩时,可以识别 skill 内容并特殊处理
  • 打包资源会被展示给模型,但不会被提前加载

列出打包资源

当专用激活工具返回 skill 内容时,它也可以顺带枚举 skill 目录中的支持文件,例如脚本、参考资料和资源文件,但 不要急着把它们的内容一起读出来。模型只应在 skill 指令引用到这些文件时,再通过读文件工具按需加载具体文件。 如果 skill 目录很大,可以考虑给这个资源列表设置上限,并明确说明列表可能不完整。

权限白名单

如果你的 agent 有基于权限的文件访问控制系统,最好把 skill 目录加入 白名单,这样模型读取打包资源时就不会不断弹出用户确认。否则,只要 skill 除了 SKILL.md 外还引用了脚本或参考文件,每次读取都会打断流程。

步骤 5:随时间管理 skill 上下文

一旦 skill 指令已经进入会话上下文,就要确保它们在整个会话过程中持续有效。

保护 skill 内容不被上下文压缩丢掉

如果你的 agent 会在上下文窗口接近上限时裁剪或总结旧消息,那么应该 把 skill 内容排除在清理之外。skill 指令属于长期有效的行为指导,如果它们在对话中途悄悄消失,agent 的表现会无声退化,而且通常不会有任何显式报错。模型表面上还在继续工作,但已经失去了 skill 提供的专业指令。 常见做法包括:
  • 把 skill 工具输出标记为受保护内容,让裁剪算法跳过它们
  • 在步骤 4 的结构化封装中使用可识别标签,这样在压缩上下文时可以准确识别并保留 skill 内容

去重激活

可以考虑在当前会话中追踪哪些 skills 已经被激活过。如果模型或用户再次尝试加载一个已经在上下文中的 skill,你就可以跳过重复注入,避免同一份指令在会话里出现多次。

subagent 委派(可选)

这是一种只有部分客户端支持的高级模式。与其把 skill 指令直接注入主对话,不如把 skill 放到一个 独立的 subagent 会话 中运行。subagent 接收 skill 指令、完成任务,然后把结果摘要返回给主对话。 当某个 skill 的工作流足够复杂,值得单独开一个聚焦会话时,这种模式会很有价值。