<!-- title: 从零造日志诊断Skill -->

# 从零造日志诊断Skill：frontmatter 让技能自己决定何时触发

## 概述

本教程带你从零构建一个能自动分析应用日志、定位错误根因的 Claude Code 技能（Skill）。围绕这个真实场景，你会掌握 `SKILL.md` 的完整写法，并真正吃透官方定义的十余个 frontmatter 字段里最关键的几个——`disable-model-invocation`、`arguments`、`allowed-tools`、动态上下文注入 `!`cmd``、`context: fork`。适合写过一两个命令行小项目、想让 Claude Code 长期记住某套固定流程的开发者。学完后你能独立判断一个需求该不该做成技能、该用哪种触发方式。

## 前置条件

- 已安装 Claude Code（建议 v2.1.196 及以上，以支持 `${CLAUDE_PROJECT_DIR}` 等新特性），能跑通 `claude` 进入交互会话。
- 一个任意 git 仓库，用于测试技能；一段真实的应用日志（`.log` 文件即可，没有的话后面会教你造一段）。
- 了解 `~/.claude/` 目录结构，知道 `CLAUDE.md` 是干什么的。
- 会基本的 shell 命令（`grep`、`tail`、`wc`）。

---

## 第一步：先想清楚——这需求该不该做成 Skill

很多人第一次接触技能，会把它和 `CLAUDE.md`、自定义斜杠命令混淆。动手前花两分钟理清边界，能避免后面反复返工。

Claude Code 官方对这三者的定位非常明确：

| 载体 | 何时加载 | 适合放什么 |
| :--- | :--- | :--- |
| `CLAUDE.md` | 每次会话全量注入上下文 | 事实、约定、项目说明（"是什么"） |
| 斜杠命令/技能 | 仅在调用时按需加载 | 流程、清单、多步操作（"怎么做"） |

官方原话很关键：**"当一个 CLAUDE.md 的章节已经长成了一套操作流程，而不是一条事实时，就该把它抽成技能。"** 因为技能的正文只有在被调用时才进上下文，长篇参考资料在用不到时几乎零成本。

而且，自定义命令（`.claude/commands/deploy.md`）已经**并入技能体系**——它和 `.claude/skills/deploy/SKILL.md` 产生的都是 `/deploy`，行为一致。技能只是多了：支持目录放辅助文件、用 frontmatter 控制谁能触发、能被 Claude 自动按需加载。**所以新需求一律用技能，别再用旧的 commands 目录。**

我们的目标场景：每次线上告警，你都要把日志丢给 Claude，让它挑出报错、归类、猜根因、给修复建议。这套固定流程重复了不下十次——典型的技能候选。

## 第二步：技能放哪里，决定谁能用

Claude Code 有四个加载层级，优先级从高到低：

| 层级 | 路径 | 作用范围 |
| :--- | :--- | :--- |
| Enterprise（托管） | managed settings | 全组织 |
| Personal（个人） | `~/.claude/skills/<name>/SKILL.md` | 你所有项目 |
| Project（项目） | `.claude/skills/<name>/SKILL.md` | 仅当前项目 |
| Plugin（插件） | `<plugin>/skills/<name>/SKILL.md` | 启用该插件处 |

同名技能，**enterprise 覆盖 personal，personal 覆盖 project**。日志诊断这种个人通用工具，放 Personal 最合适。先建目录：

```bash
mkdir -p ~/.claude/skills/diag-log
```

> **常见问题**：项目级技能里的 `allowed-tools` 不会立刻生效——必须先接受工作区信任对话框（workspace trust）。所以别人仓库里检入的技能，**信任前一定要先读一遍 SKILL.md**，因为技能可以给自己授予很宽的工具权限。个人技能无此门槛。

## 第三步：写最小可用版本——只有两个字段

每个技能必须有 `SKILL.md`，分两部分：`---` 之间的 YAML frontmatter 告诉 Claude **何时**用它，下面的 markdown 是 Claude 被触发后**遵循**的指令。先把最简版写到 `~/.claude/skills/diag-log/SKILL.md`：

```yaml
---
name: diag-log
description: Diagnose the root cause of errors in an application log file. Use when the user pastes a stack trace, points at a .log file, or asks "why is it failing" or "what's the root cause".
---

## 任务
分析用户提供的日志，定位最可能的根因。

## 步骤
1. 用 Read/Grep 读取日志，提取所有 ERROR/Exception/FATAL/panic 行。
2. 按错误签名聚类，统计出现次数。
3. 取出现最频繁的一类，结合上下文推断根因。
4. 给出 1 条最可能的根因假设 + 2 条验证步骤。

## 输出格式
- 根因假设（一句话）
- 关键证据（3 条以内，带行号引用）
- 验证步骤
```

**预期结果**：保存后无需重启——Claude Code 会实时监控技能目录的文件变化（仅对 `SKILL.md` 文本生效）。在一个 git 项目里 `claude` 进会话，造一段测试日志：

```bash
cat > /tmp/app.log <<'EOF'
2026-06-30 09:01:12 INFO  server started
2026-06-30 09:02:33 ERROR NullReferenceException at UserService.java:142
2026-06-30 09:02:33 ERROR NullReferenceException at UserService.java:142
2026-06-30 09:05:11 FATAL ConnectionPool exhausted, refusing new connections
2026-06-30 09:05:12 ERROR NullReferenceException at UserService.java:142
EOF
```

然后两种方式测试。**让 Claude 自动触发**——直接问："帮我看看 /tmp/app.log 为什么报错"。**或手动触发**——输入 `/diag-log`。两种方式都应该返回结构化的根因分析。

> **常见问题：技能不触发。** 八成是 description 太含糊。Claude 决定要不要自动加载，靠的就是读 description。公式是：**"动词短语说清做什么。Use when 触发词1、触发词2。"** 把"用户会怎么说"的关键词塞进去。如果还是不行，在会话里问 "What skills are available?" 确认它出现在列表里；frontmatter YAML 写错会被静默忽略元数据，加 `--debug` 启动能看到解析错误。

## 第四步：升级成"动作型"技能——加三件套

最小版有个隐患：它会被 Claude 自动触发。想象你随口问"日志该怎么排障"，Claude 可能误判去跑你的技能。日志分析虽然读多写少，但你希望"指定文件才跑、且能传文件名进去"。加三个字段：

```yaml
---
name: diag-log
description: Diagnose the root cause of errors in an application log file. Use when the user pastes a stack trace, points at a .log file, or asks "why is it failing" or "what's the root cause".
argument-hint: <path-to-log-file>
arguments:
  - log_file
disable-model-invocation: true
---
## 任务
诊断 `$log_file` 中错误的根因。
...（步骤同上）
```

逐个解释：

- **`disable-model-invocation: true`**：安全锁。设了之后**只有你能用 `/diag-log` 手动触发**，Claude 不会自作主张加载它。官方建议：凡是有副作用（部署、提交、发邮件、调外部 API）的技能默认都加这个。它会顺带把技能的 description 从上下文里移除，省 token。
- **`arguments: [log_file]`**：声明命名位置参数。这样在正文里就能用 `$log_file` 引用，比裸 `$ARGUMENTS` 更清晰。运行 `/diag-log /tmp/app.log` 时，`$log_file` 自动展开成 `/tmp/app.log`。
- **`argument-hint`**：纯体验优化。打 `/diag-log` 时自动补全会显示 `/diag-log <path-to-log-file>`，告诉使用者要传什么。`<>` 表示必填、`[]` 表示可选，沿用 man 手册惯例。

这两个布尔字段是新人最容易搞混的一对，画张表记牢：

| frontmatter 设置 | 你能调用 | Claude 能自动调用 | description 是否在上下文 |
| :--- | :--- | :--- | :--- |
| 默认 | 能 | 能 | 在；调用时加载全文 |
| `disable-model-invocation: true` | 能 | **不能** | **不在**；你调用时才加载 |
| `user-invocable: false` | **不能** | 能 | 在；调用时加载 |

> **常见问题：两个都设了技能就消失了。** `disable-model-invocation: true` + `user-invocable: false` 同时设，等于谁都不能触发，技能彻底不可达——别这么干。

## 第五步：预批准工具，告别逐次确认

现在技能跑起来，Claude 每读一次日志、每 grep 一次都要弹权限确认，流畅度很差。用 `allowed-tools` 把这些工具预先放行：

```yaml
allowed-tools:
  - Read
  - Grep
  - Bash(grep:*)
  - Bash(awk:*)
  - Bash(tail:*)
  - Bash(wc:*)
```

关键认知：**`allowed-tools` 是"免确认"，不是"限制"。** 它让列出的工具在技能激活期间免提示，但其他工具照样能用，仍受你全局权限规则约束。语法和 `settings.json` 的 `permissions.allow` 一致，所以 `Bash(grep:*)` 这种带前缀的细粒度规则也支持。

另一个字段 `disallowed-tools` 才是真正的"限制"——它在技能激活期间把指定工具从 Claude 可用池里**移除**，比如给一个后台循环技能配 `disallowed-tools: [AskUserQuestion]` 让它绝不打断你。注意：**这个限制在你发下一条消息时就清除了**，是临时的。

> **常见问题：写了 allowed-tools 还是弹窗。** 两种可能：一是项目级技能没接受工作区信任；二是权限太宽（如裸 `Bash`）被全局 deny 规则挡住——**全局 deny 永远赢过 allowed-tools**，这是安全特性。原则：能窄则窄，`Bash(git log:*)` 而不是 `Bash`。

## 第六步：动态上下文注入，让技能自带实时数据

这是技能体系里最趁手的特性之一。在正文里写 `!`命令``（行首或空格后的 `!`），Claude Code 会在把技能正文发给 Claude **之前**先执行它，把输出原地替换进去。等于让指令"自带实测数据"。

官方的 `summarize-changes` 示例就是用 `!`git diff HEAD`` 把当前 diff 拉进提示词，这样 Claude 看到的是真实工作区，而不是靠猜。我们给 `diag-log` 加上：调用时自动把日志里的报错行和体量统计注入进来。

```markdown
## 日志实测信号
```!
echo "## 文件体量"
wc -l "$log_file" 2>/dev/null
echo
echo "## 头尾时间戳（判断故障窗口）"
head -n 1 "$log_file" 2>/dev/null
tail -n 1 "$log_file" 2>/dev/null
echo
echo "## 报错签名（按频次）"
grep -nE 'ERROR|Exception|FATAL|panic|Traceback' "$log_file" 2>/dev/null \
  | sort | uniq -c | sort -rn | head -n 10
```

## 任务
上面的信号已从真实日志实时抽取。基于它们定位 `$log_file` 的根因……
```

**工作原理**：参数替换（`$log_file` → 实际路径）和命令注入（`!`块执行）在**同一次对原文的替换扫描**中完成；命令输出作为纯文本插入，不会被二次扫描，所以一个命令不能"吐出"另一个 `!` 占位符。Claude 最终看到的是已经填好数据的完整提示。

行内写法 `!`cmd`` 仅在 `!` 位于行首或紧跟空格时才被识别；`KEY=!`cmd`` 这种紧跟字符的写法会被当字面量。多行命令用 ` ```! ` 围栏。企业可以用 `"disableSkillShellExecution": true` 设置在托管层把这套机制关掉（仅对用户/项目/插件来源生效，bundled 和 managed 技能不受影响）。

> **常见问题：注入命令失败导致输出难看。** 注入的命令每次调用都跑，失败的命令会往提示词里灌噪声，模型可能误读。务必加 `2>/dev/null` 和 `|| echo "(无匹配)"` 兜底。

## 第七步（可选）：大日志用 fork 隔离上下文

如果日志几十 MB、要读上百个文件做交叉分析，`context: fork` 能让技能在一个**全新子代理上下文**里跑，不污染主会话：

```yaml
---
name: diag-log-deep
description: Deep multi-log root-cause analysis for large incidents. ...
context: fork
agent: Explore
allowed-tools:
  - Grep
  - Glob
  - Read
---
对比 $ARGUMENTS 指定的多个日志，交叉定位故障起源……
```

`context: fork` 时，正文成为驱动子代理的 prompt，它**看不到你的对话历史**。所以 fork 只适合"任务明确的技能"——若是纯约定类技能（"遵循这些 API 风格"），子代理收到约定却没有可执行任务，会空手而归。`agent` 字段决定执行环境：`Explore`（只读搜索，便宜）、`Plan`（架构设计）、`general-purpose`（可写，贵）。错配会静默失败，务必让 agent 类型匹配技能的职责。

## 第八步：验证与迭代——能触发不等于好用

看到技能被触发，只说明 Claude 找到了它，**不代表它干对了**。官方强调要分开测两件事：该触发的 prompt 是否触发了；触发了输出是否符合预期。方法是**基线对照**——收集几个真实 prompt，每个都在**全新会话**里分别跑"有技能"和"禁用技能"两种，对比结果。全新会话很关键，因为你写技能时残留的上下文会掩盖指令的漏洞。

手动迭代太累时，用官方的 `skill-creator` 插件自动化这套循环：

```text
/plugin install skill-creator@claude-plugins-official
/reload-plugins
```

装好后让它 `evaluate my diag-log skill with skill-creator`，它会：写测试用例存进 `evals/evals.json`、每个用例起一个隔离子代理跑、按断言打分写 `grading.json`、把"有/无技能"的通过率/耗时/token 汇总成 `benchmark.json`，还能跑两版技能的盲测 A/B、自动生成 should/should-not 触发 prompt 来调 description。

当技能太多导致描述被截断而漏触发时，跑 `/doctor` 看哪些技能描述被缩短了；技能列表的字符预算默认是模型上下文窗口的 1%，可用 `skillListingBudgetFraction`（如 `0.02`）调高，或把低优先级技能在 `skillOverrides` 里设成 `"name-only"` 省描述。单条 description + when_to_use 上限 1536 字符，**把最关键的用例放最前面**。

---

## 完整实战案例：diag-log 终版

把前面几步拼起来，得到一个可直接用的生产级技能。目录结构：

```text
~/.claude/skills/diag-log/
├── SKILL.md          # 主指令（必需）
└── scripts/
    └── extract.sh    # 可选：把重统计逻辑固化成脚本
```

`SKILL.md`：

```yaml
---
name: diag-log
description: Diagnose the root cause of errors in an application log file. Use when the user pastes a stack trace, points at a .log file, or asks "why is it failing", "what's the root cause", or "分析日志/排查报错".
argument-hint: <path-to-log-file>
arguments:
  - log_file
disable-model-invocation: true
allowed-tools:
  - Read
  - Grep
  - Bash(grep:*)
  - Bash(awk:*)
  - Bash(tail:*)
  - Bash(wc:*)
  - Bash(bash:*)
---

## 实时信号（从 $log_file 抽取）
```!
echo "## 体量"; wc -l "$log_file" 2>/dev/null
echo "## 时间窗口"; head -n1 "$log_file" 2>/dev/null; tail -n1 "$log_file" 2>/dev/null
echo "## 报错签名 Top10"
grep -nE 'ERROR|Exception|FATAL|panic|Traceback' "$log_file" 2>/dev/null \
  | sort | uniq -c | sort -rn | head -n10 || echo "(未匹配到错误模式)"
```

## 任务
基于上面的实时信号，对 `$log_file` 做根因诊断。若需要更细的行级上下文，再调用 Grep/Read；也可运行预置统计脚本：
```bash
bash ${CLAUDE_SKILL_DIR}/scripts/extract.sh "$log_file"
```

## 步骤
1. 从报错签名里挑出频次最高的一类。
2. 用 Grep 取该签名的首末出现及前后各 5 行上下文。
3. 推断根因，区分"代码缺陷"与"环境/依赖问题"。
4. 给出可执行的验证步骤。

## 输出
- **根因假设**（一句话，标注置信度 高/中/低）
- **关键证据**（≤3 条，带行号）
- **验证步骤**（≤3 步，可复制的命令）
- 若信号为空，直接说"日志中未发现已知错误模式"。
```

`scripts/extract.sh`（把统计逻辑固化，避免 Claude 每次重写命令）：

```bash
#!/usr/bin/env bash
# 用法: extract.sh <logfile>
set -euo pipefail
log="${1:?用法: extract.sh <logfile>}"
[ -f "$log" ] || { echo "文件不存在: $log"; exit 1; }

echo "== 各级别计数 =="
grep -oE '(ERROR|WARN|FATAL|Exception|panic)' "$log" 2>/dev/null \
  | sort | uniq -c | sort -rn || true

echo; echo "== 异常栈头（去重）=="
grep -E 'Exception|Error|panic' "$log" 2>/dev/null \
  | sed -E 's/^[0-9: -]+//' | sort -u | head -n 20 || true

echo; echo "== 每分钟错误密度 =="
grep -E 'ERROR|FATAL' "$log" 2>/dev/null \
  | awk '{print substr($1,1,10)"T"substr($2,1,5)}' \
  | sort | uniq -c | sort -rn | head -n 10 || true
```

跑 `/diag-log /tmp/app.log`，预期输出形如：

```text
根因假设（置信度：高）：UserService.java:142 的 NullReferenceException 反复触发，
疑似某用户记录字段为 null 未做空值判断，错误风暴耗尽连接池引发 FATAL。

关键证据：
- L2/L3/L5 三次同一行同一异常（/tmp/app.log:142）
- L4 ConnectionPool exhausted 紧随其后

验证步骤：
1. grep -n 'userId' UserService.java:142 附近定位入参
2. 查最近发布记录是否有该行改动
3. 在测试环境用 null 入参复现
```

`${CLAUDE_SKILL_DIR}` 会展开成技能所在目录，无论技能装在个人级、项目级还是插件级都能正确解析——这是技能"可移植"的关键。

---

## 全流程架构

下面的流程图把"从需求到可用技能"的决策链串起来，遇到新需求时照着走一遍：

```mermaid
flowchart TD
    A[重复的固定流程] --> B{是事实还是流程?}
    B -->|事实/约定| C[放进 CLAUDE.md]
    B -->|多步操作/清单| D[做成 Skill]

    D --> E[选层级]
    E -->|个人通用| F[~/.claude/skills]
    E -->|团队共享| G[.claude/skills 检入git]
    E -->|跨项目分发| H[打包成 Plugin]

    F --> I[写 SKILL.md]
    G --> I
    H --> I

    I --> J{有副作用?}
    J -->|是| K[disable-model-invocation: true<br/>只手动触发]
    J -->|否| L{要免确认?}
    L -->|是| M[加 allowed-tools]
    L -->|否| N[保持默认自动触发]

    K --> O[需要实时数据?]
    M --> O
    N --> O
    O -->|是| P[用 !cmd 动态注入]
    O -->|否| Q[发布]

    P --> Q
    Q --> R[skill-creator 跑 eval]
    R --> S{通过率达标?}
    S -->|否| I
    S -->|是| T[上线]
```

技能被调用后的上下文生命周期也值得记住，决定你怎么写指令：

```mermaid
sequenceDiagram
    participant U as 用户/Claude
    participant CC as Claude Code
    participant S as SKILL.md
    participant M as 模型

    U->>CC: 输入 prompt 或 /diag-log file
    CC->>CC: 参数替换 ($log_file) + !cmd 注入
    CC->>S: 渲染正文（数据已注入）
    S->>M: 作为单条消息进入对话
    Note over M: 正文此后常驻该会话
    M->>M: 后续轮次不再重读文件
    Note over CC: 自动压缩时<br/>每个技能保留前 5000 token<br/>合计预算 25000 token<br/>从最近调用的开始保留
```

核心含义：技能正文是**常驻指令**而非一次性步骤。压缩后会按"最近优先"重新挂载，所以把最重要的指令写在正文靠前位置。

---

## 常见问题速查

| 现象 | 原因 | 处理 |
| :--- | :--- | :--- |
| 技能完全不触发 | description 太含糊，或 YAML 格式错被静默忽略 | 加触发词；`--debug` 启动看解析错误 |
| 技能乱触发 | description 过宽 | 收窄 description，或设 `disable-model-invocation: true` 只手动触发 |
| 描述被截断导致漏触发 | 技能太多超出字符预算 | `/doctor` 查；调高 `skillListingBudgetFraction`；低优先级设 `"name-only"` |
| allowed-tools 仍弹窗 | 项目级未接受信任，或被全局 deny 挡住 | 接受信任；记住全局 deny 永远赢 |
| 改了 hooks/scripts 不生效 | 实时检测只覆盖 SKILL.md 文本 | `/reload-plugins` 重载 |
| 嵌套技能同名 | monorepo 多包都有 deploy | 用目录限定名 `/apps/web:deploy` |

---

## 延伸阅读

- **官方技能文档**（code.claude.com/docs/en/skills）：frontmatter 全字段、加载层级、动态注入的权威出处，本文所有字段行为均以此为准。
- **Frontmatter 全字段详解**（allahabadi.dev）：15+ 字段的逐个剖析，含"我想做 X 该用哪个字段"决策助手和四种真实模式（安全部署/沉默知识/大型研究/廉价工具）。
- **agentskills.io 评估指南**：`evals.json` 格式与迭代工作流，配合 `skill-creator` 插件做量化评测。
- **官方 skill-creator 插件**（github.com/anthropics/claude-plugins-official）：eval/benchmark/版本 A/B/description 调优的自动化闭环。
- **Subagents 文档**：深入 `context: fork` + `agent` 的子代理执行模型，理解技能如何在隔离上下文里跑。

## 训练营适用性评估

- **适合训练营类型**：面向有 1–3 年经验的工程师的"AI 工程化"进阶营、DevOps/SRE 效能营、企业内部 Claude Code 推广营。不适合纯零基础营（需先具备第三步的前置条件）。
- **建议教学时长**：理论讲解 30 分钟 + 跟练 60 分钟 + 自选场景改造 30 分钟，合计约 2 小时。可作为"技能体系"模块的第一节课，后续接 plugin 打包、skill-creator 评测、hooks 联动。
- **学员产出物**：每人交付一个贴合自己日常工作的技能（排障/发版/审查/报告皆可），带 frontmatter 决策说明和至少一组 baseline 对照测试，直接可用、可分享。
- **延伸挑战**：把 diag-log 升级成 `context: fork` 版做跨日志关联；或加 `paths: ["**/*.log"]` + `user-invocable: false`，做成"一编辑日志就自动加载诊断约定"的沉默情境技能。

## 参考来源

1. [Extend Claude with skills — Claude Code 官方文档](https://code.claude.com/docs/en/skills) — 技能体系权威规范：SKILL.md 结构、frontmatter 全字段、加载层级、动态注入、skill-creator 评测闭环。
2. [Claude Code Skill Frontmatter: Every YAML Option Explained](https://allahabadi.dev/blogs/ai/claude-code-skills-frontmatter-complete-guide/) — 逐字段详解 + 决策助手 + 四种真实技能模式（2026 年 4 月更新）。
3. [How to create custom skills — Claude 帮助中心](https://support.claude.com/en/articles/12512198-how-to-create-custom-skills) — 官方创建技能的入门指引，覆盖 Claude Code 与 API 两条路径。
4. [improving skill-creator 博客](https://claude.com/blog/improving-skill-creator-test-measure-and-refine-agent-skills) — skill-creator 插件的 benchmark 与版本对比机制背景说明。
5. [Evaluating skill output quality — agentskills.io](https://agentskills.io/skill-creation/evaluating-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学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
