> ## Documentation Index
> Fetch the complete documentation index at: https://skills.developerdoc.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 如何为你的 agent 添加 skills 支持

> 为 AI agent 或开发工具接入 Agent Skills 支持的指南。

这份指南会带你完成：如何为 AI agent 或开发工具添加 Agent Skills 支持。它覆盖完整生命周期，包括发现 skills、把它们告知模型、在需要时把内容加载进上下文，以及如何让这些内容在整个会话期间持续发挥作用。

无论你的 agent 架构是什么，核心集成思路都是一样的。真正会变化的是实现细节，而这些细节主要取决于两个因素：

* **skills 存放在哪里？** 本地运行的 agent 可以扫描用户文件系统中的 skill 目录。云端托管或运行在沙箱中的 agent，则需要其他发现机制，例如 API、远程注册表或随部署打包的静态资源。
* **模型如何访问 skill 内容？** 如果模型具备读文件能力，它可以直接读取 `SKILL.md`。否则，你就需要提供专门的工具，或者由程序把 skill 内容注入到 prompt 中。

文中会指出这些差异在哪些地方会影响实现。你不需要一口气支持所有场景，只需要选择适合你 agent 的那条路径。

**前置要求**：需要熟悉 [Agent Skills 规范](/specification)。该规范定义了 `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 就能自动被你的客户端发现，反过来也成立。

<Note>
  有些实现还会为了务实兼容而扫描 `.claude/skills/`（包括项目级和用户级），因为很多已有 skills 就安装在那里。其他可能的补充位置还包括：一直向上扫描到 git 根目录的祖先目录（对 monorepo 很有用）、[XDG](https://specifications.freedesktop.org/basedir-spec/latest/) 配置目录，以及用户自定义路径。
</Note>

### 扫描什么内容

在每个 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 块，提取必需字段 `name` 和 `description`，并读取其他可选字段。
3. 结束 `---` 之后的全部内容，去掉首尾空白后，就是 skill 的正文。

完整的 frontmatter 字段和约束，请查看 [规范](/specification)。

### 处理格式错误的 YAML

为其他客户端编写的 skill 文件，可能包含技术上不合法、但它们自己的解析器碰巧能接受的 YAML。最常见的问题是：值里有冒号却没有加引号：

```yaml theme={null}
# 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 加载。

<Note>
  [规范](/specification) 对 `name` 字段给出了严格约束，例如必须匹配父目录名、允许的字符集和最大长度。这里提到的“宽松处理”是有意放宽这些要求，以提高对其他客户端所编写 skills 的兼容性。
</Note>

### 需要存什么

至少要为每个 skill 记录三个字段：

| 字段            | 说明                 |
| ------------- | ------------------ |
| `name`        | 来自 frontmatter     |
| `description` | 来自 frontmatter     |
| `location`    | `SKILL.md` 文件的绝对路径 |

把这些记录进一个以 `name` 为键的内存映射中，便于激活时快速查找。

你也可以在发现阶段就把 **正文内容**（frontmatter 之后的 Markdown）一并存起来，或者等到激活时再根据 `location` 去读取。前者会让激活更快；后者整体内存占用更低，而且能在 skill 文件被修改后拿到最新内容。

skill 的 **基础目录**（也就是 `location` 的父目录）在后续会用来解析相对路径，以及枚举打包资源。需要时可以根据 `location` 推导出来。

## 步骤 3：向模型披露可用 skills

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

### 构建 skill catalog

对每个发现到的 skill，都把 `name`、`description`，以及可选的 `location`（即 `SKILL.md` 路径）放进适合你技术栈的结构化格式中。XML、JSON，甚至项目符号列表都可以：

```xml theme={null}
<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/*`）
* 可以执行权限检查，或请求用户确认
* 可以统计激活行为用于分析

<Tip>
  如果你使用专用激活工具，最好把 `name` 参数限制为当前有效的 skill 名称集合，例如在工具 schema 里做成 enum。这样可以防止模型虚构一个并不存在的 skill 名称。如果当前没有任何可用 skill，就不要注册这个工具。
</Tip>

### 用户显式激活

用户也应该能够直接激活某个 skill，而不是只能等模型自己判断。最常见的做法是 **slash command 或 mention 语法**（例如 `/skill-name` 或 `$skill-name`），由宿主层拦截并处理。具体语法你可以自行决定，关键点在于：查找和注入由宿主层完成，这样模型即使不主动采取激活动作，也能拿到对应的 skill 内容。

如果再加一个自动补全组件，让用户在输入时就能看到可用 skills，会更容易发现这项能力。

### 模型最终收到什么

当一个 skill 被激活时，模型会收到它的指令。具体收到什么内容，通常有两种选择：

**完整文件**：模型看到整个 `SKILL.md`，包括 YAML frontmatter。这在“读文件激活”模式下是自然结果，因为模型拿到的是原始文件；在专用工具模式下，也同样是合法选择。frontmatter 中可能包含在激活阶段仍然有用的信息，例如 [`compatibility`](/specification#compatibility-field) 字段会描述环境要求，从而影响模型如何执行该 skill。

**仅正文（去掉 frontmatter）**：由宿主层先解析并移除 YAML frontmatter，只返回 Markdown 指令正文。在现有使用专用激活工具的实现中，大多数都会这样做，也就是在发现阶段提取完 `name` 和 `description` 后，把 frontmatter 去掉，只把正文发给模型。

这两种做法在实践中都可行。

### 结构化封装

如果你使用专用激活工具，可以考虑用可识别的标签来包裹 skill 内容。例如：

```xml theme={null}
<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 的工作流足够复杂，值得单独开一个聚焦会话时，这种模式会很有价值。
