首页 › tutorials › 从零造日志诊断Skill

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

概述

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

前置条件


第一步:先想清楚——这需求该不该做成 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 最合适。先建目录:

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:

---
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 进会话,造一段测试日志:

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 可能误判去跑你的技能。日志分析虽然读多写少,但你希望"指定文件才跑、且能传文件名进去"。加三个字段:

---
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` 中错误的根因。
...(步骤同上)

逐个解释:

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

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

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

第五步:预批准工具,告别逐次确认

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

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` 加上:调用时自动把日志里的报错行和体量统计注入进来。

## 日志实测信号
```!
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 插件自动化这套循环:

/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 终版

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

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

SKILL.md:

---
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 ${CLAUDE_SKILL_DIR}/scripts/extract.sh "$log_file"

步骤

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

输出

`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,预期输出形如:

根因假设(置信度:高):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} 会展开成技能所在目录,无论技能装在个人级、项目级还是插件级都能正确解析——这是技能"可移植"的关键。


全流程架构

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

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[上线]

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

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

延伸阅读

训练营适用性评估

参考来源

  1. Extend Claude with skills — Claude Code 官方文档 — 技能体系权威规范:SKILL.md 结构、frontmatter 全字段、加载层级、动态注入、skill-creator 评测闭环。
  2. Claude Code Skill Frontmatter: Every YAML Option Explained — 逐字段详解 + 决策助手 + 四种真实技能模式(2026 年 4 月更新)。
  3. How to create custom skills — Claude 帮助中心 — 官方创建技能的入门指引,覆盖 Claude Code 与 API 两条路径。
  4. improving skill-creator 博客 — skill-creator 插件的 benchmark 与版本对比机制背景说明。
  5. Evaluating skill output quality — agentskills.io — 跨厂商技能标准的评估文件格式与迭代工作流。

[!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学通信知识星球海报-扫码加入