<!-- title: Skill参数化与动态注入实战 -->

# Claude Code Skill 参数化与动态上下文注入实战

## 概述

本教程聚焦 Claude Code Skill 中两个最实用却常被忽略的机制:**参数化**(`$ARGUMENTS`/`$N`/命名参数/`argument-hint`)与**动态上下文注入**(`!`command`` 预处理)。学完你将能构建带变量、能"开箱即装现网数据"、可携带脚本与审批白名单的可复用工作流 Skill,适合已会用基础 Skill、希望把重复流程沉淀为命令行工具的中级开发者。

## 前置条件

- Claude Code v2.1.145 或更高(本教程涉及 `allowed-tools`、`disable-model-invocation`、`context: fork` 等字段,新版行为更稳定)
- 一个本地 git 仓库,任意语言均可
- 基础 shell 知识(`git`、`gh`、`jq` 等命令行工具)
- 已创建过至少一个最简 `SKILL.md`,理解 `description` 的作用

本教程**不**重复"渐进披露三层结构""子 Agent 编排""触发词调优"等内容,而是从另一个切面切入:让一个 Skill 变成**可以接收参数、可以预装实时数据、可以携带可执行脚本**的"小型命令行程序"。

---

## 一、核心心智模型:Skill 是"带预处理器的命令"

很多人把 Skill 当成"一段提示词",这低估了它。Skill 的执行其实是**两段式流水线**:

```mermaid
flowchart LR
    A["/release-preflight v1.4.0"] --> B["1. 参数替换<br/>$ARGUMENTS / $N / 命名参数"]
    B --> C["2. 上下文注入<br/>!`command` 在 Claude 之前执行"]
    C --> D["3. 渲染后的 SKILL.md<br/>作为单条消息注入对话"]
    D --> E["Claude 按指令<br/>调用工具完成任务"]
    E --> F["结果返回主对话<br/>SKILL.md 内容常驻本会话"]
```

关键在于**第 2 步**:`!`command`` 不是让 Claude 去执行命令,而是**在 Claude 看到任何东西之前**,由 Claude Code 的运行时先把命令跑完、把输出**文本替换**到那个位置。所以 Claude 收到的永远是"现网的快照数据",而不是"一个待执行的命令"。这是消除幻觉、让 Skill 输出可复现的核心手段。

理解这一点后,后面所有字段都只是给这条流水线加"旋钮"。

---

## 二、第一步:创建带参数的 Skill 骨架

### 操作说明

在个人技能目录下创建一个**发布前检查**的 Skill,它接收一个版本号作为参数:

```bash
mkdir -p ~/.claude/skills/release-preflight/scripts
```

写入 `~/.claude/skills/release-preflight/SKILL.md`:

```markdown
---
name: release-preflight
description: 发布前自检。当用户要打 tag、发版、上线前做合规与质量检查时使用。
argument-hint: [version]
arguments: [version]
disable-model-invocation: true
---

## 发布目标
即将发布版本:**$version**

## 当前仓库快照
- 当前分支:!`git rev-parse --abbrev-ref HEAD`
- 最新提交:!`git log -1 --pretty=format:'%h %s'`
- 未提交文件:!`git status --porcelain | head -20`

## 自检清单
1. 若上述"未提交文件"非空,先提示用户提交或 stash,不要自动 commit。
2. 跑测试脚本:${CLAUDE_SKILL_DIR}/scripts/check.sh
3. 检查 CHANGELOG 是否包含 $version。
4. 给出"可以打 tag $version"或"阻塞项"结论。
```

### 预期结果

在仓库里执行 `/release-preflight v1.4.0`,Claude 收到的实际内容(伪表示)是:

```
## 发布目标
即将发布版本:**v1.4.0**

## 当前仓库快照
- 当前分支:main
- 最新提交:a1b2c3d feat: add export api
- 未提交文件: M src/api.py

## 自检清单
1. 若上述"未提交文件"非空,先提示...
2. 跑测试脚本:/home/you/.claude/skills/release-preflight/scripts/check.sh
...
```

注意三个细节:`$version` 被替换成了 `v1.4.0`;`!`git ...`` 被替换成了**真实输出**;`${CLAUDE_SKILL_DIR}` 被替换成了 Skill 目录的绝对路径——这意味着脚本路径**不依赖你当前的工作目录**,在任意子目录调用都正确。

### 常见问题与解决

| 现象 | 原因 | 解决 |
|------|------|------|
| `$ARGUMENTS` 原样出现,没被替换 | 你用了未声明的占位符,或参数没传 | 用 `arguments: [version]` 声明后再用 `$version`;或显式写 `$ARGUMENTS` |
| `!`git status`` 没执行,显示成字面量 | `!` 没出现在**行首或空格之后** | `KEY=!`cmd`` 这种紧跟字符的形式不会触发,需独占一行或前面是空白 |
| 脚本路径找不到 | 用了相对路径 `./scripts/check.sh` | 必须用 `${CLAUDE_SKILL_DIR}/scripts/check.sh` |
| Claude 不肯跑脚本 | 缺少 `allowed-tools` 授权 | 见第三步 |

---

## 三、第二步:用 `allowed-tools` 预授权工具,免去逐次确认

### 操作说明

默认情况下,Claude 每次执行 bash 命令都会弹权限确认。在自动化 Skill 里这很烦人。把上一步的 frontmatter 扩展为:

```yaml
---
name: release-preflight
description: 发布前自检。
argument-hint: [version]
arguments: [version]
disable-model-invocation: true
allowed-tools:
  - Bash(${CLAUDE_SKILL_DIR}/scripts/check.sh)
  - Bash(git status *)
  - Bash(git log *)
  - Bash(git diff *)
  - Bash(git tag *)
  - Read
  - Grep
---
```

### 关键认知(务必记牢)

`allowed-tools` **只授权、不限制**。它的语义是"当这个 Skill 激活时,以下工具可免确认使用",而**不是**"只允许用这些工具"。Claude 仍然可以调用其它工具,只是会走正常的权限流程。

- 如果你要的是**限制**(比如一个后台 Skill 永远不能 `AskUserQuestion`),用 `disallowed-tools`,且该限制在你发送下一条消息时自动清除。
- 如果你要的是**硬性禁止**某工具跨所有 Skill,去 `settings.json` 的 `deny` 规则里加。
- 项目级 Skill(在 `.claude/skills/` 下)的 `allowed-tools` 要在接受**工作区信任对话框**后才生效——这是为了防止一个被 clone 下来的恶意仓库用 Skill 给自己开后门。所以审查第三方项目的 Skill 时,先看它的 `allowed-tools`。

### 常见问题

- **现象**:Skill 还是每次都问权限。**原因**:把 `allowed-tools` 写在了 `disallowed-tools` 思维下,只列了"想用"的,却没意识到还有别的工具也在跑;真正的修复是**把脚本调用写成精确的 glob**,如 `Bash(${CLAUDE_SKILL_DIR}/scripts/check.sh)`。
- **现象**:团队 clone 仓库后 Skill 不生效。**原因**:没接受信任对话框。让队友在该项目里执行 `/trust` 或首次进入时确认。

---

## 四、第三步:用 `!`command`` 注入多维实时数据(进阶)

`!`command`` 的威力在于**可以并列多条、可以做格式化、可以跨工具**。下面是一个更完整的"PR 评审"风格 Skill,它一次性把 diff、CI 状态、变更文件清单都注入进来:

```markdown
---
name: pr-xray
description: 对当前 PR 做穿透式评审。用户提到 review PR、看这个 PR、代码评审时使用。
context: fork
agent: Explore
allowed-tools: Bash(gh *) Bash(git *) Read Grep Glob
---

## PR 上下文(运行时注入)

### 变更概览
!`gh pr view --json title,author,additions,deletions,baseRefName,headRefName | jq -r '"标题:\(.title)\n作者:\(.author.login)\n+/-:\(.additions)/\(.deletions)\n分支:\(.baseRefName) ← \(.headRefName)"'`

### 变更文件
!`gh pr diff --name-only`

### CI 状态
!`gh pr checks 2>/dev/null | head -20 || echo "无 CI 检查"`

### 完整 diff(前 400 行)
```! 
gh pr diff | head -400
```

## 评审任务
基于上述真实数据,按以下顺序输出:
1. **风险分级**:CRITICAL / HIGH / MEDIUM / LOW,各列具体文件:行。
2. **回归点**:哪些改动可能破坏既有测试。
3. **遗漏项**:有没有改了接口却没改调用方。
只引用上面真实出现过的文件名和行号,禁止臆测未出现的文件。
```

### 几个易踩的坑

1. **多行命令用代码块**。行内 `!`cmd`` 只适合单行;多行必须用 ` ```! ` 开头的围栏代码块,如上例最后一段。
2. **输出不会被二次扫描**。即一条命令的输出里如果**包含** `!`foo``,它不会被再次执行——这避免了递归注入攻击,但也意味着你不能"用命令生成命令"。
3. **`jq` 在管道里很好用**,但前提是机器装了 `jq`。生产环境建议在 Skill 里加一句 `!`command -v jq || echo "WARN: jq 未安装"`` 做软提示,而不是硬失败。
4. **`context: fork` + `agent: Explore` 的组合**:本 Skill 在一个**隔离的子 Agent** 里运行,它看不到你的对话历史,只有上面这份渲染后的内容作为唯一任务输入。`Explore`/`Plan` 这两个内置 Agent 还会**跳过 CLAUDE.md 和 git status**,所以上下文极小、成本极低,适合"只读分析"类任务。但要注意:fork 模式只适合**有明确任务**的 Skill;如果你的 Skill 只是"一些约定规范"没有任务,子 Agent 收到规范却无事可做,会直接空转返回。

---

## 五、第四步:理解 Skill 的"生命周期",别让它失效

这是最容易被忽略、却最容易导致"Skill 第一次好用、后面就不管用了"的根因。

### 加载与驻留机制

1. **启动时**:Claude Code 只把**所有 Skill 的 name + description** 装进系统提示(这就是为什么 `description` 的前几个词最关键)。完整 SKILL.md **不加载**——这是渐进披露的成本控制点。
2. **调用时**:你或 Claude 触发后,渲染后的 SKILL.md 作为**单条消息**进入对话,并**在剩余整个会话里常驻**。每一行都是**持续**的 token 成本,所以 SKILL.md 要像 `CLAUDE.md` 一样精炼。
3. **压缩时(auto-compaction)**:当上下文被摘要以腾空间时,Claude Code 会把"每个 Skill 最近一次调用"重新挂回,但**每个只保留前 5,000 token**,且所有重挂的 Skill **共享 25,000 token 总预算**,从最近调用的开始填。**结论:一个会话里调用了很多 Skill 之后,最早调用的可能被整体丢弃。**

### 实战建议

```mermaid
flowchart TD
    S1["启动:仅加载 name+description"] --> S2{"用户/Claude 调用?"}
    S2 -->|是| S3["渲染 SKILL.md 注入对话<br/>常驻整个会话"]
    S2 -->|否| S4["零成本待命"]
    S3 --> S5{"上下文触发压缩?"}
    S5 -->|是| S6["每个 Skill 留前 5000 token<br/>总计 25000 token 预算"]
    S6 --> S7["从最近向前填<br/>老的可能被丢弃"]
    S5 -->|否| S8["原样保留"]
    S7 --> S9{"行为似乎失效?"}
    S9 -->|是| S10["重新 /调用一次该 Skill<br/>恢复完整内容"]
    S9 -->|否| S11["继续工作"]
```

落地动作:

- **SKILL.md 控制在 500 行以内**,大段参考资料拆到 `reference.md` 并在主文件里用链接引用("完整 API 见 [reference.md](reference.md)"),按需加载。
- 写指令时用**持续生效的"立场句"**而不是"一次性步骤"。因为 Claude Code **不会**在后续轮次重新读 SKILL.md——它只看你最初注入的那条消息。所以"始终在提交前运行 lint"是好的,"第一步先 lint"在长任务里可能被遗忘。
- 如果一个 Skill **在第 N 轮之后看似不再影响行为**,通常内容还在,只是模型选择了别的路径。**强化 `description` 和指令**,或干脆**重新 `/invoke` 一次**把完整内容刷回来。

---

## 六、第五步:用 `skillOverrides` 统一治理团队 Skill

当你不想改某个 Skill 的源文件(比如它在共享仓库里、由 MCP 服务器提供),却想控制它的可见性时,用 `/skills` 菜单(选中后按空格切换状态)或直接写 `settings.local.json`:

```json
{
  "skillOverrides": {
    "legacy-context": "name-only",
    "deploy": "off"
  }
}
```

四种状态语义:

| 状态 | 是否给 Claude 列出 | 是否出现在 / 菜单 | 适用场景 |
|------|------------------|------------------|----------|
| `on`(默认) | 名称+描述 | 是 | 正常使用 |
| `name-only` | 仅名称 | 是 | 描述太吵、想降噪但保留手动调用 |
| `user-invocable-only` | 隐藏 | 是 | 只允许人调用、不让模型自动触发 |
| `off` | 隐藏 | 隐藏 | 完全停用 |

注意:`skillOverrides` **不影响插件 Skill**(用 `name-only` 形式),插件 Skill 走 `/plugin` 管理。

---

## 七、实战案例:`release-preflight` 完整可运行项目

把前面碎片整合成一个端到端可跑的示例。目录结构:

```
~/.claude/skills/release-preflight/
├── SKILL.md              # 入口:参数 + 注入 + 授权
├── scripts/
│   └── check.sh          # 可执行脚本:测试+lint+CHANGELOG校验
└── reference.md          # 详细检查规则(按需加载,常驻预算外)
```

### `scripts/check.sh`(记得 `chmod +x`)

```bash
#!/usr/bin/env bash
# 发布前自检脚本。退出码非0表示有阻塞项。
set -euo pipefail
VERSION="${1:?用法: check.sh <version>}"
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$ROOT"

fail=0
echo "== 测试 =="
if [ -f package.json ]; then npm test --silent || fail=1; fi
if [ -f Makefile ] && make -n test >/dev/null 2>&1; then make test || fail=1; fi
if [ -f pytest ] || [ -d tests ]; then python3 -m pytest -q 2>/dev/null || true; fi

echo "== Lint =="
if [ -f package.json ] && grep -q '"lint"' package.json; then npm run lint --silent || fail=1; fi

echo "== CHANGELOG 校验 =="
if ! grep -q "$VERSION" CHANGELOG.md 2>/dev/null; then
  echo "WARN: CHANGELOG.md 未发现 $VERSION"
fi

echo "== 工作区 =="
if [ -n "$(git status --porcelain)" ]; then
  echo "WARN: 存在未提交改动"
fi

exit $fail
```

### `reference.md`(按需加载的细则)

```markdown
# 发布检查细则

## 阻塞项(必须修复才能发版)
- 任一测试失败
- 存在 console.log / debugger / TODO(发布前)
- 接口签名变更但未更新调用方

## 建议项(可带 warning 发版)
- CHANGELOG 未更新
- 新增依赖未在 README 说明
```

### 在主 `SKILL.md` 里挂引用

在原 frontmatter 下追加一段:

```markdown
## 细则参考
完整阻塞/建议项判定见 [reference.md](reference.md),只在需要判定边界情况时读取。
```

### 验证流程

```bash
# 1. 赋权
chmod +x ~/.claude/skills/release-preflight/scripts/check.sh

# 2. 进入任意 git 仓库
cd ~/my-project

# 3. 调用
# 在 Claude Code 里输入:
/release-preflight v1.4.0
```

Claude 会:① 把 `v1.4.0` 填入 `$version`;② 自动跑 `git status`/`git log` 注入快照;③ 调用 `check.sh`(已预授权,不弹窗);④ 输出"可发版/阻塞项列表"。整个过程**零额外确认**、**数据全部来自现网**、**可复现**。

---

## 八、把整套机制画成一张图

```mermaid
flowchart TB
    subgraph 编写期["编写期(开发者)"]
        D1["SKILL.md<br/>frontmatter + 正文"]
        D2["scripts/*.sh<br/>可执行脚本"]
        D3["reference.md<br/>按需加载细则"]
    end
    subgraph 运行期["运行期(Claude Code)"]
        R1["/release-preflight v1.4.0"] --> R2["参数替换<br/>\$version = v1.4.0"]
        R2 --> R3["上下文注入<br/>git/gh 命令预执行"]
        R3 --> R4["allowed-tools<br/>免确认授权生效"]
        R4 --> R5["渲染结果作为<br/>单条消息注入"]
    end
    subgraph 执行期["执行期(Claude)"]
        R5 --> E1["调用 check.sh<br/>\${CLAUDE_SKILL_DIR}"]
        E1 --> E2["按需 Read reference.md"]
        E2 --> E3["输出结论"]
    end
    subgraph 生命周期["会话生命周期"]
        E3 --> L1["SKILL.md 常驻"]
        L1 --> L2{"压缩触发?"}
        L2 -->|是| L3["留前5000 token<br/>总预算25000"]
        L2 -->|否| L4["原样保留"]
    end
    D1 -.-> R1
    D2 -.-> E1
    D3 -.-> E2
```

---

## 九、设计自检清单

把你的 Skill 拿来对照这 8 条,过线了就是一个"工程级"Skill:

1. `description` 第一句话写清"做什么 + 什么时候用",前 100 字包含触发词。
2. 有副作用的 Skill(`deploy`/`commit`/`send-*`)加 `disable-model-invocation: true`。
3. 需要现网数据的,用 `!`command`` 预注入,而不是让 Claude 现场猜。
4. 脚本一律通过 `${CLAUDE_SKILL_DIR}` 引用,不用相对路径。
5. `allowed-tools` 用精确 glob 授权,**只授权不限制**;要限制用 `disallowed-tools`。
6. SKILL.md ≤ 500 行,大段细则拆到 `reference.md` 用链接引用。
7. 指令写成"持续生效的立场句",不写成"一次性步骤"。
8. 长会话里如果 Skill 行为漂移,先重新 `/invoke` 一次刷回完整内容。

---

## 延伸阅读

- **Claude Code 官方 Skills 文档**(本教程主要依据):`code.claude.com/docs/en/skills`,字段表与生命周期描述均来自此处,权威性最高。
- **Anthropic 工程博客《Equipping agents for the real world with Agent Skills》**:讲为什么"启动时只装 name+description"是上下文工程的关键设计,有助于理解成本权衡。
- **官方 `skill-development` SKILL.md 模板**(`github.com/anthropics/claude-code` 仓库 `plugins/plugin-dev/skills/skill-development/`):Anthropic 自己写 Skill 时遵循的内部规范,是最权威的"参考实现"。
- **Agent Skills 开放标准概览**(`claude.com/docs/skills/overview`):Skill 是跨工具开放标准,Claude Code 在其上扩展了调用控制、子 Agent 执行、动态注入。

---

## 训练营适用性评估

- **适合类型**:中高级开发者训练营、DevOps/平台工程训练营、"AI 辅助研发效能"企业内训。不适合纯零基础(需要先有第一个 Skill 的经验)。
- **预计时长**:讲师演示 + 跟做约 **90 分钟**(含 `release-preflight` 端到端跑通);若加入"团队治理 + skillOverrides 实操"模块,扩展到 **半天(3–4 小时)** 工作坊。
- **学员产出**:每人/每组产出一个**带参数 + 动态注入 + 脚本**的本团队专用 Skill(如 `db-migrate-check`、`api-contract-diff`、`i18n-scan`),当场可在真实仓库跑通。
- **考核点**:能否解释"allowed-tools 只授权不限制"、能否用 `${CLAUDE_SKILL_DIR}` 正确引用脚本、能否用 `!`command`` 注入至少 2 维实时数据、能否处理压缩后 Skill 失效的情况。
- **前置依赖**:建议排在"Claude Code 入门 + 基础 Skill"之后、"子 Agent 编排/MCP 开发"之前,作为"把 Skill 用到工程级"的承上启下模块。

---

## 参考来源

1. [Extend Claude with skills — Claude Code Docs](https://code.claude.com/docs/en/skills) — 官方权威文档,本文 frontmatter 字段表、`!`command`` 注入语义、`allowed-tools`/`disallowed-tools` 行为、Skill 内容生命周期(5000/25000 token 预算)均出自此处。
2. [Equipping agents for the real world with Agent Skills — Anthropic Engineering](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills) — 解释"启动时仅预载 name 与 description"的渐进披露设计动机与上下文成本权衡。
3. [Agent Skills overview — Claude Docs](https://claude.com/docs/skills/overview) — Agent Skills 作为跨工具开放标准的元数据加载与渐进披露机制说明。
4. [skill-development SKILL.md — anthropics/claude-code (GitHub)](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/skill-development/SKILL.md) — Anthropic 官方编写 Skill 的内部规范模板,本文"设计自检清单"参考其结构与字段约定。
5. [Claude Code Skills 2026 — Progressive Disclosure and How to Standardise Workflow for Engineering Teams](https://anhtu.dev/claude-code-skills-2026-progressive-disclosure-and-how-to-standardise-workflow-for-engineering-teams-1062) — 2026 年第三方深度解析,补充了三层渐进披露与团队标准化落地视角。

> [!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)
