首页 › tutorials › 触发优先:从零造精准Skill

触发优先:从零造一个"不被误触"的脱敏 Skill

概述

本教程带你以"触发准确性"为第一目标,从零构建一个敏感数据(PII)脱敏 Skill。你将掌握 SKILL.md frontmatter 的写法、description 触发工程的四条原则、以及一套用回归测试量化触发命中率的迭代方法。适合已经能写出能跑的 Skill、但总遇到"该触发不触发、不该触发乱触发"困扰的开发者。载体场景选了 PII 脱敏——它天然与"日志分析""安全审查""数据库查询"等 Skill 边界模糊,是最能体现触发工程的练手题材。

前置条件

为什么"触发"是 Skill 的第一道关

很多人写 Skill 的顺序是:先想功能 → 写逻辑 → 最后随便填一句 description。结果就是 Skill 能力很强,却几乎从不被调用,或者被错误地塞进毫不相干的对话里。

Claude Code 的 Skill 系统基于渐进式披露(progressive disclosure):所有 SKILL.md 的 frontmatter 常驻在上下文里,但正文和脚本只在被判定为"相关"时才加载。换句话说——name 和 description 是你的 Skill 唯一长期暴露在模型决策视野里的信息。它写得好不好,直接决定模型是否把你的 Skill 纳入候选;正文写得再精彩,触发不了就等于不存在。

所以本教程提出一个反直觉的构建顺序——触发优先:先画边界、再写描述、然后才是逻辑,最后用回归测试反复校准描述。

第 1 步:先画"触发矩阵",再动笔

写任何代码之前,拿出一张表,把"该触发"和"不该触发"的场景列清楚。这是后续 description 工程的依据。对 PII 脱敏 Skill,矩阵大致如下:

类型 场景示例 期望
正例 "帮我把这份日志里的手机号打码" ✅ 触发
正例 "脱敏一下这个 CSV 里的身份证和邮箱" ✅ 触发
正例 "这串数据要去标识化再贴到 issue 里" ✅ 触发
反例 "日志里这个错误怎么排查" ❌ 不触发(排查不是脱敏)
反例 "帮我查数据库里这张表的结构" ❌ 不触发(查询不是脱敏)
反例 "审计这段代码有没有安全漏洞" ❌ 不触发(审查不是脱敏)
边界 "把密钥从配置里删掉" ❌ 不触发(这是删除机密,不是 PII 脱敏)

注意右下角那几条反例——它们正是这个 Skill 最容易被误触发的场景。反例的价值远大于正例,因为它们定义了 Skill 的"围墙"。

第 2 步:搭建 Skill 骨架与必填 frontmatter

Skill 是一个文件夹,入口文件必须叫 SKILL.md。在 pii-skill-demo/ 下创建项目级 Skill:

pii-skill-demo/
└── .claude/
    └── skills/
        └── pii-masking/
            ├── SKILL.md
            ├── scripts/
            │   └── mask.py
            └── references/
                └── regex-rules.md

SKILL.md 的开头是 YAML frontmatter。frontmatter 只有两个必填字段:name 和 description。先写一个能跑的最小版本,下一节再精修:

---
name: pii-masking
description: Mask PII in text or files. Use when the user asks to mask, redact, or de-identify phone numbers, emails, ID cards, or bank cards in logs, CSVs, or pasted text.
---

# PII Masking

When invoked, run `scripts/mask.py` on the target text or file path.

字段说明:

第 3 步:description 触发工程四原则

这是本教程的核心。一条合格的 description 要同时满足四条:

原则 1:主动语态,避免第三人称被动。 不要写 "This skill handles PII masking"(含糊、像产品介绍),而是写 "Mask PII… Use when the user asks to mask…"(给出明确的触发动作和场景信号)。

原则 2:正向触发词清单。 把用户可能说出的关键词密集铺进描述:mask、redact、de-identify、anonymize、打码、脱敏、去标识化。模型做的是模式匹配,覆盖同义词能提高召回。

原则 3:负向排除。 显式列出"何时不触发"。这是把误触发率压下去的关键手段:Do NOT trigger for log debugging, DB queries, secret deletion, or general code review.

原则 4:与相邻 Skill 差异化。 如果你环境里已有"安全审查 Skill",就别在脱敏描述里用"audit / review"这类词,否则两者会争抢同一个请求。

按四原则重写后:

description: Mask or redact personally identifiable information (PII) — phone numbers, email addresses, ID-card numbers, bank card numbers — in files or pasted text. Use this skill when the user asks to mask, redact, anonymize, de-identify, "打码", or "脱敏" such data before sharing, pasting into issues, or committing. Do NOT trigger for log debugging, database queries, secret/key deletion, or general security code review — those belong to other skills.

第 4 步:用可选字段给触发加"保险"

frontmatter 还有一批可选字段,其中两个对触发控制尤其重要:

---
name: pii-masking
description: <上面精修后的描述>
allowed-tools:
  - Bash
  - Read
---

说明:本文聚焦触发工程,未展开 metadata(分类/版本等元信息)、license、version 等字段,它们影响分发与发现,但不直接影响运行时触发决策。

第 5 步:写脱敏逻辑(scripts/ 工程化)

把"重活"放进 scripts/mask.py,让 SKILL.md 正文只负责说明"何时调用脚本、怎么传参"——这正是渐进式披露的精髓:正文薄、脚本厚,避免把一堆正则塞进常驻上下文。

scripts/mask.py(可直接运行的正则脱敏器):

#!/usr/bin/env python3
"""Mask common PII types in text from a file or stdin."""
import re, sys

RULES = [
    # 中国大陆手机号: 1[3-9]xxxxxxxxx
    (re.compile(r'(?<!\d)1[3-9]\d{9}(?!\d)'), lambda m: m.group(0)[:3] + '****' + m.group(0)[-4:]),
    # 邮箱
    (re.compile(r'[\w.+-]+@[\w-]+\.[\w.-]+'), lambda m: m.group(0)[0] + '***@' + m.group(0).split('@')[-1]),
    # 18位身份证号
    (re.compile(r'(?<!\d)\d{17}[\dXx](?!\d)'), lambda m: m.group(0)[:6] + '********' + m.group(0)[-4:]),
    # 银行卡号(16-19位连续数字)
    (re.compile(r'(?<!\d)\d{16,19}(?!\d)'), lambda m: m.group(0)[:4] + ' **** **** ' + m.group(0)[-4:]),
]

def mask(text: str) -> str:
    for pat, repl in RULES:
        text = pat.sub(repl, text)
    return text

def main():
    # 从文件或 stdin 读取
    if len(sys.argv) > 1:
        with open(sys.argv[1], encoding='utf-8') as f:
            src = f.read()
    else:
        src = sys.stdin.read()
    sys.stdout.write(mask(src))

if __name__ == '__main__':
    main()

SKILL.md 正文写明调用方式(保持简短):

# PII Masking

When invoked:

1. Determine the input: either a file path the user gave, or pasted text.
2. Run `python3 scripts/mask.py <path>` for a file, or pipe text in via stdin.
3. Return the masked result; never echo the original unmasked data back.

For the exact regex rules and how each PII type is masked, read `references/regex-rules.md`.

把每条正则的"为什么这么写"放进 references/regex-rules.md——它只在模型需要解释规则时才被加载。

第 6 步:触发回归测试——把"祈祷"变成"数据"

描述写完后,不要凭感觉说"应该能触发"。把第 1 步的触发矩阵转成一组测试提示词,逐条手测,记录结果:

✅ 正例(应触发)
  [T1] "把这行日志里手机号打码"
  [T2] "脱敏一下这段 CSV 里的邮箱和身份证"
  [T3] "去标识化后再贴进工单"

❌ 反例(不应触发)
  [F1] "这条日志报错啥原因"
  [F2] "查一下 users 表有哪些字段"
  [F3] "审计这段 SQL 有没有注入风险"
  [F4] "把 .env 里的密钥删掉"

跑一遍,统计两个核心指标:

如果 skill-creator 这类工具可用,还能把这套提示词喂给它的 eval 机制做批量打分和方差分析,把触发质量量化成可追踪的数字,而不是"祈祷它工作"。

第 7 步:用回归数据反推描述调参

假设回归测试发现 F4(删密钥)被误触发了——模型看到"删掉数据"就联想到"脱敏"。这时候只改 description,强化边界:

# 调整前
... Do NOT trigger for log debugging, database queries, secret/key deletion ...

# 调整后(增加显式区分:删除 vs 打码)
... Do NOT trigger for log debugging, database queries, or removing/deleting
secrets and keys — masking keeps the row but hides the value, deletion
removes it entirely. Those are different operations handled elsewhere.

改完再跑一次回归,看误触发是否消失。触发优先构建法本质上是一个闭环:画边界 → 写描述 → 跑回归 → 改描述,直到两个指标都达标。

整体工作流程

下面这张流程图把"触发优先构建法"的迭代闭环串起来:

flowchart TD
    A[画触发矩阵<br/>正例 + 反例 + 边界] --> B[写必填 frontmatter<br/>name + description 最小版]
    B --> C[description 四原则精修<br/>主动语态/正向词/负向排除/差异化]
    C --> D[可选字段加保险<br/>allowed-tools / disable-model-invocation]
    D --> E[写脱敏逻辑<br/>scripts/mask.py + references/]
    E --> F[触发回归测试<br/>召回率 + 精确率]
    F --> G{指标达标?}
    G -->|否| H[用数据反推<br/>只改 description]
    H --> C
    G -->|是| I[发布/共享 Skill]

常见问题与诊断清单

症状:写了 Skill 但从不被触发。 - 诊断:description 是否太短或太抽象?是否漏了用户真实会说出的同义词(含中文)?尝试把正向触发词补全。 - 诊断:name 是否和别的 Skill 重名导致冲突?

症状:频繁被无关请求误触发。 - 诊断:description 里有没有用到"review/audit/handle"这类与其他 Skill 撞车的宽泛词? - 诊断:是否缺负向排除句?补上 Do NOT trigger for ...。 - 兜底:若仍压不住,设 disable-model-invocation: true,降级为纯手动 /skill 调用。

症状:触发不稳定,时灵时不灵。 - 诊断:边界场景是否定义清楚?把模糊反例加入回归集,固化边界。 - 诊断:脚本依赖是否齐全(Python 版本、文件路径)?触发对了但执行失败,也会让 Skill 看起来"时好时坏"。

实战案例完整代码

把前面所有片段拼起来,一个最小可运行的脱敏 Skill 就是这三件事:

  1. .claude/skills/pii-masking/SKILL.md:精修后的 frontmatter + 简短正文(见第 3、5 步)
  2. .claude/skills/pii-masking/scripts/mask.py:第 5 步的脱敏脚本
  3. .claude/skills/pii-masking/references/regex-rules.md:每条正则的说明文档

验证它工作:

# 直接验证脚本逻辑
echo "联系人 13800138000, 邮箱 [email protected], 身份证 110101199001011234" | python3 .claude/skills/pii-masking/scripts/mask.py
# 期望: 联系人 138****8000, 邮箱 a***@test.com, 身份证 110101********1234

# 在 Claude Code 里验证触发
# 粘贴: "帮我把这份日志里的手机号和邮箱打码"
# 期望: 自动加载 pii-masking 并调用脚本

训练营适用性评估

延伸阅读

参考来源

  1. Extend Claude with skills — Claude Code Docs — 官方 Skill 创建、管理与共享权威文档
  2. SKILL.md frontmatter reference — ClaudSkills — 必填/可选字段与 disable-model-invocation 等触发控制字段解析
  3. Frontmatter and Skill Triggering — DeepWiki — frontmatter 如何影响 Skill 触发决策的机制说明
  4. Build a Claude Skill: YAML Frontmatter, SKILL.md Instructions, and Testing — sjramblings — 从 frontmatter 写到测试的实战教程
  5. Claude Code Skill Frontmatter: Every YAML Option Explained — Allahabani — 全部 frontmatter 字段的逐项解释与示例

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