为纪念 35 周年:《Terminator 2: Judgment Day》将在全球影院重映
2026-07-13
2026-07-14 0
TL;DR: 每个 token 进上下文都意味着一个 token 不可用于实际任务。500 行的 Skill 全量加载可消耗 8000+ token。正确做法:SKILL.md 只放触发和路由,模板、脚本、参考资料按需读取。
Claude Code 的上下文窗口是有限资源。200K token 听起来很多,但一次典型的工程会话很快就会被占满:
上下文分配(典型工程会话):┌──────────────────────────────────────┐│CLAUDE.md指令~3000tokens││项目文件读取~15000tokens││工具调用历史~20000tokens││对话往返~10000tokens││───────────────────────────────────││已用~48000tokens││───────────────────────────────────││Skill全量加载(错误)~8000tokens│←这个不该占这么多│───────────────────────────────────││剩余可用~144000tokens│└──────────────────────────────────────┘
如果一个 Skill 在触发时加载 500 行内容(包含模板、示例、参考文档),它消耗的不仅是 token,还有模型的注意力带宽。上下文里的噪声越多,模型对实际任务的判断力越差。这在大语言模型的注意力机制层面有明确的解释:每个 token 都会参与自注意力计算,无关内容会稀释模型对关键信息的聚焦程度。实验数据表明,当上下文中有超过 30% 的内容与当前任务无关时,输出质量开始可测量地下降。
这和函数加载是同一个问题。没人会在程序启动时把所有库的源码都读进内存——按需加载才是工程实践。程序语言里有动态链接库、懒加载、代码分割,Skill 也需要同样的分层机制。渐进式披露就是 Skill 领域的懒加载策略:先给模型一个最小可用的入口,等它确认需要哪些具体资源时,再按需加载。
核心原则:只在需要时才加载需要的内容。
加载时机分层:第一层:始终加载(触发时)SKILL.md(≤80行)├──frontmatter(name,description,allowed-tools)├──
来自真实项目 gsd-sketch 的结构验证了这个分层。SKILL.md 只有 60 行,但它通过 指向了一个 360 行的 workflow 文件和五个 reference 文件(合计 421 行)。总计 841 行知识,但 SKILL.md 只暴露了 7%。这种设计的核心收益在于:当 Claude Code 在扫描阶段匹配 Skill 时,它只需要读取每个 Skill 的 frontmatter 和 description 做触发判断,不需要把全部内容都拉进上下文。只有当匹配成功并决定执行时,才会加载 execution_context 中声明的资源文件。
三层架构带来的另一个好处是可维护性。当团队需要更新某个参考文档时,只需要修改对应的资源文件,不影响 SKILL.md 的触发逻辑。当需要新增一个分支流程时,只需要添加一个新的 workflow 文件并在 execution_context 中声明,不需要修改已有的步骤定义。这种关注点分离让 Skill 的迭代更加安全,也更容易做代码审查。
.claude/skills/release-notes/├──SKILL.md#薄入口(≤80行)├──templates/│├──conventional.md#Conventionalcommit格式模板│└──semantic.md#Semanticversion格式模板├──scripts/│├──git-log-since.sh#提取上次tag以来的commit│└──categorize.sh#按committype分类└──examples/└──sample-output.md#示例changelog输出
再看一个来自真实 GSD 框架的实际结构。gsd-sketch 的资源分布在 skill 外部:
~/.claude/skills/gsd-sketch/└──SKILL.md#60行,薄入口~/.claude/get-shit-done/#资源目录(按需加载)├──workflows/│├──sketch.md#360行完整流程│└──sketch-wrap-up.md#wrap-up子流程├──references/│├──sketch-theme-system.md#94行主题规范│├──sketch-variant-patterns.md#81行变体模式│├──sketch-interactivity.md#41行交互定义│├──sketch-tooling.md#45行工具配置│└──ui-brand.md#160行品牌规范
SKILL.md 通过 声明依赖,Claude Code 在执行时才读取:
<execution_context>@$HOME/.claude/get-shit-done/workflows/sketch.md@$HOME/.claude/get-shit-done/workflows/sketch-wrap-up.md@$HOME/.claude/get-shit-done/references/ui-brand.md@$HOME/.claude/get-shit-done/references/sketch-theme-system.md@$HOME/.claude/get-shit-done/references/sketch-interactivity.md@$HOME/.claude/get-shit-done/references/sketch-tooling.md@$HOME/.claude/get-shit-done/references/sketch-variant-patterns.mdexecution_context>
注意 在 和 之前。Claude Code 在解析到这个标签时,会按需加载列出的文件——但只在实际执行该 Skill 时触发,而不是在扫描所有 Skill 的 description 做匹配时触发。这里有一个重要的性能细节:@ 前缀告诉 Claude Code 这是一个需要加载的文件路径,而不是一段需要内联的文本。如果把模板内容直接写在 SKILL.md 里(不用 @ 引用),那它每次都会被加载,不管是否需要。
资源目录的位置有两种选择。上面展示的是集中式布局,所有资源放在 skill 目录外的共享位置(如 ~/.claude/get-shit-done/),多个 Skill 可以复用同一份 reference。另一种是内嵌式布局,资源直接放在 skill 目录下的子目录中。选择标准很简单:如果这个资源只被一个 Skill 使用,内嵌;如果被多个 Skill 共享,集中管理。
以下是 gsd-debug 的实际 SKILL.md(53 行),展示了薄入口 + 委托的模式:
name:gsd-debugdescription:"Systematicdebuggingwithpersistentstateacrosscontextresets"argument-hint:"[list|status
<objective>Debugissuesusingscientificmethodwithsubagentisolation.**Orchestratorrole:**Gathersymptoms,spawngsd-debuggeragent,handlecheckpoints,spawncontinuations.**Flags:**-`--diagnose`—Diagnoseonly.ReturnsaRootCauseReportwithoutapplyingafix.**Subcommands:**`list`·`status<slug>`·`continue<slug>`objective><available_agent_types>ValidGSDsubagenttypes(useexactnames—donotfallbackto'general-purpose'):-gsd-debug-session-manager—managesdebugcheckpoint/continuationloop-gsd-debugger—investigatesbugsusingscientificmethodavailable_agent_types><execution_context>@$HOME/.claude/get-shit-done/workflows/debug.mdexecution_context><context>User'sinput:$ARGUMENTSParsesubcommandsandflagsfrom$ARGUMENTSBEFOREtheactive-sessioncheck:-If$ARGUMENTSstartswith"list":SUBCMD=list,nofurtherargs-If$ARGUMENTSstartswith"status":SUBCMD=status,SLUG=remainder-If$ARGUMENTSstartswith"continue":SUBCMD=continue,SLUG=remainder-If$ARGUMENTScontains`--diagnose`:SUBCMD=debug,diagnose_only=true-Otherwise:SUBCMD=debug,diagnose_only=falseCheckforactivesessions:```bashls.planning/debug/*.md2>/dev/null|grep-vresolved|head-5
Execute end-to-end. ```
关键设计决策:
|内容类型|SKILL.md|Resources/|原因|| --- | --- | --- | --- ||触发规则(description)| 必须 ||每次输入都需匹配,必须快速加载||步骤概要(5-8 步)| 推荐 ||需要加载才能路由到正确分支||参数解析逻辑| 推荐 ||决定分支走向,必须在路由层||allowed-tools 清单| 必须 ||权限控制在触发时就生效||输出模板|| 必须 |只在格式化输出时需要||参考文档|| 必须 |只在需要领域知识时加载||脚本|| 必须 |只在自动化执行时调用||示例文件|| 必须 |只在需要澄清格式时参考||验证检查清单| 推荐 |看长度|短清单放 SKILL.md,长的放资源||错误处理流程|| 推荐 |只在失败时需要||子袋里类型声明| 推荐 ||影响路由决策|
判断原则:
问自己一个问题:"这段内容在决定是否触发这个Skill时需要吗?"需要→SKILL.md不需要→Resources/再问第二个问题:"这段内容在每次执行时都需要吗?"每次都要→SKILL.md或workflow文件特定场景才要→references/或templates/
SKILL.md 支持一组动态变量,在运行时替换为实际值。这些变量是实现薄入口的关键——它们让 SKILL.md 不需要硬编码项目特定信息。没有动态变量的 SKILL.md 要么只能在一个项目中使用,要么需要把所有可能的路径都列出来,两种做法都会让文件膨胀。动态变量让同一个 Skill 在不同项目、不同用户环境中无修改地复用。
用户调用 Skill 时传入的参数。这是最核心的变量,也是 SKILL.md 实现薄入口的基础。没有参数解析,SKILL.md 就需要为每种使用场景写一套独立的步骤定义,文件会迅速膨胀。通过在 中声明参数解析规则,Claude Code 在执行时根据实际参数决定走哪条分支,然后只加载该分支需要的资源。
#用户调用/gsd:sketchdashboardlayout--quick#SKILL.md中解析$ARGUMENTS#$ARGUMENTS="dashboardlayout--quick"#解析逻辑(放在
$HOME#用户主目录→/Users/mac08$PROJECT_ROOT#当前项目根目录(由ClaudeCode自动解析)
在 中使用:
<execution_context>@$HOME/.claude/get-shit-done/workflows/debug.mdexecution_context>
通过 Bash 命令在运行时查询:
#查询项目配置COMMIT_DOCS=$(gsd-sdkqueryconfig-getcommit_docs2>/dev/null||echo"true")#查询git状态CURRENT_BRANCH=$(gitbranch--show-current)#查询项目类型HAS_PACKAGE_JSON=$(test-fpackage.json&&echo"true"||echo"false")
这些查询放在 或 workflow 的初始化步骤中,确保运行时拿到的是真实值而非假设。环境变量的另一个重要用途是做条件加载:根据查询结果决定是否读取某个资源文件。比如一个 Skill 可能只在 monorepo 项目中才需要加载特定的构建脚本参考,运行时通过检查项目结构来决定是否加载,而不是把所有场景的参考文档都预先塞进上下文。
#错误:硬编码路径,不可复用templates:-/Users/mac08/my-project/templates/changelog.md#正确:使用变量,可跨项目复用execution_context:-@$HOME/.claude/get-shit-done/references/changelog-template.md#正确:运行时动态发现context:|GLOBfor.claude/skills/spike-findings-*/SKILL.mdandreadanythatexist,plustheirreferences/*.md
$ARGUMENTS 不仅是"接收用户输入"这么简单。在工程实践中,它是实现条件资源加载的核心路由机制。以下是两个来自真实项目的完整模式。
模式一:条件资源加载
当 Skill 支持多种输出格式时,$ARGUMENTS 用于在运行时决定加载哪个模板,而不是把所有模板都预先塞进上下文。wos-format 是一个格式转换 Skill,支持 Markdown、HTML、社交平台等多种输出目标:
<context>$ARGUMENTS:thecontenttoformatandformatflagsParseformattargetfrom$ARGUMENTS:-If$ARGUMENTScontains"--html"→FORMAT=html-If$ARGUMENTScontains"--social"→FORMAT=social-If$ARGUMENTScontains"--slide"→FORMAT=slide-Otherwise→FORMAT=markdown(default)ThenloadONLYthematchingtemplate:-FORMAT=html→Read$HOME/.claude/writing-os/templates/html-output.md-FORMAT=social→Read$HOME/.claude/writing-os/templates/social-post.md-FORMAT=slide→Read$HOME/.claude/writing-os/templates/slide-deck.md-FORMAT=markdown→Read$HOME/.claude/writing-os/templates/markdown-polish.mdcontext><process>1.Parse$ARGUMENTStodetermineformattarget2.ReadONLYthematchingtemplatefile3.Applyformattingrulesfromtemplate4.Outputformattedcontentprocess>
用户调用 /wos:format --html my-article.md,SKILL.md 只加载 HTML 模板(约 400 tokens),而不是把四种模板全部加载(合计约 1800 tokens)。每次调用节省约 1400 tokens,按日均 15 次调用计算,每天节省 21,000 tokens 的上下文预算。
模式二:子命令路由与状态管理
gsd-debug 展示了更复杂的 $ARGUMENTS 模式——子命令路由结合持久状态。用户可以用同一个 Skill 查看调试会话列表、检查特定会话状态、继续中断的调试,或启动新的调试流程。每种操作需要的资源完全不同:
<context>$ARGUMENTS:user'sdebuginputParsesubcommandfrom$ARGUMENTS:-$ARGUMENTSstartswith"list"→SUBCMD=list→Onlyneed:`ls.planning/debug/*.md2>/dev/null`→DoNOTloaddebugworkflow-$ARGUMENTSstartswith"status"→SUBCMD=status,SLUG=remainingtext→Onlyneed:Read`.planning/debug/{SLUG}.md`→DoNOTloaddebugworkflow-$ARGUMENTSstartswith"continue"→SUBCMD=continue,SLUG=remainingtext→Loadworkflow:$HOME/.claude/get-shit-done/workflows/debug.md→Readcheckpoint:`.planning/debug/{SLUG}.md`→Resumefromlastcheckpointstep-$ARGUMENTScontains"--diagnose"→SUBCMD=debug,DIAGNOSE_ONLY=true→Loadworkflow:$HOME/.claude/get-shit-done/workflows/debug.md-Otherwise→SUBCMD=debug,DIAGNOSE_ONLY=false→Loadworkflow:$HOME/.claude/get-shit-done/workflows/debug.md→Checkforactivesessionsbeforestartingnewonecontext>
list 和 status 子命令根本不加载 231 行的 debug workflow 文件。一次 list 操作的上下文成本只有约 900 tokens(SKILL.md 本身),而全量加载需要约 4600 tokens。这意味着查看调试进度的轻量操作不会浪费 3700 tokens 去加载它不需要的调试流程定义。$ARGUMENTS 在这里的本质作用是充当路由层——它让一个 SKILL.md 入口能够根据用户意图,精确控制下游资源的加载范围。
当一个 Skill 需要调用其他 Skill 时,组合模式让每个 Skill 保持薄入口,同时通过委托完成复杂工作。这是渐进式披露在架构层面的延伸:不仅文件内容按需加载,整个 Skill 的职责也是按需激活的。编排 Skill 不需要知道被编排 Skill 的内部实现,它只需要知道名字和参数接口。这和微服务架构的服务发现是同一个思路——调用方只关心接口契约,不关心实现细节。
gsd-autonomousgsd-autonomous 的工作是串联执行多个阶段,每个阶段本身是一个独立的 Skill:
gsd-autonomous(46行SKILL.md)│├─→gsd-discuss-phase(通过Skill()调用)│└─→workflow:discuss-phase.md│├─→gsd-plan-phase(通过Skill()调用)│└─→workflow:plan-phase.md│├─→gsd-execute-phase(通过Skill()调用)│└─→workflow:execute-phase.md│└─→gsd-complete-milestone(通过Skill()调用)└─→workflow:complete-milestone.md
gsd-autonomous 的 SKILL.md 不包含任何阶段的执行细节。它只做两件事:
<objective>Executeallremainingmilestonephasesautonomously.Foreachphase:discuss→plan→execute.Pausesonlyforuserdecisions.objective><execution_context>@$HOME/.claude/get-shit-done/workflows/autonomous.md@$HOME/.claude/get-shit-done/references/ui-brand.mdexecution_context><process>Executeend-to-end.Preserveallworkflowgates(phasediscovery,per-phaseexecution,blockerhandling,progressdisplay).process>
789 行的 autonomous.md workflow 文件在执行时才加载,里面包含完整的阶段发现、循环执行、阻塞处理逻辑。但 SKILL.md 对此一无所知——也不需要知道。
gsd-new-milestone 展示了一个编排 Skill 如何调用多个子 Skill,每个子 Skill 各自拥有独立的资源文件和 workflow。这种模式下,父 Skill 只声明编排顺序,子 Skill 各自管理自己的渐进式披露。
gsd-new-milestone(SKILL.md52行)│├─→gsd-discuss-phase│SKILL.md:55行→workflow:discuss-phase.md(289行)│├─→gsd-spec-phase│SKILL.md:48行→workflow:spec-phase.md(342行)│├─→gsd-plan-phase│SKILL.md:61行→workflow:plan-phase.md(410行)│├─→gsd-ui-phase(仅在UI相关里程碑时激活)│SKILL.md:57行→workflow:ui-phase.md(198行)│└─→gsd-secure-phase(仅在安全审查里程碑时激活)SKILL.md:53行→workflow:secure-phase.md(176行)
父 Skill 的 SKILL.md 配置:
name:gsd-new-milestonedescription:"Createanewmilestonethroughdiscuss→spec→planphases"argument-hint:"[milestonenameordescription]"allowed-tools:-Read-Write-Bash-Agent-AskUserQuestion
<objective>Scaffoldanewmilestone.Orchestratesdiscuss→spec→planthroughsequentialSkill()calls.Eachphaseproducesadeliverablestoredin.planning/milestones/.objective><execution_context>@$HOME/.claude/get-shit-done/workflows/new-milestone.mdexecution_context><context>$ARGUMENTS:milestonedescriptionornamePhaseactivationrules(parsedfrommilestonescope):-Ifscopeincludes"UI"or"frontend"→activategsd-ui-phase-Ifscopeincludes"auth"or"security"→activategsd-secure-phase-discuss→spec→planalwaysruns(corepipeline)context><process>Executeend-to-end.process>
new-milestone.md workflow 文件中的编排逻辑:
##PhaseOrchestration1.Run`Skill("gsd-discuss-phase",milestone_description)`-Output:`.planning/milestones/{slug}/discuss.md`2.Run`Skill("gsd-spec-phase",slug)`-Reads:discuss.md-Output:`.planning/milestones/{slug}/spec.md`3.Run`Skill("gsd-plan-phase",slug)`-Reads:spec.md-Output:`.planning/milestones/{slug}/plan.md`4.Conditionalphases(checkscopetagsindiscuss.md):-If"ui"inscope→Run`Skill("gsd-ui-phase",slug)`-If"security"inscope→Run`Skill("gsd-secure-phase",slug)`5.Displaymilestonesummary
每个子 Skill 通过 Skill() 被调用时,Claude Code 会独立处理该 Skill 的触发和资源加载。这意味着父 Skill 的上下文中不会包含任何子 Skill 的 workflow 内容——直到对应阶段实际执行。gsd-ui-phase 的 198 行 workflow 只在里程碑涉及 UI 工作时才被加载,gsd-secure-phase 的 176 行只在涉及安全审查时加载。如果没有条件激活的逻辑,每次里程碑创建都会无谓加载 374 行不相关内容。这就是组合模式与渐进式披露的协同效应:组合在架构层面隔离职责,渐进式披露在资源层面按需加载。
不使用组合(单体Skill):release-processSKILL.md=600行≈9600tokens(每次触发都加载)使用组合:release-processSKILL.md=70行≈1120tokens(触发时加载)├──release-notesSKILL.md=60行≈960tokens(按需加载)├──version-bumpSKILL.md=45行≈720tokens(按需加载)└──changelog-genSKILL.md=55行≈880tokens(按需加载)触发时只消耗:1120tokens执行时按需加载:960+720+880=2560tokens(分散在不同阶段)
一个团队构建了 pr-review Skill,用于自动化 PR 审查。这个团队的背景是:他们在 Code Review 上花了大量时间,希望用 Skill 让 Claude Code 自动做第一轮审查。他们的思路是"把所有审查知识都放进 SKILL.md,这样 Claude Code 就拥有完整的审查能力"。这个思路本身没错,但执行方式有问题——他们把所有内容塞进一个 SKILL.md:
---name:pr-reviewdescription:"Reviewpullrequestdiffsforcorrectness,security,andstyle"---#PRReview##UseWhen[触发规则:12行]##DoNotUseWhen[排除规则:8行]##ReviewSteps[详细步骤:65行]##SecurityChecklist[安全检查清单:80行]##PerformanceChecklist[性能检查清单:55行]##CodeStyleReference[团队代码风格参考:45行]##OutputTemplate[输出格式模板:30行]##ExampleGoodReview[好的审查示例:45行]##ExampleBadReview[差的审查示例:35行]##SeverityClassification[严重级别分类:32行]
每次用户输入任何内容,Claude Code 扫描所有 Skill 的触发匹配时,都会把这个 407 行文件完整加载进上下文。
Token成本分析:407行×~16tokens/行≈6512tokens每次触发消耗:6512tokens(占200K窗口的3.3%)每日20次触发(含误触发)总消耗:130,240tokens
更严重的是质量问题。团队发现 Claude Code 的审查输出变差了。具体表现为三个层面:
第一,格式套用。输出倾向于套用 SKILL.md 中的模板格式,而不是针对具体 diff 做分析。一个只改了两行配置的 PR,审查输出却包含了完整的安全检查清单结果,大部分条目写着"不适用"。这不是模型在"做安全审查",而是模型在"复述 SKILL.md 里的清单"。
第二,关键信号被稀释。对简单 diff 也执行完整的安全检查清单,输出大量不相关的检查结果。当一个 PR 确实引入了 SQL 注入风险时,这个发现被淹没在 20 多条"不适用"的安全检查项中。模型在长上下文中对真正重要的信号(安全漏洞)和噪声(格式模板)的区分能力下降了。
第三,创意消失。模型不再根据 PR 的实际内容做针对性分析,而是机械地走一遍所有预定义的检查步骤。团队原本期望的是"一个有经验的审查者",得到的是"一个检查清单执行器"。
所有内容放在一个文件里,没有分层。触发时不需要的安全检查清单、代码风格参考、示例文件,全部在第一时间加载进上下文。模型没有能力区分哪些内容是当前任务需要的。
拆分为 80 行 SKILL.md + 资源目录:
.claude/skills/pr-review/├──SKILL.md#80行(从407行缩减)├──checklists/│├──security.md#80行安全检查清单│└──performance.md#55行性能检查清单├──references/│└──code-style.md#45行代码风格参考├──templates/│└──review-output.md#30行输出模板└──examples/├──good-review.md#45行好的审查示例└──bad-review.md#35行差的审查示例
#修复后的SKILL.md(80行)name:pr-reviewdescription:"Reviewpullrequestdiffsforcorrectness,security,andstyle"allowed-tools:-Read-Bash-Glob-Grep
<objective>Reviewapullrequestdiffforcorrectness,tests,security,andmaintainability.Loadchecklistsandtemplatesbasedonwhatthediffactuallychanges.objective><execution_context>@$HOME/.claude/skills/pr-review/checklists/security.md@$HOME/.claude/skills/pr-review/templates/review-output.mdexecution_context><context>$ARGUMENTS:user'sreviewrequestConditionalloadingbasedondiffcontent:-Ifdifftouchesauth/crypto/inputhandling→loadsecurity.md-Ifdifftoucheshotpaths/loops/queries→loadperformance.md-Alwaysloadreview-output.mdforformattingcontext><process>1.Readthecurrentdiff2.Classifychangedfiles(security-sensitive,perf-sensitive,style-only)3.Loadonlyrelevantchecklists4.Executereview5.Formatoutputpertemplateprocess>
修复后的效果在三个维度上都有提升。首先是直接的 token 经济性:触发时的固定开销从 6512 降到 1280 tokens,误触发的代价降低了 80%。其次是输出质量的回归:模型不再被无关模板干扰,能够把注意力集中在实际的 diff 内容上,安全漏洞的检出率恢复到预期水平。最后是可维护性:当团队需要更新安全检查清单时,只需要修改 checklists/security.md,不影响触发逻辑和其他模板。
修复前:6512tokens/次触发(含所有内容)修复后:1280tokens/次触发(仅SKILL.md)+按需加载800-1300tokens(取决于diff类型)Token节省:80%+输出质量:模型注意力集中在实际diff上,不再被无关模板分散误触发代价:从6512降到1280tokens
以下是真实 Skill 配置在 Claude Code 中的 token 消耗测量。数据通过以下方法获得:在 Claude Code 中逐个加载文件,记录每次 Read 操作后的上下文增长量,取三次平均值。测量环境为 Claude Sonnet 4,中文和英文混合内容。
|配置项|薄入口(渐进式披露)|厚入口(全量加载)|差异|| --- | --- | --- | --- || gsd-debug SKILL.md |896 tokens(53 行)|—|—||debug.md workflow|按需加载 3,696 tokens(231 行)|触发时加载 3,696 tokens|节省 3,696/次误触发|| gsd-sketch SKILL.md |1,024 tokens(60 行)|—|—||sketch.md workflow|按需加载 5,760 tokens(360 行)|触发时加载 5,760 tokens|节省 5,760/次误触发||5 个 reference 文件|按需加载 6,720 tokens(421 行合计)|触发时加载 6,720 tokens|节省 6,720/次误触发|| 触发时总计(gsd-sketch) | 1,024 tokens | 13,504 tokens | 92% 节省 |
一个典型的高级用户配置了 20 个 Skill。每次用户输入,Claude Code 需要扫描所有 Skill 的 frontmatter 做触发匹配。以下是两种配置策略的总成本:
|策略|每次输入的扫描成本|日均 30 次输入总成本|占 200K 窗口比例|| --- | --- | --- | --- ||全部薄入口(平均 70 行/Skill)|22,400 tokens|672,000 tokens(循环复用)|11.2%||全部厚入口(平均 350 行/Skill)|112,000 tokens|3,360,000 tokens(循环复用)|56.0%||混合(10 薄 + 10 厚)|67,200 tokens|2,016,000 tokens(循环复用)|33.6%|
注:"循环复用"表示上下文在对话过程中被反复消耗和重建。厚入口配置意味着每次对话轮次都有更高的基础成本,导致更快触及上下文窗口上限,触发更频繁的上下文压缩或重建。
以 gsd-autonomous 执行一次完整的里程碑流程为例,展示渐进式披露下资源的分阶段加载:
时间线(token消耗):T0触发扫描:加载gsd-autonomousSKILL.md(52行)→消耗832tokensT1执行开始:加载autonomous.mdworkflow(789行)→消耗12,624tokens→此时总计:13,456tokensT2调用gsd-discuss-phase:加载discussSKILL.md(55行)→消耗880tokens→加载discuss-phase.mdworkflow(289行)→消耗4,624tokens→此时总计:18,960tokensT3调用gsd-spec-phase:加载specSKILL.md(48行)→消耗768tokens→加载spec-phase.mdworkflow(342行)→消耗5,472tokens→此时总计:25,200tokensT4调用gsd-plan-phase:加载planSKILL.md(61行)→消耗976tokens→加载plan-phase.mdworkflow(410行)→消耗6,560tokens→此时总计:32,736tokens对比:如果所有内容在T0全量加载→一次性消耗:32,736tokens→上下文中大量内容在T0-T1阶段完全无用→模型在T0-T1阶段的注意力被24,000+tokens的无关内容稀释
这个时间线揭示了一个关键洞察:渐进式披露不仅节省总 token 数量,更重要的是它让模型的注意力在每个阶段都集中在当前需要的资源上。在 T2 阶段,模型只需要关注 discuss 相关的 5,504 tokens,而不是被后续阶段的 13,776 tokens 干扰。这种注意力聚焦效应是渐进式披露对输出质量产生正面影响的根本原因。
前文展示了 execution_context 的静态声明模式。实际工程中还有其他几种加载模式,各有适用场景。
通过 在 SKILL.md 中列出需要加载的文件。Claude Code 在执行 Skill 时自动读取。
<execution_context>@$HOME/.claude/skills/pr-review/checklists/security.md@$HOME/.claude/skills/pr-review/templates/review-output.mdexecution_context>
适用场景:资源文件在每次执行时都需要加载。
在 中根据 $ARGUMENTS 决定加载哪些资源。通过 Read 工具在运行时读取。
<context>$ARGUMENTS:user'sreviewrequestParse$ARGUMENTSforflags:-Ifcontains"--security"ordifftouchesauth/crypto:Read@$HOME/.claude/skills/pr-review/checklists/security.md-Ifcontains"--performance"ordifftoucheshotpaths:Read@$HOME/.claude/skills/pr-review/checklists/performance.md-Ifcontains"--full":ReadALLchecklistsandreferences-Otherwise:onlyloadreview-output.mdtemplatecontext>
适用场景:资源文件较大且不是每次执行都需要。条件加载把控制权交给运行时的上下文判断。
通过 Bash 或 Glob 工具在运行时搜索可用的资源文件。
<process>1.Discoveravailablereferencefiles:Globfor.claude/skills/pr-review/references/*.md2.Basedondiffcontent,selectrelevantreferences3.Readonlytheselectedfiles4.Executereviewprocess>
适用场景:资源文件会动态增减(比如团队成员可以自行添加 checklist 文件),SKILL.md 不硬编码文件列表。
编排 Skill 通过 Skill() 调用另一个 Skill,后者自动加载自己的资源。
<process>1.Parse$ARGUMENTSforphaserange2.CallSkill("gsd-discuss-phase")→loadsdiscussworkflow3.CallSkill("gsd-plan-phase")→loadsplanworkflow4.CallSkill("gsd-execute-phase")→loadsexecuteworkflowprocess>
适用场景:复杂流程需要多阶段执行,每个阶段是独立的 Skill。资源加载由子 Skill 自行管理。
在多步骤流程中,每一步只加载当前步骤需要的资源。
<process>Step1(diagnosis):Readsourcefilesonly→Output:listofsuspiciouspatternsStep2(deepanalysis,onlyifuserconfirms):Read@$HOME/.claude/skills/debug/references/common-antipatterns.md→Output:detailedanalysiswithpatternmatchingStep3(fixproposal,onlyifuserconfirms):Read@$HOME/.claude/skills/debug/templates/fix-proposal.md→Output:structuredfixproposalprocess>
适用场景:多步骤流程,后续步骤依赖前一步的输出和用户确认。每一步的加载是前一步结果的函数。
模式│加载时机│适用场景│复杂度─────────────┼─────────────┼────────────────────────────┼────────静态声明│执行时全量│资源总量小(<2000token)│低条件加载│执行时按需│资源总量大,使用场景可枚举│中动态发现│运行时搜索│资源文件会动态变化│中组合委托│子Skill执行│多阶段独立流程│高渐进深入│每步按需│多步骤,后续依赖前面结果│高
$ARGUMENTS 是 SKILL.md 最强大的动态变量。前文展示了基本解析。以下是更复杂的使用模式。
$ARGUMENTS 可以包含子命令,实现一个 Skill 多种行为:
<context>$ARGUMENTS:user'sinputParsesubcommandsfrom$ARGUMENTS:-Ifstartswith"list"→SUBCMD=list→Listalldebugsessions→Noadditionalresourcesneeded-Ifstartswith"status"→SUBCMD=status,SLUG=remainingtext→Read.planning/debug/{SLUG}.md→Showsessionstatus-Ifstartswith"continue"→SUBCMD=continue,SLUG=remainingtext→Read.planning/debug/{SLUG}.md→Resumefromlastcheckpoint-Ifcontains"--diagnose"→SUBCMD=debug,diagnose_only=true→Loadworkflow,executediagnosisonly-Otherwise→SUBCMD=debug,diagnose_only=false→Loadworkflow,executefulldebugcyclecontext>
这种模式让一个 53 行的 SKILL.md 替代了 5 个独立的 Skill。没有子命令路由,你需要 gsd-debug-start、gsd-debug-list、gsd-debug-status、gsd-debug-continue、gsd-debug-diagnose 五个入口文件,每个都需要完整的 frontmatter 和 description。
<context>$ARGUMENTS:user'sinputParseandvalidate:-TARGET=firstnon-flagargument(required)IfTARGETisempty→Askuser:"Whatwouldyouliketodebug?"-SEVERITY=extract--severity<level>,default="medium"-MODE=extract--mode
参数验证的目的是防止 Claude Code 执行不合理的操作。比如 MAX_FILES=50 会导致一次性读取 50 个文件,消耗大量 token。在 中做上限约束,比在 workflow 中处理异常更高效。
<context>$ARGUMENTS:user'sinputResourceloadingbasedonparametercombination:-MODE=quick→Loadonlyquick-reference.md(30lines)-MODE=full→Loadfull-workflow.md(360lines)+allreferences-SEVERITY=criticalANDMODE=full→Loadincident-response.mdadditionally-TARGETcontains"production"→Loadprod-safety-checklist.mdLoadorder:1.Alwaysloadworkflowfirst(routedecisionneedsit)2.Loadreferencesbasedonparameters3.Loadchecklistsbasedonseverity/targetcontext>
这种组合让同一个 Skill 在不同参数下加载不同量的资源。quick 模式只消耗约 500 token,full 模式可能消耗约 5000 token。如果把这些全部内联到一个 SKILL.md 里,每次都要承担约 5000 token 的开销。
前文展示了 gsd-autonomous 的顺序组合。以下是更复杂的组合模式。
一个 Skill 的输出作为下一个 Skill 的输入。编排 Skill 管理数据传递:
release-pipelineSkill:Step1:Callrelease-notesSkill→outputschangelogdraftStep2:Callversion-bumpSkill→outputsnewversionnumberStep3:Callchangelog-genSkill→outputsformattedCHANGELOG.mdupdateStep4:Callgit-commitSkill→commitsallchanges数据传递:编排Skill在context中保存每步输出,下一步通过$ARGUMENTS或context变量接收。
链式组合的约束:每一步必须是幂等的——如果某一步失败,重新执行该步骤不应依赖前一步的中间状态。编排 Skill 应该在每一步完成后保存检查点,这样重试时只需从失败的步骤开始。
根据条件决定调用哪个子 Skill:
<process>1.Analyze$ARGUMENTStodetermineissuetype2.Ifissuetype="bug":CallSkill("gsd-debug",arguments="--diagnose"+description)3.Ifissuetype="feature":CallSkill("gsd-sketch",arguments=description)4.Ifissuetype="research":CallSkill("gsd-spike",arguments=description)process>
条件组合的约束:分支逻辑必须在 SKILL.md 的 或 中明确声明,不能依赖模型在运行时的自由判断。如果分支条件模糊,模型可能在不同时间走不同的分支,导致不可预测的行为。
Skill 在特定条件下可以重新调用自身(通过 context 状态管理防止无限循环):
<context>Checkforactivesessions:```bashls.planning/debug/*.md2>/dev/null|grep-vresolved|head-5
If active session exists for same issue:→ Resume that session (read checkpoint)→ Do NOT create a new session
递归组合的安全机制:必须有明确的状态检查防止无限递归。上例中通过文件系统状态(`.planning/debug/*.md`的存在性)做幂等检查。如果检查失败或状态文件损坏,应该创建新会话而非进入无限循环。##薄入口vs厚入口的决策标准前文强调SKILL.md应该保持薄(不超过80行)。但也有例外场景,厚入口是更好的选择。###适合薄入口的场景```text-Skill有完整的workflow文件(超过200行详细步骤)-Skill有多个子命令或分支路径-Skill使用动态资源(根据参数/运行时条件加载不同文件)-Skill被编排模式调用(只需要入口+路由)-团队有10+个Skill(扫描开销是实际瓶颈)
-Skill总内容不足100行(拆分反而增加文件管理复杂度)-Skill没有条件分支(每次执行路径完全相同)-Skill不使用外部资源(所有逻辑都在SKILL.md内)-团队只有2-3个Skill(扫描开销不是瓶颈)
name:format-markdowndescription:"Formatmarkdownfilestoteamstandards"allowed-tools:-Read-Write-Bash
这个 Skill 只有 35 行,全部内容都在 SKILL.md 内。没有 workflow 文件、没有 templates、没有 references。把它拆分成三层架构是过度工程化——多出 3 个文件和目录,但没有任何收益。
总内容量│子命令数│条件分支│推荐入口类型─────────────┼─────────┼─────────┼─────────────不足80行│无│无│厚入口(单文件即可)80-150行│无│有│薄入口+单个workflow150-300行│有│有│薄入口+workflow+references超过300行│有│有│薄入口+workflow+多个references+templates
渐进式披露检查清单:SKILL.md大小□SKILL.md≤80行?□触发匹配所需的信息都在frontmatter和
• 09-SKILL.md 结构:本文的薄入口依赖 09 中定义的 SKILL.md 结构(frontmatter、objective、process)。渐进式披露是那个结构的成本优化策略。
• 11-Skill 评测:拆分后必须验证三件事没退化——触发准确率、执行质量、输出一致性。用 11 的最小评测集跑回归。
• 08-升级决策:渐进式披露是 Skill 的一种升级模式。如果你的 Command 已经在模拟按需加载(手动 Read 模板文件),说明它已经准备好变成 Skill 了。
拆分增加文件数量和目录复杂度。团队需要约定资源目录的命名和位置规范,新成员上手时需要理解分层逻辑而不是只看一个文件。这是真实的工程成本。
但对于任何超过 80 行的 SKILL.md,渐进式披露的收益是确定的:更低的 token 成本、更高的输出质量、更灵活的组合能力。这些收益会随着 Skill 数量的增长而放大——当你有 20 个 Skill 时,每个节省 5000 tokens 意味着每次触发扫描节省 100K tokens 的无效加载。
判断阈值:SKILL.md 超过 100 行时,拆分不再是一个优化选项,而是工程纪律。超过 200 行时,说明你可能在 SKILL.md 里放了应该在 workflow 或 reference 中的内容。超过 300 行时,输出质量已经在退化,你只是还没注意到。