上一篇讲的是模型最终看到的输入结构。这一篇顺着其中的
skill listings or reminders往下走,继续看 skill 怎样进入上下文、怎样被定义和暴露,以及怎样在 runtime 中真正执行。下一篇会继续往下,进入与 skill 并列、但粒度更低的 tool system。
很多人第一次接触 Claude Code 的 skill,会把它当成“几个写在 markdown 里的提示词模板”。
但只要顺着源码往下追,你很快就会发现:skill 的定位比轻量提示词文件高得多,更接近一套高层工作流系统。
从 skill 文件怎样被装配进系统,到模型和用户分别怎样触发它,再到一次 skill 调用为什么会影响后续 session 行为,这整条链路都比“提示词模板”复杂得多。
这一篇就顺着这条链路,把 Claude Code 的 skill system 拆开来看。
系列回顾:
- Claude Code 源码深度解析之架构总览:它如何成为一套 agent runtime
- Claude Code 源码深度解析之:agent loop 主链路
- Claude Code 源码深度解析之 Harness Engineering 上篇:如何从用户输入进入运行时主循环
- Claude Code 源码深度解析之 Harness Engineering 下篇:如何治理工具、权限、恢复与长会话
- Claude Code 源码深度解析之 Context Engineering:如何管理 prompt 之外的“上下文工程”
- Claude Code 源码深度解析之 Prompt / Context 结构:Claude Code 到底把哪些东西送进模型
先把 skill 的定位说清楚
在前面文章中,我们已经看到 skill 会以 listing、reminder、attachment 等形式进入模型可见输入面。但如果只停在“模型能看见 skill”,其实还没触到 skill system 的核心。Claude Code 里的 skill,表面上是一段进入上下文的说明,往下看却连着调用入口、运行配置和执行方式。它既可以是用户直接触发的 prompt 型命令,也可以是模型通过 SkillTool 调用的能力单元;把 frontmatter 里的 allowed-tools、model、effort、hooks 一起算进来之后,你会更容易把它理解成一个高层工作流包。
也就是说,skill 处在 prompt、command 和 runtime 配置的交叉点上。把它单纯理解成“更高级的 prompt 模板”,会低估它在整个系统里的位置。
skills 从哪里来
先看一件事:Claude Code 的 skill 并不来自单一目录,它本身就是一个 多来源合流系统。本地 skills/ 目录、legacy commands/ markdown、bundled skills、plugin skills、MCP skills,甚至动态发现的 skills,最后都会汇进同一套能力视图里。
这也和前一篇讲的“输入表面设计”刚好对上。模型看到的 skill 暴露面,背后不是某个静态文件夹,而是一套先聚合、再筛选、再压缩的能力来源集合。
skill 为什么最终会变成 Command
这是 Claude Code 设计里最重要的一点之一。

这意味着 file-based skill 最终会挂载到统一命令宇宙里的 prompt command 抽象上,Claude Code 并没有为它单独维护一套独立的 Skill 类型。
那这个 Command 抽象本身长什么样?
// src/types/command.tsexporttype CommandBase = { name: string description: string aliases?: string[] argumentHint?: string whenToUse?: string version?: string isEnabled?: () =>boolean isHidden?: boolean disableModelInvocation?: boolean userInvocable?: boolean loadedFrom?: 'commands_DEPRECATED' | 'skills' | 'plugin' | 'managed' | 'bundled' | 'mcp' kind?: 'workflow' isSensitive?: boolean}exporttype Command = CommandBase & (PromptCommand | LocalCommand | LocalJSXCommand)Command 有三种变体:PromptCommand、LocalCommand、LocalJSXCommand。skill 对应的是其中的 PromptCommand。一旦被编译成这种形态,它就能和所有内建命令共享同一套查找、权限和调用机制。
这样一来,slash runtime 可以统一处理 skill,commands.ts 可以统一聚合 skill 与 command,SkillTool 也能直接复用 command 的查找与元数据。整体思路很明确:先把 skill 收进已有的 command 抽象,再在这个基础上往上叠高层行为。
frontmatter 才是 skill 的“运行时声明语言”
到这里再回头看 markdown skill 文件,重点就不再是正文,而是 frontmatter。在 src/skills/loadSkillsDir.ts 的 parseSkillFrontmatterFields() 里,Claude Code 会解析大量字段,比如 description、allowed-tools、when_to_use、model、disable-model-invocation、user-invocable、hooks、context: fork、agent、effort、shell,以及 name(显示名覆盖)、argument-hint、arguments、version 等。paths 字段则由同文件中的 parseSkillPaths() 单独解析,再传入 createSkillCommand()。
这说明 frontmatter 不能只当作文档注释来看,它更接近 skill 的运行时声明语言。正文负责提供任务说明,frontmatter 则声明这个工作流怎样被暴露、怎样被调用,以及调用后会改变什么。

skill 怎么同时服务用户和模型
Claude Code 给 skill 准备了两条入口:
用户入口
用户可以通过 /commit、/review-pr 之类的 slash command 触发 skill。
模型入口
模型则通过 SkillTool 正式调用 skill。
更准确地说,这条路径对用户来说常常是“隐式命中”的:模型先在 system-reminder 或 skill listing 里看到有哪些 skill 可用,判断当前任务命中了某个 skill 之后,再显式发起一次 SkillTool 调用。从 runtime 角度看,它仍然是一次标准的 tool invocation;只是触发点不是用户手动输入 /commit 这类命令,而是模型根据当前上下文自主决定要不要调用。
这两条入口背后共享的是同一套 command/skill 定义宇宙。Claude Code 从一开始就让用户入口和模型入口复用同一份定义,差别主要落在入口形式和执行路径上。
SkillTool 的角色到底是什么
src/tools/SkillTool/SkillTool.ts 的地位非常关键。
如果说上一篇讲的是“模型怎样先看见 skill”,那么到了这里,SkillTool 讲的就是“模型看见之后,怎样正式进入 skill runtime”。它承担的是正式入口的职责:查找 skill 对应的 command,校验它是不是 prompt skill,判断模型能不能调用,检查权限,决定 inline 执行还是 fork 执行,并在调用后修改上下文能力面。

SkillTool 是 模型侧 skill runtime 的正式入口。
skill 为什么会成为 runtime modifier
SkillTool.call() 的重要之处在于,它除了返回新消息,还可能带回 contextModifier。
这意味着一次 skill 调用除了补进一段 prompt,还有机会改写 allowedTools、model、effort。
skill 一旦执行,受影响的往往不止当前这一条消息,后续多个 turn 的能力配置也可能一起变化。所以把它看成“高层工作流封装”会更贴切,单次文本替换的理解太窄了。
skill 还能 fork 成子 agent
这一步很能说明 skill 的定位:它已经接近 子 agent 任务模板。前面几篇反复强调 Claude Code 是一个 agent runtime;放到这个语境里看,skill 的作用就是把某些高层任务模式封装成可复用、可调度、必要时可分叉执行的 runtime 单元。
模型为什么不会看见全部 skill
skill 系统还有一个经常被忽略但非常重要的层:SkillTool/prompt.ts。
上一篇里已经提过,模型真正看到的通常不是 skill 全文,而是预算内的 skill listing 或 reminder。这里可以把那部分再落到源码职责上:SkillTool/prompt.ts 负责告诉模型 slash command 本质上也是 skill,告诉模型匹配到 skill 时必须调用 SkillTool,计算 skill listing 的字符预算,并在预算内对描述进行截断。
所以 skill system 除了定义与执行,还有一层很关键的工作:暴露面设计。
skill surfacing:模型侧可见面的预算压缩

这里最关键的设计是,模型看到的 skill 集合,不一定等于用户看到的 slash skill 集合;skills listing 只占上下文窗口中的很小一部分;bundled skill 往往还有更高的可见性优先级。Claude Code 的做法是,先给模型一个技能路标,等真正命中后再展开完整 skill 内容。
这和前一篇总结的 budget-first 输入设计是同一条思路:先暴露足够做决策的摘要,再把昂贵的详细说明延后到真正命中之后。
skill 在整轮模型上下文里处在什么位置
如果把这一篇单独拿出来看,读者很容易把 attention 全部放在 SkillTool 本身上;但把它放回整个系统里,skill 更准确的位置,其实是在“模型可见能力面”里。
上一篇已经讲过,Claude Code 每一轮送进模型的并不是单一 prompt,而是 system prompt、user context、system context、messages、tool schema、attachments、system-reminder 等多层输入一起组成的结构。skill 在这里通常并不以“完整正文”的形态出现,而更常见地出现在两类位置:
system-reminder里的 skill 提示attachment 派生出的 skill listing / skill discovery 信息
也就是说,skill 在模型上下文中的位置,更像是“高层工作流入口信息”,而不是一段长期常驻的主 prompt 正文。它先帮助模型判断当前有没有值得调用的高层工作流,真的命中之后,才会进一步通过 SkillTool 加载更完整的内容。

这个位置关系很重要。因为它解释了两个容易混的点。第一,skill 虽然属于能力系统,但它一开始是通过“上下文暴露”进入模型视野的;第二,模型能不能用对某个 skill,很多时候不取决于 skill 文件本身写得多长,而取决于当前这一轮里有没有把相关 skill 摘要成功表面化。
一个更具体的例子:模型这一轮到底怎么看见 skill
假设用户这一轮说的是:
“帮我 review 这个 PR,先总结主要风险,再给我一个 review comment 草稿。”
从用户视角看,这只是一个普通请求;但从 Claude Code 组织输入的角度看,模型当前这一轮更可能看到的是下面这种结构:
[systemPrompt]- 系统规则、输出风格、工具使用原则[messages]- user: 帮我 review 这个 PR,先总结主要风险,再给我一个 review comment 草稿。[tool schemas]- Read / Grep / Bash / SkillTool / ...[skill listing / reminder]- Skills relevant to your task:- - review-pr: 用于代码审查、风险总结和 review 建议输出- - commit: 用于生成提交信息(当前任务相关性较低)[attachments / other context]- 可能还有当前分支、diff、被引用文件、已有诊断信息等这时模型并不是“直接读到 review-pr skill 的完整 markdown,然后自然照着做”。更常见的情况是:它先在这一轮输入里看到一个和当前任务高度相关的 skill 摘要,于是判断“这里适合调用 review-pr 这个高层工作流”,然后再通过 SkillTool 去真正加载和执行它。
换句话说,skill 在整轮上下文里扮演的,首先是“决策入口”,然后才是“执行内容”。这样再回头看前面的 surfacing、budget 和 SkillTool,三者之间的关系就会清楚很多:surfacing 负责让模型看见入口,SkillTool 负责把入口变成正式执行,而 skill 文件正文和 frontmatter 则定义执行后到底会发生什么。
为什么 skill 能改变 session 行为
skill 甚至还可以注册 hooks。
这意味着一个 skill 调用之后,可能既插入一段 prompt,也给当前 session 增加新的 hook 行为,改变后续轮次的执行方式,甚至在 compact / resume 之后仍保留影响。
这样再回看前面篇章中讲的 compact、恢复和上下文重建,就更容易明白 skill 为什么不能只按“模板”来理解:它进入系统后留下的影响,未必会随着当前 turn 结束而消失。
skill 系统真正解决的问题
Claude Code 把一部分高层任务逻辑沉淀成 skill,没有全部硬编码进主 system prompt。这样做的好处是,它更可组合、更可按需暴露,也更容易被用户和模型共同调用,还更适合携带特定工具、模型、努力级别与 hook 配置。
从系列前几篇建立的框架看,这一步的意义可以概括成一句话:Claude Code 没把所有高层能力都塞进 prompt,其中一部分被做成了可发现、可执行、可改写 runtime 的工作流单元。这正是 agent 系统走向工程化时很常见的一步。
源码锚点
建议重点看:
src/skills/loadSkillsDir.tssrc/commands.tssrc/tools/SkillTool/SkillTool.tssrc/tools/SkillTool/prompt.tssrc/utils/hooks/registerSkillHooks.ts
结语
如果你把 Tool 看成 Claude Code 的底层动作层,Skill 就是它的高层工作流层。
Claude Code 真正强的地方之一,就是它把能力从平铺的大 prompt 里拆了出来,做成一套可发现、可调用、可预算控制、可持续生效的 skill 系统。
如果按当前源码再补一个边界,那么更准确的说法应该是:skill 仍然是最重要的一层高层工作流抽象,但它已经不是唯一的高层 runtime 单元。AgentTool、内建 agent 和子 agent 调度,也在把 Claude Code 的能力层继续往多 agent / 多 runtime 的方向推进。把这一点看见之后,再回来看 skill,就更容易理解它为什么既像工作流,又像更大能力编排系统中的一个中间层。
skill 跟 tool 分工:skill 讨论的是“高层工作流如何定义、如何暴露、如何接入 runtime”,tool 讨论的则是“底层动作能力怎样被建模、授权、执行并回填结果”。两者都属于能力层,但粒度并不一样。

动手练习
打开 src/skills/loadSkillsDir.ts,搜索createSkillCommand,看返回的 Command 对象有哪些字段搜索 parseSkillFrontmatterFields,看 frontmatter 解析了哪些字段打开 src/tools/SkillTool/SkillTool.ts,搜索contextModifier,看 skill 是如何改写后续能力配置的
夜雨聆风