BLOG: https://blog.pdjjq.org/post/claude-code-skill-25ron8
让我们来抓包 Claude Code 的请求, 看看 Claude Skill 的实现吧
{
"name": "Skill",
"description": "Execute a skill within the main conversation\n\nWhen users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.\n\nWhen users ask you to run a \"slash command\" or reference \"/<something>\" (e.g., \"/commit\", \"/review-pr\"), they are referring to a skill. Use this tool to invoke the corresponding skill.\n\nExample:\n User: \"run /commit\"\n Assistant: [Calls Skill tool with skill: \"commit\"]\n\nHow to invoke:\n- Use this tool with the skill name and optional arguments\n- Examples:\n - `skill: \"pdf\"` - invoke the pdf skill\n - `skill: \"commit\", args: \"-m 'Fix bug'\"` - invoke with arguments\n - `skill: \"review-pr\", args: \"123\"` - invoke with arguments\n - `skill: \"ms-office-suite:pdf\"` - invoke using fully qualified name\n\nImportant:\n- When a skill is relevant, you must invoke this tool IMMEDIATELY as your first action\n- NEVER just announce or mention a skill in your text response without actually calling this tool\n- This is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task\n- Only use skills listed in \"Available skills\" below\n- Do not invoke a skill that is already running\n- Do not use this tool for built-in CLI commands (like /help, /clear, etc.)\n- If you see a <command-name> tag in the current conversation turn (e.g., <command-name>/commit</command-name>), the skill has ALREADY been loaded and its instructions follow in the next message. Do NOT call this tool - just follow the skill instructions directly.\n\nAvailable skills:\n- tr:reviewPR: Conducts an automated review of a GitHub Pull Request.\n- tr:updateDoc: Updates the documentation based on recent code changes.\n- tr:initDoc: Generate great doc system for this project\n- tr:what: Clarifies a vague user request by asking clarifying questions.\n- tr:withScout: Handles a complex task by first investigating the codebase, then executing a plan.\n- tr:commit: Analyzes code changes and generates a conventional commit message.\n",
"input_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"skill": {
"description": "The skill name. E.g., \"commit\", \"review-pr\", or \"pdf\"",
"type": "string"
},
"args": {
"description": "Optional arguments for the skill",
"type": "string"
}
},
"required": [
"skill"
],
"additionalProperties": false
}
}
Execute a skill within the main conversation
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
When users ask you to run a \"slash command\" or reference \"/<something>\" (e.g., \"/commit\", \"/review-pr\"), they are referring to a skill. Use this tool to invoke the corresponding skill.
Example:
User: \"run /commit\"
Assistant: [Calls Skill tool with skill: \"commit\"]
How to invoke:
- Use this tool with the skill name and optional arguments
- Examples:
- `skill: \"pdf\"` - invoke the pdf skill
- `skill: \"commit\", args: \"-m 'Fix bug'\"` - invoke with arguments
- `skill: \"review-pr\", args: \"123\"` - invoke with arguments
- `skill: \"ms-office-suite:pdf\"` - invoke using fully qualified name
Important:
- When a skill is relevant, you must invoke this tool IMMEDIATELY as your first action
- NEVER just announce or mention a skill in your text response without actually calling this tool
- This is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task
- Only use skills listed in \"Available skills\" below
- Do not invoke a skill that is already running
- Do not use this tool for built-in CLI commands (like /help, /clear, etc.)
- If you see a <command-name> tag in the current conversation turn (e.g., <command-name>/commit</command-name>), the skill has ALREADY been loaded and its instructions follow in the next message. Do NOT call this tool - just follow the skill instructions directly.
Available skills:
- tr:reviewPR: Conducts an automated review of a GitHub Pull Request.
- tr:updateDoc: Updates the documentation based on recent code changes.
- tr:initDoc: Generate great doc system for this project
- tr:what: Clarifies a vague user request by asking clarifying questions.
- tr:withScout: Handles a complex task by first investigating the codebase, then executing a plan.
- tr:commit: Analyzes code changes and generates a conventional commit message.
除此之外, 在 System 的 Tool Usage Policy 中还有这么一个
- /<skill-name> (e.g., /commit) is shorthand for users to invoke a user-invocable skill. When executed, the skill gets expanded to a full prompt. Use the Skill tool to execute them. IMPORTANT: Only use Skill for skills listed in its user-invocable skills section - do not guess or use built-in CLI commands.
核心定位: 这是一个中转站。它的核心功能是将自然语言(如 "help me fix this bug")或 显式指令(如 "/commit")映射到具体的系统预设能力( Skills )上。
触发机制:
/。状态管理: 它不仅仅是调用,还包含了一个非常关键的状态检查功能(检查 <command-name> 标签),用于防止死循环(即防止模型在已经加载了技能的情况下再次尝试调用技能)。
采用了 Context (背景) -> Trigger (触发器) -> Examples (示例) -> Constraints (约束) -> Knowledge Base (可用列表) 的经典结构。
强调内容( Emphasis ):
IMMEDIATELY、BLOCKING REQUIREMENT、BEFORE generating。这是为了解决 LLM 常见的一个毛病: “光说不练” (即模型回复“好的,我来帮你运行 commit”,但实际上并没有发起工具调用)。这里强制要求工具调用必须是第一动作。<command-name> 标签。这说明该系统是一个多轮对话或基于 Agent 的系统,技能加载后会注入新的 Prompt ,必须防止 Router 再次拦截。适配度:高。这段 Prompt 非常适合高智商模型。
风格分析:
思维链引导: Prompt 隐式地要求模型先进行判断( Check if relevant ),然后行动。
让我们直接调用一个 command, 然后观察他的执行流程吧
Claude Code 其实会把一个 command 的调用拆解成两个步骤
command-nameTag 包裹的命令请求 {
"type": "text",
"text": "<command-message>tr:what</command-message>\n<command-name>/tr:what</command-name>"
},
SKILL.md 的内容
{
"type": "text",
"text": "CONTENT of SKILL"
}
claude code skill 提供的另外一个令人兴奋的能力是: https://code.claude.com/docs/en/skills#add-supporting-files
可以将 skill 涉及到的文件封装在同一个目录下, 这样可以实现更好的结构化.
来看看这部分是怎么实现的, 我们将使用一个空的目录, 看看这个目录的结构吧
.claude
└── skills
└── say-hello
├── scripts
│ └── hello.sh
└── SKILL.md
skill 的内容非常简单
---
name: say-hello
description: Say hello to the user
disable-model-invocation: true
---
run scripts/hello.sh
❯ /say-hello
⏺ Bash(bash /Users/test/code/claude-code-skill-research/.claude/skills/say-hello/scripts/hello.sh)
⎿ Hello, World!
⏺ 脚本已成功执行,输出:Hello, World!
这部分其实就没有什么惊喜了, 但是这里要注意: 不要声明绝对目录
{
"role": "user",
"content": [
{
"type": "text",
"text": "<command-message>say-hello</command-message>\n<command-name>/say-hello</command-name>"
},
{
"type": "text",
"text": "Base directory for this skill: /Users/test/code/claude-code-skill-research/.claude/skills/say-hello\n\nrun scripts/hello.sh\n",
"cache_control": {
"type": "ephemeral"
}
}
]
}
可以看到, 这里也没有什么魔法, 或者很工程的设计, 唯一一个需要注意的是:
Base directory for this skill: /Users/test/code/claude-code-skill-research/.claude/skills/say-hello
所以, skill 的 folder 是怎么实现的呢? 很简单, 在提示词里显式的说明了 skill 的路径, 好吧, 有点失望
另外一个值得观察的: context fork 的能力, 按照官网描述:
Set to
forkto run in a forked subagent context.
听描述应该是说, 会启动一个 subagent, fork 主 agent 的 context, 在完全独立的上下文中运行
让我们看一看, 这个 subagent 的使用吧
现在修改下 SKILL.md, 使用 context fork 以及使用 general-purpose agent 来运行
---
name: say-hello
description: Say hello to the user
disable-model-invocation: true
context: fork
model: sonnet
agent: General-purpose
---
run scripts/hello.sh
执行路径
❯ hello
⏺ Hello! How can I help you today?
❯ /say-hello
⎿ The script ran successfully and outputted:
Hello, World!
The script is located at /Users/test/code/claude-code-skill-research/.claud
/skills/say-hello/scripts/hello.sh and simply prints a greeting message.
❯ ok, hi, claude code
⏺ Hi! What can I help you with?
{
"model": "claude-sonnet-4-5-20250929",
"messages": [
{
"role": "user",
"content": "Base directory for this skill: /Users/test/code/claude-code-skill-research/.claude/skills/say-hello\n\nrun scripts/hello.sh\n"
},
{
"role": "assistant",
"content": [
{
"type": "thinking",
"thinking": "The user wants me to run the script `scripts/hello.sh`. They've also provided a base directory for a skill: `/Users/test/code/claude-code-skill-research/.claude/skills/say-hello`\n\nI should run the script. The path could be:\n1. Relative to the current working directory (which is `/Users/test/code/claude-code-skill-research`)\n2. Relative to the skill base directory\n\nLet me try running it. Since the current working directory is `/Users/test/code/claude-code-skill-research`, the script path would likely be `scripts/hello.sh` relative to that, or it could be within the skill directory.\n\nLet me check what's available. I'll try to run the script first and see what happens.\n\nActually, given that the base directory for the skill is provided, the script is likely at `/Users/test/code/claude-code-skill-research/.claude/skills/say-hello/scripts/hello.sh`\n\nLet me run that script.",
"signature": "Eq0ICkYICxgCKkDugd7USNmyDSocrUDwZIgs5NHZ+c/rCy1NrlCexGsxGojGT34iwzaaRAzlzx8Ej9zyvLcL1W1RcBlABJtseubTEgxgFSMj0JHL/v34/OwaDI8rm6XT4rIsJdZHQCIwfc+IqhSnC2tuc6oAFVemNtHNM8zHjbHe88RSGxRCttWvDstUOwN/yFzsBRWkWJs3KpQHn2gPaqbkB+KO4rHA9zL57EM0U9fag2hq1LudmJQ+EjevffqwidkNw5SQOkxKZi5sWE7bEnBYFLw02stS2B8MPYC/sLlKSXom4/G5EuEDWDK92yOuLa/otpAiVNC897c4m7Tq4EZmPpt52elsdwJFtJSRfn4XWdqP0xi5UeT/h6Z6m0NR3zgF04vpg9HMpo/DYEV0/MxGgKcPODXFOuPfup6LHCsXEH2v2g1TAiqECtUc1Qo/RY2Fc2VV6jrN+vmp8pnwuOUiJuXgSUP4hFIkqZylo9Nwv4tHik2sdzfQW+2RAiKDmsjOjuCLaYEeJ2G6TWEe0kYGzSDiY+Wvl5TXPOPpCtYVrQBWySS7ojMT/1uK9UKD2lPyiiEzYjzlfZ40lLDsJf4YTAXI5IEiiJxi4Du5gxgcq50aanwGXnnbkiYQB8hucKI18CF2NG/7obDyidOYJj56aVLAVqauTFp2Wf6Gw3coY4WZuAokS8uPhucM35oo4Ip+XS5VYwCvVe7iSNqZNgVzntnsipFkEi31F0TKmu9lo/mXEix3p9gjem88JvCAOvGFxorh+1+yPZbK+sIUmwf8DAU+gdb2G1s9Tsiue3aUPiImGY53cxrRWu8OlbJ0GBHNeW/9HrX30Rmx2cV1DkSQpV0VZLES6X8d0q821wqGnF+wPp+QXvN2VDTdQzv74GPnKWKfyDVMlBRIVMZ9Zy4YzOeO5kEDAkXgPKMkTUbfd8f/K97I38+jE9uavVRkyVefjpNkSca89tXCnMMYGormXrkVczZr08Wgny/nJXIS4qapbaoRwVpQWKh8thfVttpkQngktd2152e1iFspfNlnn1PxVJ8EDKvQTKBEXL5hXl6j42XkybXI19za79ppvvViXK/s373IUTtlLeJGZuqUdj1cbtEunNlS0NjDFRMZD6nWdx81tJraJjr7AFKzbuXG57UhUlJ6pR7co3kaCzkpVU899LM0XXoZ702VXy1JvgKIEh8yceQuhGuCVDMT04OeVZMsGk7EPVQpIYLOdMVl08fzrc3096TkFM+wRunonIqDNq/t5pCA/mHiREAOkpSD5ZyFfj1eTY/s5H1vrxJJah26yeHuPRAU4ermSduIx9tcSqioJe/aNK/9HUZTZXDz1eNKw5DUoNCwmeLIa0Tod7Rb+85aGlqTKVyk9EUqhMXBvz4LWn6b4yII2+Ta7wjr+xS3akphBB67TsLQ/BgB"
},
{
"type": "text",
"text": "I'll run the hello.sh script for you."
},
{
"type": "tool_use",
"id": "toolu_01ANhWLy3masVW2f6K7dcSTF",
"name": "Bash",
"input": {
"command": "bash /Users/test/code/claude-code-skill-research/.claude/skills/say-hello/scripts/hello.sh",
"description": "Run the hello.sh script"
},
"cache_control": {
"type": "ephemeral"
}
}
]
},
{
"role": "user",
"content": [
{
"tool_use_id": "toolu_01ANhWLy3masVW2f6K7dcSTF",
"type": "tool_result",
"content": "Hello, World!",
"is_error": false,
"cache_control": {
"type": "ephemeral"
}
}
]
}
]
}
https://github.com/anthropics/claude-code/issues/20492
很遗憾, subagent 里并不是 fork master context, 看起来就像是运行普通的 agnet 一样, 只是把 Skill 的执行放在了 subagent 中
我尝试了以下做法:
现在看来, 这里的 context fork 指的并不是 fork master agent 的 context, 而是指的是 subagent skill + agent 本身的 context, 其实是一个 new 的 context, 有点失望
Skill 工具是一个路由器( Router ) ,而非执行器。它的职责是:
/command)映射到预定义的能力<command-name> 标签进行状态管理,防止递归调用Skill 调用被拆解为两步:
用户输入 → Skill Router → 注入 Skill 内容 → LLM 执行
<command-name>/skill-name</command-name>这不是一个复杂的编排系统,而是简单的 Prompt 拼接。
毫无魔法可言。
实现方式仅仅是在 Prompt 中显式声明基础路径:
Base directory for this skill: /path/to/skill/folder
然后依赖 LLM 自行推断相对路径。这是一个脆弱且依赖模型理解能力的设计。
文档描述具有误导性。
| 官方描述 | 实际行为 |
|---|---|
| "Fork" 主 Agent 的上下文 | 创建全新的独立上下文 |
| Subagent 继承对话历史 | Subagent 仅接收 Skill 指令 |
| 类似子进程 fork | 更像是 spawn 一个新进程 |
实测证明:
context: fork 的 "fork" 是名义上的,实际是 "new"Claude Code Skill 系统是一个工程上极简、能力上有限的实现:
| 维度 | 评价 |
|---|---|
| 架构复杂度 | 低(本质是 Prompt 路由 + 拼接) |
| 扩展性 | 中(支持目录结构、多模型) |
| 隔离性 | 弱(依赖 LLM 自律,无真正沙箱) |
| 上下文管理 | 差(无真正的 fork ,仅 spawn ) |
| 文档准确性 | 存在误导( context fork 名不副实) |
1
arthur63 1 天前 via Android
不太清楚两个问题:
1. command/skill 在被调用时,当前的对话上下文是否会继承给 command/skill 使用? 2. command/skill 被调用完之后,它的 prompt 是否会遗留在主上下文里?如果不遗留,下次调用时会重新加载读取吗? |
2
yoghurtguy 1 天前 via iPhone
这也太 ai 了吧,这风格小弟斗胆猜一下是 GPT 写的
|
3
defunct9 21 小时 15 分钟前 via iPhone
|
4
Valid 20 小时 19 分钟前
基于 markdown 编程
|
5
key 19 小时 0 分钟前
感谢分享!非常有用的内容。
正准备自己抓包研究下呢 |
6
pDJJq OP @yoghurtguy 哈哈哈, 数据抓取和基本的结论是我写的, 最后的结论总结和中间的提示词评价是 Gemini3.0 做的
|
7
pDJJq OP @arthur63
1. 哪怕使用了 context: fork 也不会继承, 这个结论的有效期到 2026.01.24 2. 如果这里的 prompt 值得是调用的输入和输出, 那么会保留, 但是中间步骤不会保留 |
8
gxy2825 18 小时 45 分钟前
提个小问题,Blog 里代码块颜色对比度有点差
|
9
hitsmaxft 17 小时 31 分钟前 via iPhone
前几天和一个同事在争论关于 skill 。
他非要认为 skill 是 subagent 封装。我反对这种意见,skill 绝对是对 agent 的增强,就是动态加载 md 描述 @arthur63 skill 本身是没有执行概念的,你可以理解成程序语言的 import ,引入了能力,接下来交给模型自己发挥 |
10
hitsmaxft 17 小时 28 分钟前 via iPhone
@pDJJq contex fork 这点我还没来得及测试,感谢分享。确实叫 context fork 有点误导人,明明就是 skill 加 task
|
11
815377546 12 小时 9 分钟前
6666 大佬拆解的好
|
12
gateszhang 10 小时 5 分钟前
fork 功能感觉换是很使用的。类似于 linux 的 fork 进程。感觉之后官方应该会晚上的
|
13
pDJJq OP 好滴, 收到
|