首页 › tutorials › Skill渐进披露:三层按需加载实战

Skill 渐进式披露:三层按需加载实战

学习目标:掌握 Agent Skills 的"发现—激活—执行"三层架构,学会把一个臃肿的 Skill 重构为按需加载的分层结构,让 17 个 Skill 的发现总成本压到 ~1700 tokens,激活成本控制在 5000 tokens 以内。 适用人群:已经写过第一个 Skill、想让 Skill 在大规模(10+ 技能)环境下稳定高效运行的开发者。

前置条件

一、为什么不能把所有内容塞进一个 SKILL.md

很多开发者写第一个 Skill 时会犯同一个错误:把所有规则、所有示例、所有脚本说明,一股脑写进 SKILL.md,文件膨胀到 8000 tokens。表面上看"内容全在一处很方便",实际却带来两个严重问题:

问题 1:上下文不是免费的。 大语言模型存在"lost in the middle"现象——上下文越长,模型对中间位置信息的提取准确率越低。研究表明,agent 同时挂载的工具一旦超过 10 个,准确率就开始下降;超过 20 个则明显劣化。指令和工具定义遵循同样的规律。当你挂了几个 MCP 服务器,工具的 JSON Schema 很容易就堆到 50000 tokens,模型还没开始推理,注意力已经被稀释。

问题 2:激活成本暴涨。 Skill 一旦被判定为相关,整个 SKILL.md 会被加载进上下文。一个 8000 tokens 的 Skill 每次激活就吃掉这么多窗口;如果你有 20 个这样的 Skill,热点任务反复激活时,窗口被无效内容反复挤压。

Anthropic 官方仓库里 skill-creator 的 SKILL.md 确实达到约 8000 tokens,但那是少数——官方 17 个 Skill 的 SKILL.md body 中位数只有约 2000 tokens,最小的 internal-comms 仅 275 tokens。差距的全部秘密,就在于渐进式披露(Progressive Disclosure)。

二、三层架构:发现、激活、执行

渐进式披露是一个源自人机交互(Nielsen Norman Group)的经典设计原则:只展示当前任务需要的信息,把进阶选项藏到二级入口。Agent Skills 把这个原则应用到了上下文管理上,形成三层加载模型:

层级 触发时机 加载内容 典型成本
Layer 1 发现 启动时,常驻 仅 name + description ~80 tokens/skill
Layer 2 激活 判定任务相关 完整 SKILL.md body < 5000 tokens 推荐
Layer 3 执行 执行中按需 scripts/、references/、assets/ 仅当前所需

对 Anthropic 官方 17 个 Skill 的实测显示:发现层成本从 55 tokens(webapp-testing)到 235 tokens(xlsx)不等,全部 17 个加起来约 1700 tokens——让 agent "意识到"几十个技能存在,成本比激活单个技能还低。这正是分层设计的威力。

flowchart TD
    A[Agent 启动] -->|扫描所有 Skill| B["Layer 1 发现<br/>仅 name+description<br/>~80 tokens/skill"]
    B --> C{description 与<br/>当前任务相关?}
    C -->|否| D[保持休眠<br/>零额外开销]
    C -->|是| E["Layer 2 激活<br/>加载完整 SKILL.md<br/>建议 < 5000 tokens"]
    E --> F[按指令执行]
    F --> G{遇到需要<br/>细节的步骤?}
    G -->|否| I[直接完成]
    G -->|是| H["Layer 3 执行<br/>按需读 references/scripts/assets"]
    H --> I
    I --> J[完成后缓存或卸载]

关键洞察:路由准确性几乎完全取决于 description 的质量。模型在 Layer 1 用纯推理判断"这个 Skill 该不该激活",写好 description 就等于写好路由表。这也是为什么 name 和 description 是 frontmatter 里仅有的两个必填字段。

三、分步实战:从臃肿到分层

下面我们以一个"API 文档生成器"为案例,演示完整的三层重构过程。先看反例——一个把所有东西塞进 SKILL.md 的臃肿版本:

---
name: api-doc-generator
description: Helps with API docs.
---

# API 文档生成器(臃肿版)

(此处省略 8000 tokens 的 OpenAPI 字段规范、
各框架解析规则、20 个示例、报错排查……全部堆在这一个文件里)

这个版本有三大病灶:description 模糊("Helps with API docs" 缺少触发关键词)、body 臃肿(每次激活浪费数千 token)、没有执行层(细节和脚本混在一起)。我们逐层修复。

第一步:精修 frontmatter(优化 Layer 1 发现)

description 必须同时回答两个问题——做什么和何时用,并埋入用户真实会说的关键词。规范要求 1–1024 字符。

---
name: api-doc-generator
description: 为 Express/FastAPI/Fastify/Spring Boot 项目自动生成 OpenAPI 3.x 接口文档。当用户说"生成API文档""导出OpenAPI/Swagger""列出所有端点""从路由代码提取接口"时使用;也可用于把现有路由转成可交互的 API 参考。
metadata:
  author: bootcamp-demo
  version: "1.0"
---

frontmatter 字段速查(基于 Agent Skills 开放规范):

字段 必填 约束
name 是 ≤64 字符;仅小写字母/数字/连字符;不得以连字符开头结尾、不得连续连字符;必须与父目录名一致
description 是 1–1024 字符;同时说明做什么+何时用
license 否 许可证名或指向 LICENSE 文件
compatibility 否 ≤500 字符;环境要求(系统包、网络等)
metadata 否 任意 string→string 映射
allowed-tools 否 空格分隔的预批准工具(实验性)

name 字段有几个易错点:PDF-Processing(大写不允许)、-pdf(连字符开头)、pdf--processing(连续连字符)都会验证失败。

常见问题:description 写了"帮助处理 API",结果 Skill 死活不触发。 解决:用 /help 或 skills-ref 观察实际加载的 metadata,把用户真实措辞("导出接口文档""Swagger")作为关键词写进 description。

第二步:把 SKILL.md body 压到决策骨架(优化 Layer 2 激活)

激活层只放"决策骨架"——分支判断、高层流程、指针。目标是把 body 控制在 500 行以内、5000 tokens 以内:

# API 文档生成器

## 决策流程
1. 识别项目框架(检查 package.json / requirements.txt / pom.xml)
2. 按下表选择解析脚本
3. 解析后套用输出模板,产出 OpenAPI 结构

## 框架 → 脚本映射
| 框架 | 解析脚本 |
|------|---------|
| Express / Koa | `scripts/parse_js_routes.py` |
| FastAPI | `scripts/parse_fastapi.py` |
| Spring Boot | `scripts/parse_spring.py` |

## 何时需要深入细节
- OpenAPI 字段写法不确定 → 读 `references/openapi-spec.md`
- 输出格式与模板 → 读 `references/output-templates.md`
- 完整示例 → 读 `references/example-output.md`

> 除非当前任务明确需要,**不要预读** references/ 下的文件。

这个 body 约 250 tokens,相比 8000 tokens 的臃肿版,激活成本骤降 96%。骨架里最关键的一句是最后那行防预读指令——明确告诉模型"没需要就别加载",避免它好心把所有 reference 全读进来,前功尽弃。

第三步:把细节拆进 references/(构建 Layer 3 执行层)

执行层遵循一个铁律:单个文件聚焦一个主题,越小越好。因为模型按需加载,文件越小,单次加载越省。目录结构如下:

api-doc-generator/
├── SKILL.md                    # Layer 2 骨架(~250 tokens)
├── scripts/
│   ├── parse_js_routes.py      # Express/Koa 路由解析
│   ├── parse_fastapi.py        # FastAPI 装饰器解析
│   └── parse_spring.py         # Spring 注解解析
├── references/
│   ├── openapi-spec.md         # OpenAPI 3.x 字段规范
│   ├── output-templates.md     # 输出模板
│   └── example-output.md       # 完整示例
└── assets/
    └── example-openapi.json    # 静态示例文件

references/openapi-spec.md(节选)放的是字段级细节:

### paths 对象
每个路径以 `/` 开头,键为路径模板,值为 Path Item Object。

### parameters
- `name`:必填,参数名
- `in`:query | header | path | cookie
- `required`:path 参数必须为 true
- `schema`:类型定义,引用 `$ref: '#/components/schemas/...'`

### responses
- HTTP 状态码作键(如 `200`、`404`)
- 每个值含 `description`(必填)和可选 `content`

当任务是"审查这个 FastAPI 项目的接口安全"时,模型只会加载 scripts/parse_fastapi.py 和 references/openapi-spec.md,而 Express 的解析脚本、Spring 的注解规则——全部留在磁盘上零开销。这就是分层加载的本质收益:按任务裁剪上下文。

第四步:正确引用文件,控制引用深度

规范明确要求:文件引用用相对路径,且保持一层深度,避免深层嵌套引用链。

正确写法(在 SKILL.md 中):

见 [OpenAPI 字段规范](references/openapi-spec.md) 了解详细字段。
运行解析:`python scripts/parse_fastapi.py`

错误写法:

见 references/openapi-spec.md,它又指向 references/advanced/security.md,
后者再引用 references/advanced/oauth/flows.md ……

深层嵌套引用链会让模型陷入"读了 A 才知道要读 B,读了 B 才知道要读 C"的连环加载,既慢又费 token。每层 reference 应当自包含,直接被 SKILL.md 指向。

第五步:用 skills-ref 验证

写完后用官方参考库 skills-ref 校验 frontmatter 合法性与命名规范:

npx skills-ref validate ./api-doc-generator

它会检查 name 是否匹配目录名、是否含非法字符、description 是否非空等。验证通过再发布。

四、完整实战案例:API 文档生成器端到端

把上面片段拼成可运行的项目。先创建目录与最小文件:

mkdir -p ~/.claude/skills/api-doc-generator/{scripts,references,assets}

scripts/parse_fastapi.py(一个真实可跑的最小解析器):

"""从 FastAPI 项目提取路由定义,输出端点列表。
用法: python parse_fastapi.py <app_dir>
"""
import ast, sys, json
from pathlib import Path

VERBS = {"get", "post", "put", "delete", "patch"}

def extract(file_path: Path):
    tree = ast.parse(file_path.read_text(encoding="utf-8"))
    endpoints = []
    for node in ast.walk(tree):
        if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
            if node.func.attr in VERBS:
                path = ast.literal_eval(node.args[0]) if node.args else ""
                endpoints.append({"method": node.func.attr.upper(), "path": path,
                                  "file": str(file_path)})
    return endpoints

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("用法: python parse_fastapi.py <app_dir>", file=sys.stderr); sys.exit(1)
    root = Path(sys.argv[1])
    all_eps = []
    for py in root.rglob("*.py"):
        try: all_eps += extract(py)
        except SyntaxError: continue
    print(json.dumps({"openapi": "3.0.3", "paths_count": len(all_eps),
                      "endpoints": all_eps}, indent=2, ensure_ascii=False))

预期结果:对一个含 @app.get("/users") 的 FastAPI 文件运行,输出:

{
  "openapi": "3.0.3",
  "paths_count": 1,
  "endpoints": [{"method": "GET", "path": "/users", "file": "app.py"}]
}

触发演示:在项目里对 Claude 说"帮我导出这个 FastAPI 服务的 OpenAPI 文档",Claude 会:①Layer 1 匹配 description 命中 → ②Layer 2 加载 ~250 token 骨架,识别出是 FastAPI 分支 → ③Layer 3 仅加载 parse_fastapi.py + openapi-spec.md → ④产出文档。全程上下文干净,Express/Spring 那几个文件从不被加载。

五、三层结构 vs 单文件臃肿:一次量化对比

假设一个 agent 同时挂载 20 个 Skill,其中 5 个是本案例这样的"知识密集型"技能:

指标 单文件臃肿版 三层分层版
单个 Skill 激活成本 ~8000 tokens ~250 tokens(骨架)
20 个 Skill 发现总成本 ~1600 tokens ~1600 tokens(发现层不变)
一次任务无关文件加载 0(全在一起) 仅命中分支的文件
"lost in the middle"风险 高 低

发现层成本两种结构相同(都只读 name+description),差异全在激活层和执行层。分层结构的全部收益,体现在"只加载被命中的那一支"。

flowchart LR
    subgraph 单文件臃肿版
        A1[激活即加载 8000 tokens<br/>所有规则/示例/脚本说明]
    end
    subgraph 三层分层版
        B1["骨架 250 tokens"] --> B2{命中分支?}
        B2 -->|FastAPI| B3["parse_fastapi.py<br/>+openapi-spec.md<br/>≈1200 tokens"]
        B2 -->|Express| B4["parse_js_routes.py<br/>≈900 tokens"]
        B2 -->|其余| B5[不加载]
    end

六、常见问题与解决方案

Q1:我的 Skill 描述很详细,为什么还是不触发? A:description 里缺"用户真实措辞"。你写的是"处理文档",但用户说的是"导出 Swagger"。把后者作为关键词写进去。路由纯靠 Layer 1 的推理,关键词就是路由表。

Q2:模型激活后总是把 references 全读一遍,怎么办? A:在 SKILL.md 骨架末尾加一句显式指令:"除非当前任务明确需要,不要预读 references/ 下的文件。" 这句指令的省 token 效果立竿见影。

Q3:reference 文件该切多细? A:按"一次任务只会同时需要其中几个"来切分。OpenAPI 字段规范、输出模板、示例天然是独立主题,就拆成三个文件;如果两个文件几乎总是一起被读,就合并。

Q4:跨平台(Claude Code / Codex / Gemini)格式一样吗? A:SKILL.md 格式是 2025-12-18 发布的开放标准,OpenAI、Google、GitHub、Cursor 均已采纳,格式一致;只是存放路径不同(Claude Code 用 ~/.claude/skills/,Codex 用 .agents/skills/,Gemini 用 .gemini/skills/)。

Q5:激活后卸载,下次又重载,会不会反复浪费? A:会。成熟的实现会缓存最近用过的 Skill、保持高频激活的 Skill"warm",在上下文效率与重复加载成本间取平衡。设计上让骨架小,正是为了降低这个反复成本。

延伸阅读

训练营适用性评估

参考来源

  1. Agent Skills Specification — Agent Skills 开放标准的完整格式规范,含 frontmatter 字段约束、目录结构与渐进披露定义
  2. Agent Skills: Progressive Disclosure as a System Design Pattern — SwirlAI Newsletter — 对 Anthropic 17 个官方 Skill 的 token 实测(发现层 80 tokens 中位数、激活层 275–8000 tokens 区间)及三层架构系统设计分析
  3. Skills: the art of progressive disclosure in the era of context engineering — Marcel Castro — 从上下文工程视角解读 Skill 分层加载与 context curation
  4. Skills overview — Claude 官方文档 — Anthropic 官方 Skills 工作机制与元数据加载说明
  5. wshobson/agents: Agent Skills 文档 — 社区整理的 Agent Skills 实践文档与示例结构

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入