Skills根本不是你想的那样!
Han Lee 通过抓包和逆向工程从第一性原理深度解析 Claude Agent Skills 系统的内部架构与工作机制,还原了Skills的运行原理(原文链接见文末)
一句话:Claude Skills 并非可执行代码,而是一套基于 prompt 注入的元工具架构。

原文包含了大量的图表和概念,理解起来还是会有一些些困难的,所以我另外再结合官方的仓库提供的SkillA社自家的Claude Skills 仓库都有哪些宝贝技能?将流程进行了进一步的简化,相信会让你对于Skills 全流程有一个更清晰的了解。
在使用官方Skill处理一个 PDF 文件时,你在终端输入:
> 帮我从 report.pdf 提取文字
现在我们来看背后到底发生了什么。
第一步:Claude Code 启动时做了什么
你看到的
$ claude Claude Code v1.0.0 Ready. >
实际发生的(抓包内容)
Claude Code 扫描了这些目录:
~/.claude/skills/# 你的个人 skills.claude/skills/# 项目 skillsplugins/*/skills/# 插件提供的 skills
找到了一个 pdf skill,文件结构:
~/.claude/skills/pdf/ ├── SKILL.md# 主文件 ├── scripts/ │ └── extract.py# Python 脚本 └── references/ └── api-docs.md # API 文档
读取 SKILL.md 的头部:
---name: pdfdescription: 从 PDF 提取文字和表格。当用户提到 PDF、文档提取时使用allowed-tools: Bash(python:*), Read, Write---
注意这里只读这几行! 完整的 SKILL.md 有 800 行,但现在不读。
第二步:你的请求发送到 Claude
实际的 HTTP 请求简化后的版本如下:
POST https://api.anthropic.com/v1/messages{ "model": "claude-sonnet-4-5", "messages": [{"role": "user","content": "帮我从 report.pdf 提取文字"} ], "tools": [{"name": "Skill","description": "执行一个 skillnn可用的 skills:n"pdf": 从 PDF 提取文字和表格。当用户提到 PDF、文档提取时使用n"excel": 创建和编辑 Excel 文件n"ppt": 创建演示文稿","input_schema": {"type": "object","properties": {"command": {"type": "string"}}}},{"name": "Bash","description": "执行 bash 命令","input_schema": {...}},{"name": "Read","description": "读取文件","input_schema": {...}} ]}
关键点:
pdfskill 的 800 行内容没有发送- 只发送了描述:"从 PDF 提取文字和表格。当用户提到 PDF、文档提取时使用"
- 这只有 20 个 token
第三步:Claude 决定调用 pdf skill
Claude 的响应(抓包内容)
{ "content": [{"type": "text","text": "我会帮你从 PDF 中提取文字。"},{"type": "tool_use","id": "toolu_abc123","name": "Skill","input": {"command": "pdf"}} ]}
Claude 的思考过程,我们看不到,大概它这么想的:
用户说 "从 report.pdf 提取文字" → 关键词: PDF, 提取, 文字 → 看看我有什么工具... → "Skill" 工具里有 "pdf": 从 PDF 提取文字和表格 → 完美匹配! → 调用 Skill 工具,command="pdf"
第四步:执行 Skill 工具(这里是重点了!)
Claude Code 收到 Claude 要调用 Skill("pdf")
现在才读取完整的 SKILL.md(800 行):
--- name: pdf description: 从 PDF 提取文字和表格allowed-tools: Bash(python:*), Read, Write --- # PDF 处理专家 你是一个 PDF 处理专家。 ## 工作流程 ### 步骤 1: 检查文件 使用 Read 工具检查 PDF 文件是否存在 ### 步骤 2: 提取文字 运行脚本: python {baseDir}/scripts/extract.py report.pdf ### 步骤 3: 读取结果 Read 工具读取 output.txt ### 步骤 4: 展示给用户 ## 可用工具 - Bash(python:*) - 运行 Python 脚本 - Read - 读取文件 - Write - 保存结果 基础目录: {baseDir}
准备发送给 Claude 的两条消息:
消息 1 (用户可见):
<command-message>正在加载 "pdf" skill</command-message><command-name>pdf</command-name>
消息 2 (用户看不见,但发给 API):
你是一个 PDF 处理专家。## 工作流程### 步骤 1: 检查文件使用 Read 工具检查 PDF 文件是否存在 ... (完整 800 行)
第五步:发送增强后的请求给 Claude
你在界面上看到
> 帮我从 report.pdf 提取文字 正在加载 "pdf" skill
实际发送的完整 HTTP 请求
POST https://api.anthropic.com/v1/messages{ "model": "claude-sonnet-4-5", "messages": [{"role": "user","content": "帮我从 report.pdf 提取文字"},{"role": "assistant","content": [{"type": "text", "text": "我会帮你从 PDF 中提取文字。"},{"type": "tool_use", "name": "Skill", "input": {"command": "pdf"}}]},{"role": "user","content": "<command-message>正在加载 "pdf" skill</command-message>"// 这条你能看到 ↑},{"role": "user","content": "你是一个 PDF 处理专家。nn## 工作流程nn### 步骤 1: 检查文件...","isMeta": true// ↑ 这条你看不到,但发给了 Claude!// 完整 800 行都在这里!},{"role": "user","content": {"type": "command_permissions","allowedTools": ["Bash(python:*)", "Read", "Write"]}// ↑ 这个告诉系统:这些工具预先批准了} ]}
Token 对比:
- 第一次请求: ~50 tokens (只有描述)
- 第二次请求: ~2,000 tokens (完整 skill 内容)
第六步:Claude 按照 skill 指令执行
现在 Claude 的"记忆"里有:
1.用户原始请求:"帮我从 report.pdf 提取文字"
2. 新注入的指令:"你是一个 PDF 处理专家。按照以下步骤。。。"
3. 预批准的工具: Bash(python:*), Read, Write
Claude 开始工作
第 1 个工具调用 – 检查文件:
{ "type": "tool_use", "name": "Read", "input": {"path": "report.pdf","description": "检查 PDF 文件是否存在" }}
系统返回:
{ "type": "tool_result", "content": "文件存在,大小 1.2MB"}
第 2 个工具调用 – 运行 Python 脚本:
{ "type": "tool_use", "name": "Bash", "input": {"command": "python ~/.claude/skills/pdf/scripts/extract.py report.pdf","description": "提取 PDF 文字" }}
这个调用不需要用户确认!
为什么? 因为 Bash(python:*) 在 allowed-tools 里被预批准了!
系统执行脚本,返回:
{ "type": "tool_result", "content": "提取完成,输出保存到 output.txt"}
第 3 个工具调用 – 读取结果:
{ "type": "tool_use", "name": "Read", "input": {"path": "output.txt" }}
返回:
{ "type": "tool_result", "content": "这是一份关于 2024 年度财务报告...nn总收入: 1000 万美元n..."}
第七步:Claude 返回结果给你
你看到的
> 帮我从 report.pdf 提取文字 正在加载 "pdf" skill ✓ 已从 report.pdf 提取文字 内容如下: 这是一份关于 2024 年度财务报告 总收入: 1000 万美元 总支出: 800 万美元 净利润: 200 万美元 ...
为什么Skill高效又节省呢?
没有 Skill 的情况
你需要这样说:
> 用 pdftotext 命令从 report.pdf 提取文字, > 然后读取输出文件, > 如果遇到错误要处理, > 最后格式化显示给我
每次都要重复这些指令!
API 请求(每次都发):
{ "messages": [{"role": "user","content": "用 pdftotext 命令从 report.pdf 提取文字,然后读取输出文件,如果遇到错误要处理,最后格式化显示给我"} ]}
Token: ~80 tokens × 每次使用
有 Skill 的情况
你只需要说:
> 帮我从 report.pdf 提取文字
第一次 API 请求(只发描述):
{ "tools": [{"name": "Skill","description": "..."pdf": 从 PDF 提取文字和表格..." }]}
Token: ~20 tokens
第二次 API 请求(发完整指令):
{ "messages": [...,{"content": "你是 PDF 专家...(完整 800 行)", "isMeta": true} ]}
Token: ~2,000 tokens × 只在激活时发一次
算下来你要处理 10 个 PDF
没有 Skill:
10 次 × 80 tokens = 800 tokens
有 Skill:
启动时: 20 tokens (描述) 第 1 次激活: 2,000 tokens (完整指令) 后续 9 次: 0 tokens (已在上下文中) 总计: 2,020 tokens
但实际上,Skill 会在对话过程中被压缩,所以后续调用可能会重新加载。

如果你真的抓包 Claude Code,你会看到:
请求头:
POST /v1/messages HTTP/1.1 Host: api.anthropic.com anthropic-version: 2023-06-01 content-type: application/json x-api-key: sk-ant-...
请求体(关键部分):
{ "model": "claude-sonnet-4-5-20250929", "max_tokens": 4096, "tools": [{"name": "Skill","description": "Execute a skill within the main conversationnn<skills_instructions>nWhen users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively...nn<available_skills>n"pdf": 从 PDF 提取文字和表格。当用户提到 PDF、文档提取时使用n"xlsx": 创建和编辑 Excel 文件n</available_skills>","input_schema": {"type": "object","properties": {"command": {"type": "string","description": "The skill name (no arguments). E.g., "pdf" or "xlsx""}},"required": ["command"]}} ], "messages": [...]}
看到了吗?pdf skill 的 800 行内容不在这里只有一行描述
所以Skills 到底是怎么工作的?
就这几步:
- 启动时: 扫描所有 skills,只读前几行(name + description)
- 发请求时: 把所有 skills 的简短描述塞进 "Skill" 工具里
- Claude 匹配: 用 AI 理解能力匹配用户意图和 skill 描述
- 激活 skill: 读取完整的 SKILL.md,注入到对话中
- 执行: Claude 按照注入的指令工作,工具预批准,不用确认

这种架构的设计核心优势:
不用的 skill 不加载完整内容,可以有 100 个 skills,只占 2000 tokens,所有 skills 格式一样,容易分享安全, 工具权限仅在 skill 执行时有效。


