跳到主要内容

如何为你的 Agent 添加 Skills 支持

本指南将介绍如何为 AI Agent 或开发工具添加 Agent Skills 支持。它涵盖了完整的生命周期:发现 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)脚本、参考资料、资产当指令引用它们时视情况而定

模型从一开始就能看到目录,因此它知道有哪些可用的 Skills。当它认为某个 Skill 相关时,就会加载完整的指令。如果这些指令引用了支持文件,模型会根据需要逐个加载它们。

这保持了基础上下文的精简,同时让模型能够按需访问专业知识。一个安装了 20 个 Skills 的 Agent 不需要预先支付 20 套完整指令的 token 成本——只需支付在特定对话中实际使用的那些。

第 1 步:发现 Skills

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

扫描位置

扫描哪些目录取决于你的 Agent 环境。大多数本地运行的 Agent 至少扫描两个作用域:

  • 项目级(相对于工作目录):特定于某个项目或代码库的 Skills。
  • 用户级(相对于主目录):对特定用户的所有项目都可用的 Skills。

其他作用域也是可能的——例如,由管理员部署的组织级 Skills,或与 Agent 本身捆绑的 Skills。合适的作用域集合取决于你的 Agent 的部署模型。

在每个作用域内,考虑同时扫描客户端特定目录.agents/skills/ 约定目录

作用域路径目的
项目级<project>/.<your-client>/skills/你的客户端的原生位置
项目级<project>/.agents/skills/跨客户端互操作性
用户级~/.<your-client>/skills/你的客户端的原生位置
用户级~/.agents/skills/跨客户端互操作性

.agents/skills/ 路径已成为跨客户端 Skill 共享的广泛采用的约定。虽然 Agent Skills 规范没有强制规定 Skill 目录的存放位置(它只定义了里面的内容),但扫描 .agents/skills/ 意味着其他兼容客户端安装的 Skills 会自动对你的客户端可见,反之亦然。

出于实用兼容性的考虑,一些实现也会扫描 .claude/skills/(包括项目级和用户级),因为许多现有的 Skills 都安装在那里。其他额外的位置包括直到 git 根目录的祖先目录(对 monorepo 很有用)、XDG 配置目录以及用户配置的路径。

扫描什么内容

在每个 Skills 目录中,寻找包含确切命名为 SKILL.md 文件的子目录

~/.agents/skills/
├── pdf-processing/
│ ├── SKILL.md ← 已发现
│ └── scripts/
│ └── extract.py
├── data-analysis/
│ └── SKILL.md ← 已发现
└── README.md ← 已忽略(不是 skill 目录)

实用的扫描规则:

  • 跳过不会包含 Skills 的目录,例如 .git/node_modules/
  • 可选择遵循 .gitignore 以避免扫描构建产物
  • 设置合理的边界(例如,最大深度 4-6 层,最多 2000 个目录),以防止在大型目录树中发生失控扫描

处理名称冲突

当两个 Skills 共享相同的 name 时,应用确定性的优先级规则。

现有实现中普遍的约定是:项目级 Skills 覆盖用户级 Skills。

在同一作用域内(例如,在 <project>/.agents/skills/<project>/.<your-client>/skills/ 下都找到了名为 code-review 的 Skills),采用最先发现或最后发现的原则都是可以接受的——选择一种并保持一致。当发生冲突时记录一条警告,以便用户知道某个 Skill 被遮蔽(shadowed)了。

信任考量

项目级 Skills 来自正在处理的代码库,这可能是不受信任的(例如,一个刚克隆的开源项目)。考虑将项目级 Skill 的加载置于信任检查的控制之下——只有在用户将项目文件夹标记为受信任时才加载它们。这可以防止不受信任的代码库将指令静默注入到 Agent 的上下文中。

云端托管和沙盒 Agent

如果你的 Agent 运行在容器或远程服务器中,它将无法访问用户的本地文件系统。发现机制需要根据 Skill 作用域的不同而有所区别:

  • 项目级 Skills 通常是最简单的情况。如果 Agent 在克隆的代码库上运行(即使在沙盒内),项目级 Skills 会随代码一起移动,并且可以从代码库的目录树中扫描到。
  • 用户级和组织级 Skills 在沙盒中不存在。你需要从外部源提供它们——例如,克隆一个配置库、通过 Agent 的设置接受 Skill URL 或包,或者让用户通过 Web UI 上传 Skill 目录。
  • 内置 Skills 可以作为静态资产打包在 Agent 的部署产物中,使其在每个会话中都可用,而无需外部获取。

一旦 Agent 可以获取到 Skills,生命周期的其余部分——解析、披露、激活——的工作方式都是相同的。

第 2 步:解析 SKILL.md 文件

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

提取 Frontmatter

一个 SKILL.md 文件包含两部分:位于 --- 分隔符之间的 YAML Frontmatter,以及闭合分隔符之后的 Markdown 正文。解析步骤如下:

  1. 找到文件开头的起始 --- 以及其后的闭合 ---
  2. 解析它们之间的 YAML 块。提取 namedescription(必填),以及任何可选字段。
  3. 闭合 --- 之后的所有内容(去除首尾空白)即为该 Skill 的正文内容。

有关完整的 Frontmatter 字段集及其约束,请参阅规范

处理格式错误的 YAML

为其他客户端编写的 Skill 文件可能包含技术上无效的 YAML,但它们的解析器碰巧接受了。最常见的问题是包含冒号的未加引号的值:

# 技术上无效的 YAML —— 冒号会破坏解析
description: Use this skill when: the user asks about PDFs

考虑一种后备方案,在重试之前将此类值用引号包裹,或将它们转换为 YAML 块标量。这能以极低的成本提高跨客户端兼容性。

宽松验证

对问题发出警告,但在可能的情况下仍加载该 Skill:

  • 名称与父目录名称不匹配 → 警告,仍然加载
  • 名称超过 64 个字符 → 警告,仍然加载
  • 描述缺失或为空 → 跳过该 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 目录

对于每个发现的 Skill,将 namedescription 以及可选的 locationSKILL.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 字段有两个用途:它启用了文件读取激活(参见第 4 步),并为模型提供了一个基础路径,用于解析 Skill 正文中的相对引用(如 scripts/evaluate.py)。如果你的专用激活工具在其结果中提供了 Skill 目录路径(参见第 4 步中的结构化包装),你可以从目录中省略 location。否则,请包含它。

每个 Skill 大约为目录增加 50-100 个 tokens。即使安装了数十个 Skills,目录仍然保持紧凑。

将目录放置在何处

有两种常见的方法:

系统提示词部分:将目录作为带有标签的部分添加到系统提示词中,并在前面加上关于如何使用 Skills 的简短说明。这是最简单的方法,适用于任何可以访问文件读取工具的模型。

工具描述:将目录嵌入到专用 Skill 激活工具的描述中(参见第 4 步)。这保持了系统提示词的整洁,并自然地将发现与激活结合在一起。

两者都有效。系统提示词放置更简单且兼容性更广;当你有专用的激活工具时,工具描述嵌入则更整洁。

行为指令

在目录旁边包含一个简短的指令块,告诉模型如何以及何时使用 Skills。措辞取决于你支持哪种激活机制(参见第 4 步):

如果模型通过读取文件来激活 Skills:

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.

如果模型通过专用工具激活 Skills:

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 内容本身会提供详细的指令。

过滤

某些 Skills 应该从目录中排除。常见原因包括:

  • 用户在设置中禁用了该 Skill
  • 权限系统拒绝访问该 Skill
  • 该 Skill 已选择退出模型驱动的激活(例如,通过 disable-model-invocation 标志)

从目录中完全隐藏被过滤的 Skills,而不是列出它们并在激活时进行拦截。这可以防止模型浪费对话轮次去尝试加载它无法使用的 Skills。

当没有可用的 Skills 时

如果没有发现任何 Skills,请完全省略目录和行为指令。不要显示空的 <available_skills/> 块,也不要注册没有有效选项的 Skill 工具——这会使模型感到困惑。

第 4 步:激活 Skills

当模型或用户选择了一个 Skill 时,将完整的指令传递到对话上下文中。这是渐进式披露的第 2 层

模型驱动的激活

大多数实现依赖模型自身的判断作为激活机制,而不是在宿主端实现触发器匹配或关键字检测。模型读取目录(来自第 3 步),判断某个 Skill 与当前任务相关,并加载它。

两种实现模式:

文件读取激活:模型使用目录中的 SKILL.md 路径调用其标准的文件读取工具。不需要特殊的基础设施——Agent 现有的文件读取能力就足够了。模型将文件内容作为工具结果接收。当模型具有文件访问权限时,这是最简单的方法。

专用工具激活:注册一个工具(例如 activate_skill),该工具接收 Skill 名称并返回内容。当模型无法直接读取文件时,这是必需的;即使模型可以读取文件,这也是可选的(但很有用)。与原始文件读取相比的优势:

  • 控制返回的内容——例如,剥离 YAML Frontmatter 或保留它(参见下面的模型接收的内容
  • 将内容包装在结构化标签中,以便在上下文管理期间进行识别
  • 在指令旁边列出捆绑的资源(例如 references/*
  • 强制执行权限或提示用户同意
  • 跟踪激活以进行分析

如果你使用专用的激活工具,请将 name 参数约束为有效 Skill 名称的集合(例如,作为工具 Schema 中的枚举)。这可以防止模型幻觉出不存在的 Skill 名称。如果没有可用的 Skills,则完全不要注册该工具。

用户显式激活

用户也应该能够直接激活 Skills,而无需等待模型做出决定。最常见的模式是宿主拦截的斜杠命令或提及语法/skill-name$skill-name)。具体语法由你决定——核心思想是宿主处理查找和注入,因此模型接收 Skill 内容而无需自己采取激活操作。

自动补全小部件(在用户输入时列出可用的 Skills)也可以使其易于发现。

模型接收的内容

当一个 Skill 被激活时,模型会接收该 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 内容(第 5 步
  • 捆绑的资源会呈现给模型,而无需急切加载

列出捆绑的资源

当专用激活工具返回 Skill 内容时,它还可以枚举 Skill 目录中的支持文件(脚本、参考资料、资产)——但它不应该急切地读取它们。当 Skill 的指令引用特定文件时,模型会使用其文件读取工具按需加载它们。

对于大型 Skill 目录,考虑限制列表的长度,并注明它可能是不完整的。

权限白名单

如果你的 Agent 有一个控制文件访问的权限系统,请将 Skill 目录加入白名单,以便模型可以读取捆绑的资源而不会触发用户确认提示。如果没有这个设置,每次引用捆绑的脚本或参考文件都会导致弹出权限对话框,从而打断那些包含 SKILL.md 本身之外资源的 Skills 的工作流。

第 5 步:随着时间的推移管理 Skill 上下文

一旦 Skill 指令进入对话上下文,请在整个会话期间保持它们的有效性。

保护 Skill 内容免受上下文压缩的影响

如果你的 Agent 在上下文窗口填满时会截断或总结较旧的消息,请豁免对 Skill 内容的修剪。Skill 指令是持久的行为指导——在对话中途丢失它们会静默地降低 Agent 的性能,而没有任何可见的错误。模型会继续运行,但缺少了 Skill 提供的专业指令。

常见方法:

  • 将 Skill 工具的输出标记为受保护,以便修剪算法跳过它们
  • 使用第 4 步中的结构化标签来识别 Skill 内容,并在压缩期间保留它

激活去重

考虑跟踪在当前会话中已激活了哪些 Skills。如果模型(或用户)尝试加载已经存在于上下文中的 Skill,你可以跳过重新注入,以避免相同的指令在对话中多次出现。

子 Agent 委托(可选)

这是一种仅受部分客户端支持的高级模式。与其将 Skill 指令注入到主对话中,不如在一个独立的子 Agent 会话中运行该 Skill。子 Agent 接收 Skill 指令,执行任务,并将工作总结返回给主对话。

当一个 Skill 的工作流足够复杂,能够从专用的、专注的会话中受益时,这种模式非常有用。