Skill(技能)是 Claude Code 的领域知识模块。每个 Skill 将特定领域的操作指南封装为一个独立的 Markdown 文件(SKILL.md),按功能分目录存放。Claude Code 支持两个层级的 Skill 目录:
# 项目级:跟随仓库,团队共享
.github/skills/
├── openspec-propose/
│ └── SKILL.md
├── openspec-apply-change/
│ └── SKILL.md
└── openspec-explore/
└── SKILL.md
# 用户级:跨项目生效,个人专属
~/.agents/skills/
└── find-skills/
└── SKILL.md
Skill 的注册方式
Claude Code 启动时,会扫描上述目录,将每个 Skill 的元数据以 XML 块的形式注入 System Prompt:
<skills>
Here is a list of skills that contain domain specific knowledge on a variety of topics.
Each skill comes with a description of the topic and a file path that contains
the detailed instructions.
When a user asks you to perform a task that falls within the domain of a skill,
use the 'read_file' tool to acquire the full instructions from the file URI.
<skill>
<name>openspec-propose</name>
<description>
Propose a new change with all artifacts generated in one step.
Use when the user wants to quickly describe what they want to build...
</description>
<file>/path/to/.github/skills/openspec-propose/SKILL.md</file>
</skill>
<skill>
<name>find-skills</name>
<description>
Helps users discover and install agent skills when they ask questions
like "how do I do X", "find a skill for X"...
</description>
<file>/Users/benson/.agents/skills/find-skills/SKILL.md</file>
</skill>
</skills>注意:此时 SKILL.md 的内容并没有被载入上下文,注入的只有名称、描述和文件路径。<skills> 块同时携带了一条明确指令——当任务匹配某个 Skill 时,主动用 read_file 工具去读取对应文件。这是整个 Skill 系统的核心设计:延迟加载(Lazy Loading)。
Skill 的触发流程
整个过程分为两个阶段:意图匹配和指令注入。
意图匹配
用户输入到达后,模型根据 <skills> 块中所有 Skill 的 <description>,判断当前请求是否命中某个 Skill。这本质上是一次语义推理,而非关键词搜索:
用户输入: "我想提一个新功能的改动方案"
│
▼
模型匹配所有 Skill 描述
│
├── openspec-propose: "...generate a complete proposal..." ✓ 匹配
├── openspec-apply-change: "...start implementing tasks..." ✗
└── find-skills: "...discover and install skills..." ✗
│
▼
决策: 需要加载 openspec-propose指令注入
意图匹配后,模型发出 read_file 工具调用,读取对应 SKILL.md 的完整内容:
{
"type": "tool_use",
"name": "read_file",
"input": {
"filePath": "/path/to/.github/skills/openspec-propose/SKILL.md",
"startLine": 1,
"endLine": 200
}
}工具返回全文后,Skill 的详细步骤被写入当前对话上下文,模型随即按照 Skill 中定义的流程逐步执行。
Tool Use API
Skill 的懒加载依赖 Anthropic 的 Tool Use(工具调用)能力。每次加载一个 Skill,底层完成一次完整的 API 往返。
第一次请求:模型决定调用工具
POST https://api.anthropic.com/v1/messages
{
"model": "claude-sonnet-4-6",
"tools": [
{
"name": "read_file",
"description": "Read the contents of a file.",
"input_schema": {
"type": "object",
"properties": {
"filePath": { "type": "string" },
"startLine": { "type": "number" },
"endLine": { "type": "number" }
},
"required": ["filePath", "startLine", "endLine"]
}
}
],
"messages": [
{ "role": "user", "content": "我想提一个新功能的改动方案" }
]
}模型返回 stop_reason: "tool_use",表示它需要先读取文件才能继续:
{
"stop_reason": "tool_use",
"content": [
{
"type": "text",
"text": "我需要先读取 openspec-propose 的详细指引..."
},
{
"type": "tool_use",
"id": "toolu_01XYZ",
"name": "read_file",
"input": {
"filePath": "/path/to/.github/skills/openspec-propose/SKILL.md",
"startLine": 1,
"endLine": 300
}
}
]
}Claude Code 在本地执行工具
收到 tool_use 响应后,Claude Code 在本地读取文件。这一步完全不经过 Anthropic 服务器,文件内容不会离开本机:
const content = fs.readFileSync(input.filePath, 'utf-8')
const lines = content.split('\n')
const excerpt = lines.slice(input.startLine - 1, input.endLine).join('\n')第二次请求:携带 Skill 内容继续推理
Claude Code 将文件内容封装为 tool_result,追加到对话历史后再次请求:
POST https://api.anthropic.com/v1/messages
{
"messages": [
{
"role": "user",
"content": "我想提一个新功能的改动方案"
},
{
"role": "assistant",
"content": [
{ "type": "text", "text": "我需要先读取 openspec-propose 的详细指引..." },
{ "type": "tool_use", "id": "toolu_01XYZ", "name": "read_file", "input": { "..." } }
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01XYZ",
"content": "# Skill: openspec-propose\n\n## Steps\n### 1. Interview the user\n..."
}
]
}
]
}此时模型拥有完整的 Skill 指令,第二次响应中 stop_reason 变为 end_turn,正式开始执行任务。
SKILL.md 的典型结构
一个设计良好的 SKILL.md 会明确划分触发条件和执行步骤,让模型仅凭 <description> 就能做出是否触发的判断:
# Skill: openspec-propose
## When to use this skill
- User wants to propose a new feature or change
- User says "I want to build X" or "propose a change for Y"
## When NOT to use this skill
- User wants to implement (use openspec-apply-change instead)
- User wants to explore an idea (use openspec-explore instead)
## Steps
### 1. Interview the user
Gather requirements:
- What problem does this change solve?
- What is the expected outcome?
### 2. Generate the proposal
Create the following artifacts under `openspec/changes/<name>/`:
- `design.md` — high-level design
- `spec.md` — detailed specification
- `tasks.md` — implementation tasks
### 3. Review with the user
Walk through the proposal and refine until approved."When to use" / "When NOT to use" 的分离设计是关键——它让 <description> 保持简短,模型只需读少量 token 就能完成意图判断,只有真正命中时才加载全文。
多 Skill 的调度
一个请求可能跨越多个 Skill。每个 Skill 的加载都是独立的一次工具调用往返:
tool_use: read_file(openspec-propose/SKILL.md)
↓
tool_result: [Skill A 全文]
↓
tool_use: read_file(find-skills/SKILL.md)
↓
tool_result: [Skill B 全文]
↓
stop_reason: end_turn — 根据所有 Skill 指令执行每加载一个 Skill 增加两次 API 往返,同时 context window 的 token 消耗也随之增长。这正是延迟加载的价值所在——不相关的 Skill 内容始终不进入上下文。
Skill 与 RAG 的区别
Skill 系统与 RAG(Retrieval-Augmented Generation)在表面上都是"按需加载知识",但机制截然不同:
| RAG | Skill | |
|---|---|---|
| 检索方式 | 系统自动,向量相似度 | 模型主动决策(tool_use) |
| 检索粒度 | 文本块(chunk) | 整个 Skill 文件 |
| 内容形式 | 非结构化知识片段 | 结构化操作指南 |
| 可追溯性 | 弱(近似匹配) | 强(精确文件路径) |
RAG 擅长从海量文档中找到相关片段,而 Skill 是主动的、精确的能力模块调度——模型清楚地知道自己需要什么,并主动去取。
总结
Skill 的完整调用链路:
System Prompt 注入 <skills> 块(name + description + file path)
│
▼
用户输入 → 模型语义匹配 → 命中目标 Skill
│
▼
模型生成 tool_use: read_file(SKILL.md)
│
▼
Claude Code 本地读取文件(不经过 Anthropic 服务器)
│
▼
tool_result 携带 Skill 全文追加到 messages
│
▼
第二次 API 请求 → 模型依照 Skill 指令执行任务Skill 系统的精妙之处在于将知识存储与调度决策完全解耦:System Prompt 只承载元数据,完整的领域知识按需加载。模型只需要一句描述,就能在合适的时机自主决定是否读取完整指令——既节省 token,又保留对专项领域的完整操控能力。