<!-- title: 渐进式披露造依赖许可证合规Skill -->

# Claude Code Skills 进阶实战：用渐进式披露架构造一个多文件合规扫描技能

## 概述

本教程面向已经会写「一个 SKILL.md 文件搞定一切」、但想把技能做得更稳健、更省 token、更易维护的开发者。你将掌握 Claude Code Skills 的**三层渐进式披露（Progressive Disclosure）架构**，并通过一个完整的、可直接运行的**依赖许可证合规扫描 Skill（dep-license-checker）**，学会何时把逻辑拆成 bundled 脚本、何时放进按需加载的 references、何时用 assets 模板，最终完成「构建 → 安装 → 触发测试 → eval 验证 → 打包分发」的完整生命周期。适合训练营中「进阶工程化」阶段的学员，预计 2.5–3 小时。

## 前置条件

- Claude Code v1.0+（内置 Skills 支持，无需额外配置）
- 能熟练用 `bash`、会读 YAML 与 Markdown
- 本机装好 Python 3（脚本用纯标准库，零依赖）
- 一台已登录 Claude Code 的终端（macOS/Linux/WSL 均可）

---

## 一、为什么不能「一个大 SKILL.md 走天下」

很多入门教程教的是「最小可用技能」：一个目录、一个 SKILL.md、几十行指令。这在原型阶段够用，但一旦技能变复杂（分类规则、评分体系、输出模板、几百行分析逻辑），单一文件会出现三个致命问题：

1. **token 失控**：每次触发都把全部内容塞进上下文，长会话里持续占用。
2. **靠模型「猜」而不是「算」**：让模型用文本生成去分类、排序、统计，既慢又不稳定。
3. **难以维护**：规则散在自然语言段落里，改一处怕动全身。

Claude Code 官方的设计哲学叫**渐进式披露**：技能按需分层加载，每一层只在前一层不够用时才进入上下文。理解它，是写出「生产级」技能的分水岭。

## 二、三层渐进式披露架构

官方文档把 Skills 的加载明确分为三个阶段，每一层都在为 token 效率服务：

| 阶段 | 触发时机 | 进入上下文的内容 | 典型开销 |
|------|----------|------------------|----------|
| 第 1 层 · 元数据扫描 | 会话启动时 | 仅 `name` + `description` | 每个技能约 30–50 tokens |
| 第 2 层 · 全文加载 | 模型判定相关后 | SKILL.md 正文（不含子文件） | 通常 < 5000 tokens |
| 第 3 层 · 资源执行 | 实际需要时 | 运行脚本（仅输出进上下文）、按需读 references/assets | 脚本代码本身永不进入上下文 |

关键认知有两条：

- **脚本代码永远不会进入上下文，只有它的输出会。** 你可以 bundle 一个 1000 行的 Python 分析器，最终进入上下文的可能只是一行汇总数字。这就是用「代码执行」替代「文本生成」的底层红利。
- **references/assets 默认是休眠的。** 只有当 SKILL.md 明确指引 Claude「需要时去读 `references/xxx.md`」、且当前任务真的需要时，它们才被加载。这让你可以放很厚的参考文档而不拖累日常请求。

> 注意：会话启动时 Claude 才扫描技能目录，**修改任何技能文件后必须重启 Claude Code 才能生效**。这是排查「改了不生效」问题的第一站。

```mermaid
flowchart TD
    A[会话启动] --> B[第1层: 扫描所有 SKILL.md 元数据<br/>name+description, 各30-50 tokens]
    B --> C[用户提问]
    C --> D{模型语义匹配}
    D -->|相关| E[第2层: 加载本技能 SKILL.md 正文]
    D -->|无关| Z[技能保持休眠]
    E --> F{需要确定性计算?}
    F -->|是| G[第3层: 执行 bundled 脚本<br/>代码不进上下文, 仅输出]
    F -->|否| H{需要查细则?}
    H -->|是| I[第3层: 按需读 references/*.md]
    H -->|否| J[生成结果]
    G --> J
    I --> J
    J --> K[填充 assets 模板输出报告]
```

## 三、规划技能目录结构

我们的目标技能 `dep-license-checker`：用户问「帮我检查这个项目的依赖许可证合规性」时自动触发——读取 `package.json` / `requirements.txt` / `go.mod` 里的依赖，用确定性脚本分类为宽松型（permissive）/ 左版权型（copyleft）/ 未知型，标记违规项，最后套用模板生成 Markdown 合规报告。

它的目录结构如下，每一层都对应渐进式披露的一个角色：

```
dep-license-checker/
├── SKILL.md                      # 第2层入口：指令，<500行
├── scripts/
│   └── classify.py               # 第3层：确定性分类器，纯标准库
├── references/
│   ├── license_knowledge.md      # 第3层：SPDX 分类细则，按需加载
│   └── policy_default.md         # 第3层：默认合规策略，按需加载
└── assets/
    └── compliance_report.md      # 第3层：报告模板，按需填充
```

设计原则一句话：**「能用代码算的就别让模型猜，细则文档只在要用时才加载，输出靠模板而非现编。」**

## 四、步骤 1：编写 SKILL.md（第 2 层入口）

SKILL.md 由 YAML frontmatter 和 Markdown 正文组成。frontmatter 有两个**必填**字段，校验很严格——写错会导致技能**静默加载失败**（不报错，就是不出现在索引里）：

- `name`：最长 64 字符，只能含小写字母、数字、连字符；不能含 `anthropic`、`claude` 等保留词，不能含 XML 标签。
- `description`：最长 1024 字符，非空。**这是技能能否被触发的命门**——模型靠它做语义匹配。

写作诀窍：description 必须同时回答「做什么」和「何时用」，并塞进用户可能脱口而出的触发词。

```markdown
---
name: dep-license-checker
description: 扫描项目的依赖清单（package.json / requirements.txt / go.mod / Cargo.toml），用确定性脚本将每个依赖的许可证分类为宽松型 / 左版权型 / 未知型，标记违反「仅允许宽松许可证」策略的依赖，并生成 Markdown 合规报告。当用户提到「许可证合规」「license compliance」「检查依赖 license」「GPL/AGPL 风险」「开源许可审查」「引入第三方依赖前检查」时使用。
---

# 依赖许可证合规检查

## 何时触发
用户要把第三方依赖引入项目、或要做开源合规审查、或担心左版权（GPL/AGPL/LGPL/MPL）污染时。

## 执行步骤（务必按顺序）
1. 用 Glob/Grep 找到项目根下的清单文件：`package.json`、`requirements.txt`、`go.mod`、`Cargo.toml`。没有就停止并提示用户。
2. 运行确定性分类器（**首选，代码不进上下文**）：
   `python3 scripts/classify.py <清单文件路径>`
   解析它的 JSON 输出，不要自己用文本去分类。
3. 仅当存在 `copyleft` 或 `unknown` 项、且需要向用户解释某许可证含义时，再去读 `references/license_knowledge.md`。否则跳过以省 token。
4. 仅当用户询问「为什么默认禁止左版权」或要改策略时，才读 `references/policy_default.md`。
5. 把脚本输出填进 `assets/compliance_report.md` 模板，生成最终报告。违规项必须高亮，并给出「替换为宽松许可的替代品」建议。

## 边界
- 不联网。本技能基于本地清单与内置的常见包许可证映射做静态判断。
- 对不在映射表里的包标记为 `unknown`，请用户人工确认，不要臆测许可证。
```

注意正文里大量出现「仅当……才读……」——这正是**手动编排渐进式披露**的写法：你明确告诉模型第 3 层资源何时该被唤醒。

## 五、步骤 2：编写 bundled 脚本（第 3 层 · 确定性引擎）

把分类逻辑写成纯标准库 Python，保证零依赖、可单跑、输出 JSON。脚本体量可以很大，但**只有最终 JSON 进入上下文**。下面是可直接保存运行的 `scripts/classify.py`：

```python
#!/usr/bin/env python3
"""依赖许可证确定性分类器。纯标准库，输出 JSON。
分类口径：
  permissive = MIT / Apache-2.0 / BSD-* / ISC
  copyleft   = GPL / AGPL / LGPL / MPL
  unknown    = 映射表未覆盖（需人工确认）
退出码：存在 copyleft 违规返回 1，否则 0。
"""
import json, re, sys
from pathlib import Path

# 常见包 -> SPDX 许可证（演示用，可按团队实际扩展）
NPM = {
    "react": "MIT", "react-dom": "MIT", "vue": "MIT", "lodash": "MIT",
    "express": "MIT", "axios": "MIT", "moment": "MIT", "chalk": "MIT",
    "jquery": "MIT", "typescript": "Apache-2.0", "rxjs": "Apache-2.0",
    "elasticsearch": "Apache-2.0", "left-pad": "WTFPL",
    "chart.js": "MIT", "bluebird": "MIT",
}
PY = {
    "requests": "Apache-2.0", "django": "BSD-3-Clause", "flask": "BSD-3-Clause",
    "numpy": "BSD-3-Clause", "pandas": "BSD-3-Clause", "fastapi": "MIT",
    "celery": "BSD-3-Clause", "scipy": "BSD-3-Clause", "pyyaml": "MIT",
    "gunicorn": "MIT", "uvicorn": "BSD-3-Clause",
}
PERMISSIVE = {"MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause",
              "ISC", "0BSD", "WTFPL", "Unlicense"}

def category(license_id: str) -> str:
    if not license_id:
        return "unknown"
    lid = license_id.upper()
    if lid in {x.upper() for x in PERMISSIVE}:
        return "permissive"
    if re.search(r"\b(GPL|AGPL|LGPL|MPL)\b", lid):
        return "copyleft"
    return "unknown"

def parse_package_json(text):
    data = json.loads(text)
    deps = {**data.get("dependencies", {}), **data.get("devDependencies", {})}
    return [(name, NPM.get(name.lower(), "")) for name in deps]

def parse_requirements(text):
    out = []
    for line in text.splitlines():
        line = line.split("#")[0].strip()
        m = re.match(r"^([A-Za-z0-9_.\-]+)", line)
        if m:
            name = m.group(1).lower()
            out.append((name, PY.get(name, "")))
    return out

def parse_go_mod(text):
    out = []
    for line in text.splitlines():
        m = re.match(r"^\s*(require\s+)?([^\s]+)\s+v[\d.]+", line)
        if m:
            pkg = m.group(2).lower()
            # 粗映射：含 / 的取最后一段
            last = pkg.split("/")[-1]
            out.append((pkg, NPM.get(last) or PY.get(last) or ""))
    return out

PARSERS = {
    "package.json": parse_package_json,
    "requirements.txt": parse_requirements,
    "go.mod": parse_go_mod,
}

def main():
    path = Path(sys.argv[1]) if len(sys.argv) > 1 else None
    if not path or not path.exists():
        print(json.dumps({"error": f"未找到清单文件: {path}"}))
        sys.exit(2)
    parser = PARSERS.get(path.name)
    if not parser:
        print(json.dumps({"error": f"不支持的清单类型: {path.name}"}))
        sys.exit(2)

    result = {"permissive": [], "copyleft": [], "unknown": []}
    for name, lic in parser(path.read_text(encoding="utf-8")):
        cat = category(lic)
        result[cat].append({"name": name, "license": lic or "未声明/未知"})

    violations = result["copyleft"]
    result["summary"] = {
        "total": sum(len(result[k]) for k in ("permissive", "copyleft", "unknown")),
        "violations": len(violations),
        "policy": "仅允许宽松许可证 (MIT/Apache/BSD/ISC)",
        "verdict": "不合规" if violations else "合规",
    }
    print(json.dumps(result, ensure_ascii=False, indent=2))
    sys.exit(1 if violations else 0)

if __name__ == "__main__":
    main()
```

**预期结果**（针对一个含 `react`、`lodash`、`elasticsearch` 的 `package.json`）：

```json
{
  "permissive": [
    {"name": "react", "license": "MIT"},
    {"name": "lodash", "license": "MIT"},
    {"name": "elasticsearch", "license": "Apache-2.0"}
  ],
  "copyleft": [],
  "unknown": [],
  "summary": {"total": 3, "violations": 0,
              "policy": "仅允许宽松许可证 (MIT/Apache/BSD/ISC)",
              "verdict": "合规"}
}
```

一旦混入一个 GPL 依赖，`copyleft` 列表非空、退出码变 1、`verdict` 变「不合规」——确定性、可复现、零幻觉。

## 六、步骤 3：编写按需 references（第 3 层 · 知识库）

这两份文件**默认休眠**，只有 SKILL.md 指引、且任务需要时才被读取。可以写得详尽而不怕拖累日常请求。

`references/license_knowledge.md`（节选）：

```markdown
# 许可证分类细则

## 宽松型（permissive）——通常可放心引入
- MIT / BSD-2 / BSD-3 / ISC / 0BSD：几乎无义务，保留版权声明即可。
- Apache-2.0：需保留 NOTICE 文件，含专利授权条款，企业友好。

## 左版权型（copyleft）——触发「传染」，需法务介入
- GPL：衍生作品必须同样以 GPL 开源。
- AGPL：网络服务也触发开源义务，SaaS 场景高风险。
- LGPL：可作为库链接而不传染，但修改库本身需开源。
- MPL-2.0：文件级 copyleft，比 GPL 弱。

## 未知型（unknown）
映射表未覆盖。处置：去 npm/PyPI 页面或源码 LICENSE 文件人工核实，
**不要由模型猜测**。
```

`references/policy_default.md`（节选）：

```markdown
# 默认合规策略：仅宽松许可证
- 允许：MIT、Apache-2.0、BSD-2/3、ISC、0BSD、Unlicense
- 禁止：GPL、AGPL、LGPL、MPL（除非走例外审批）
- 未知：阻断引入，待人工确认后更新 scripts/classify.py 的映射表
- 若团队策略不同（如允许 LGPL），在此文件修改即可，无需动脚本
```

## 七、步骤 4：编写 assets 模板（第 3 层 · 输出骨架）

`assets/compliance_report.md`（节选）——让模型「填空」而非「从零写」，输出更稳定一致：

```markdown
# 依赖许可证合规报告

- 检查范围：{{manifest_file}}
- 依赖总数：{{total}}
- 违规项数：{{violations}}
- 结论：**{{verdict}}**

## 违规依赖（需处理）
{{#each copyleft}}
- ⚠️ `{{name}}` —— 许可证 `{{license}}`，建议替换为等价的宽松许可替代品
{{/each}}

## 合规依赖
{{#each permissive}}
- ✅ `{{name}}` —— `{{license}}`
{{/each}}

## 待人工确认
{{#each unknown}}
- ❓ `{{name}}` —— `{{license}}`
{{/each}}
```

## 八、步骤 5：安装并触发测试

把整个目录放到技能发现路径之一。三种位置按优先级覆盖（同名时 personal > project > plugin）：

```bash
# 个人级（跨项目可用）
mkdir -p ~/.claude/skills && cp -r dep-license-checker ~/.claude/skills/

# 或项目级（随 git 共享给团队）
mkdir -p .claude/skills && cp -r dep-license-checker .claude/skills/
```

**重启 Claude Code**（发现扫描只发生在启动时），然后在终端对一个小项目说：

> 「帮我检查一下这个项目依赖的许可证合规性。」

预期：Claude 不需要你输入任何 `/` 命令——它读到 `dep-license-checker` 的 description 后**自动语义匹配**并加载全文，接着调用 `scripts/classify.py`，最后套模板出报告。这就是「模型调用（model-invoked）」模式：技能是被「想起来」的，不是被「点开」的。

**自检要点**：若不触发，99% 是 description 不够具体——对照第二条，补足触发词。

## 九、步骤 6：用 skill-creator 跑 eval 验证

生产级技能必须可量化。用内置的 `skill-creator` 技能为 `dep-license-checker` 生成评测集：准备 5–8 条「应触发」的正例（如「这个 npm 包能不能引入」「AGPL 的依赖有风险吗」）和 5–8 条「不应触发」的负例（如「帮我写个单元测试」「重构这个函数」），跑触发准确率；再准备带已知许可证的样例工程，跑分类正确率与退出码。把 eval 结果写进技能 README，作为回归基线。每次改 description 或脚本后重跑，确保没有劣化。

## 十、步骤 7：打包与分发

- **团队共享**：放 `.claude/skills/` 提交 git，clone 即得，零额外安装。
- **社区分发**：做成 plugin，把 `dep-license-checker` 放进 plugin 的 `skills/` 目录，用户安装 plugin 即自动发现、卸载即移除。
- **跨平台注意**：Skills **不跨平台同步**——Claude.ai 网页版、API、Claude Code 三套独立管理。本教程的文件型技能只在 Claude Code 生效；上传到 Claude.ai/API 需另行操作（API 还受「单请求最多 8 个技能」的硬上限约束）。

```mermaid
flowchart LR
    subgraph 构建
      S1[SKILL.md] --- S2[scripts/classify.py]
      S2 --- S3[references/]
      S3 --- S4[assets/]
    end
    S4 --> R[重启 Claude Code]
    R --> D[启动扫描<br/>仅加载 name+description]
    D --> M{用户提问<br/>模型语义匹配}
    M -->|命中| L2[加载 SKILL.md 正文]
    L2 --> EXE[执行 classify.py<br/>仅 JSON 输出进上下文]
    EXE --> REF{需要细则?}
    REF -->|是| RD[按需读 references]
    REF -->|否| TPL
    RD --> TPL[填 assets 模板]
    TPL --> OUT[Markdown 合规报告]
    EV[skill-creator eval<br/>触发率+分类正确率] -.回归.-> S1
```

## 十一、常见问题与解决方案

| 现象 | 根因 | 解决 |
|------|------|------|
| 技能装了却不触发 | description 太泛（如「分析数据」） | 补「做什么 + 何时用 + 触发词」，参照步骤 1 |
| 技能完全不出现 | frontmatter YAML 非法 / name 含保留词或大写 | 校验 `---` 三连、name 全小写连字符；用 debug 启动看加载错误 |
| 改了文件没生效 | 启动后才扫描 | 重启 Claude Code |
| 分类结果不稳定 | 让模型用文本分类而非跑脚本 | 在 SKILL.md 强制「先跑 classify.py 再解析 JSON」 |
| 上下文莫名变满 | references 被无条件读取 | 把「仅当……才读」写进正文，减少默认加载 |
| 依赖不在映射表 | 设计如此 | 标 `unknown` 让用户人工确认，并提示扩充映射表 |
| 大团队技能过多变慢 | 超 ~100 个技能，元数据开销显著 | 精简到「少而精」，删不用、禁用不常用 |

## 十二、训练营适用性评估

- **目标训练营类型**：AI 工程化 / DevOps 自动化 / 研发效能提升营。也适合「Claude Code 进阶」专题的第二天课程（第一天讲单文件技能入门，第二天讲本架构）。
- **学员画像**：有 3 个月以上编程经验、用过 Claude Code 完成过至少一个小任务。
- **预计教学时长**：讲授 + 跟做约 2.5 小时；含扩展练习（让学员为本队策略写自定义 `policy_default.md` 并重跑 eval）约 3.5 小时。
- **可扩展作业**：① 把映射表从硬编码改为读取本地 `license_db.json`；② 增加 `Cargo.toml` 解析器；③ 用 hooks 在每次 `git commit` 前自动跑该技能做门禁（衔接 Hooks 训练模块）。
- **不宜用于**：纯零基础营（需先理解单文件技能）或纯算法/数据结构营。

## 延伸阅读

- 官方 Skills 文档（创建、管理、共享）—— `code.claude.com/docs/en/skills`
- Claude.ai Skills 总览（渐进式披露原理）—— `claude.com/docs/skills/overview`
- skill-creator 技能（生成、优化、跑 eval）—— Claude Code 内置技能
- 「How to Build a Production-Ready Claude Code Skill」—— towardsdatascience
- MCP 与 Skills 的边界：技能无网络、靠 MCP 提供外部连接

## 参考来源

1. [Claude Code Skills: Complete Technical Deep-Dive（gregpriday/claude-code-docs）](https://github.com/gregpriday/claude-code-docs/blob/main/claude-code-skills.md) — 三层渐进式披露架构、SKILL.md frontmatter 校验规则、脚本不进上下文等核心机制的权威技术拆解（本文主要事实来源）。
2. [Extend Claude with skills — Claude Code 官方文档](https://code.claude.com/docs/en/skills) — 技能的创建、目录发现路径（`~/.claude/skills/`、`.claude/skills/`、plugin）、管理与共享的官方说明。
3. [Claude Code Skills 2026 — Progressive Disclosure and How to Standardise Workflow](https://anhtu.dev/claude-code-skills-2026-progressive-disclosure-and-how-to-standardise-workflow-for-engineering-teams-1062) — 工程团队视角的三层披露实践与工作流标准化建议。
4. [How to Build a Production-Ready Claude Code Skill — Towards Data Science](https://towardsdatascience.com/how-to-build-a-production-ready-claude-code-skill/) — 生产级技能的打包、版本管理与生态现状（截至 2026 年 3 月）。
5. [Claude Skills Documentation: Official Reference — Verdent](https://www.verdent.ai/guides/claude-skills-documentation) — SKILL.md 格式、目录结构、API 端点与版本管理的开发者参考。

> [!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学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
