如何写一个好的skill 让你的效率加倍

这里是沃基 2026-04-08 17:09 1


本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容:



  • 我的帖子已经打上 开源推广 标签:

  • 我的开源项目完整开源,无未开源部分:

  • 我的开源项目已链接认可 LINUX DO 社区:

  • 我帖子内的项目介绍,AI生成、润色内容部分已截图发出:

  • 以上选择我承诺是永久有效的,接受社区和佬友监督:


以下为项目介绍正文内容,AI生成、润色内容已使用截图方式发出




本文已从[开发调优]模块迁移到[文档共建模块],大家可以自行在文档后面进行补充调整


如何写一个好的skill


帖子最下面有具体的项目示例,欢迎大家一起提交pr修改




阅读路径




  • 只想知道"怎么开始" → 读第一章(单文件 skill)→ 第二章(文件夹布局),够用




  • 想做到工程级可维护 → 按顺序读 1→16,每章都是在前面基础上加一层




全文核心是三句话:




  1. 结构服务于内容,




  2. 激活优于存储




  3. 结构可复用,内容禁止预制






前言:skill是什么




agent的流程,skill的作用



一次 Agent 的执行,背后藏着上图这么多环节。但这套默认流程,不一定贴合我们各自的工作场景——于是 Skill 应运而生。


好奇的佬友可能会冒出一个疑问(真的有吗?):这个领域里意思相近的词其实不少,比如 MCP、RAG,它们和 Skill 到底是什么关系?


其实很简单——这三者的根本目的,都是为了优化上图的执行流程,只是各管一段:

RAG 提供了外挂知识库的能力,让 Agent 有据可依、思考更有深度;

MCP 赋予了 Agent 探索外部世界的能力;

Skill,则更像整个 Agent 流程的大脑,决定它"该怎么做"。


而说到 Skill 的前身,得从去年大火的 Prompt(提示词) 讲起。提示词的作用是什么?说白了,就是规范 Agent 的行为,让它按照特定的流程或规则去完成任务。但是后面我们发现一个提示词适用的场景太单薄了,所以我们将提示词,和他对应的规则(其他md),脚本(sh或者py脚本),各种文档一起打包成一个"能力",这就是skill了.


而skill比起来prompt最优的地方在于他的加载方式: 按需加载,agent不会每一次都会把skill的全部内容都塞到上下文里面,而是会读skill的description,然后命中,然后才会按需去读,这样不但可以精简上下文,也可以省钱(这才是最重要的!!!)



一、skill 的最初形态:一个文件就够了



在我们聊"上下文"、“薄壳”、"Harness"这些概念之前,先看看 skill 最开始的样子 —— 一个 markdown 文件,几条行为准则,仅此而已。


参考项目:forrestchang/andrej-karpathy-skills(基于 Andrej Karpathy 总结的 LLM 编码反模式)





单文件 skill 长什么样

Andrej Karpathy的整个项目就 6 个文件、859 行。核心是一个 67 行的 SKILL.md,里面只有四条行为准则:



目录结构就这么简单:


skills/karpathy-guidelines/
└── SKILL.md ← 全部内容都在这里


  • 没有 rules/,因为四条准则放在 SKILL.md 里就够了

  • 没有 workflows/,因为这个 skill 不绑定特定任务流程

  • 没有 references/,因为一个外部链接就够了


这不是偷懒,是对架构复杂度的准确判断:结构服务于内容,而不是用结构撑完整性。


这个项目最重要的地方


设计一:原则 + 检验句,而不是原则 + 解释


大多数 skill 里面的规则是这样写的:



保持简洁。 写解决问题所需的最少代码。



Karpathy Skills 的写法是:



简洁优先。 解决问题所需的最少代码,不写投机性的功能。

问自己:“资深工程师会觉得这过度复杂吗?” 如果是,就简化。



再看一条:



精准修改。 只改必须改的,只清理你自己造成的乱。

检验标准:每一行改动都应该能直接追溯到用户的请求。



区别在哪?前者是声明 —— 告诉 Agent"应该这样"。后者是检验句 —— 给 Agent 一个可以在执行后自我验证的具体问题。


Agent 在生成代码之后,可以真的去问自己"每一行改动都能追溯到用户的请求吗",然后根据答案决定要不要回滚某些行。而 “Be Simple” 这种声明,Agent 只能在生成前抽象地"记住",生成后根本没有钩子去触发验证。


可以直接借鉴的格式:


## 原则名称

一句话描述原则。

检验:[一个可以跑的命令 / 一个可以问自己的具体问题]

设计二:代码行为层面的 ^-^/^-^ 对比


Karpathy Skills 的 EXAMPLES.md 有 522 行,全部是代码级 before/after。关键在于 —— 展示的不是明显的错误(内存泄漏、SQL 注入、死循环),而是看起来合理但时机错了的改动


请求是"修复空 email 导致的崩溃":

❌ Agent 实际会做的:
def validate_user(user_data):
+ """Validate user data.""" # 加了 docstring(没被要求)
+ email = user_data.get('email', '').strip()
- if not user_data.get('email'):
+ if not email:
raise ValueError("Email required")
- if '@' not in user_data['email']:
+ if '@' not in email or '.' not in email.split('@')[1]: # "顺手"加强校验
raise ValueError("Invalid email")
+ if len(username) < 3: # 没被要求的 username 校验
+ raise ValueError("Username too short")

✅ 应该只做的改动:
def validate_user(user_data):
- if not user_data.get('email'):
+ email = user_data.get('email', '')
+ if not email or not email.strip():
raise ValueError("Email required")
# 其他代码保持原样

docstring、更严格的邮箱校验、username 长度限制 —— 这种过度设计在agent的开发过程中特别多见。但在一个只需要修 bug 的 PR 里加进去,就是 Surgical Changes 原则的违反。


这类反模式 Agent 最容易踩,因为"看起来都对"。通用的"禁止写烂代码"提示根本拦不住它,因为Agent不知道什么样子是烂代码,必须用真实的例子提醒他。


设计三:按需引入结构复杂度


设计 skill 结构前先回答三个问题:




























问题 如果答案是… 结论
这个 skill 有多少个不同主题的内容? 少于 3 个 单文件够了
绑定了多少种任务流程? 没有 不需要 workflows/
预期会随项目演进持续更新吗? 不会 不需要自演化机制

三个都是"否" → 单文件 skill 就是最优解。只有出现"是"的时候,才引入对应的目录层级。


按需引入复杂度,不要一开始就摆全套架构。 一上来就搭完整目录(rules/ + workflows/ + references/ + 多 harness 薄壳……)会制造"这个项目很完整"的错觉,但每个子目录里只有一两行占位符,维护成本反而更高。



Karpathy Skills 是一个静态的行为提示,不是自我更新的知识系统。它从一开始就知道自己要做什么,所以结构刚好够用。


但一旦出现以下信号之一,单文件就撑不住了:



  • 多主题:SKILL.md 开始出现"### X 相关"、"### Y 相关"的分节

  • 任务路由:不同类型的任务需要读不同的规则(加 Controller 和修 bug 读的不是同一套)

  • 需要沉淀教训:同样的坑第二次踩,但没有地方记录它

  • 多人协作 / 多项目复用:规则开始有变体,需要分文件管理

    这些信号出现 → 就该进入文件夹化 skill 的阶段。




二、将 Skill 文件夹化



当单文件撑不住——主题 ≥ 3、任务路由复杂、需要沉淀教训——skill 就该从一个文件裂成一个文件夹。2000 行的 SKILL.md 不是"内容丰富",是 Agent 每次都要读完整本书。





Skill 不只是一个 Markdown 文件

Skill 是一个文件夹,可以包含 Markdown、脚本、资产、数据、配置等。Agent 会自主发现和使用其中的所有内容。


把它想成一个小型项目,而不是一份文档。


^-^ 这个非常重要。

如果是一个很小的 skill,用单文件没问题(见第一章的 Karpathy 例子);但是博主之前公司有一个 2000 字的md,AI 根本读不到……而且拓展性极差,要了博主半条老命,这也是这篇文章和项目的初衷。


skills/<name>/
├── SKILL.md # 入口:路由表 + 优先级
├── rules/ # 长期约束
├── workflows/ # 步骤流程
├── references/ # 背景资料:架构、坑点、索引
│ └── gotchas.md # 已知的坑(通常是最高价值内容)
├── docs/ # 可选:提示词、报告
└── scripts/ # 可选:辅助脚本、脚手架工具

Skill 文件夹能放什么



Anthropic 的关键洞察: 让 Agent 把时间花在组合编排上,而非从头写样板代码。Skill 文件夹里的脚本和可复用资产会显著降低 Agent 的出错率




如果把所有内容都混在一起会怎样?Agent 会在 3000 行约束里翻找检查清单,一个"规则"文件里藏着流程步骤——浪费 token,维护也变噩梦。





文件内容严格分离


边缘情况分类(按形式决定目标,不按内容)


有些内容既是解释性的又容易违反(如"输入验证的坑"),按形式决定:



  • “你必须做 X”(指令性的) → rules/

  • “小心 X”(警告性的) → references/gotchas.md

  • 第 1 步、第 2 步、第 3 步(流程性的) → workflows/


判断窍门:当迷茫的时候,挠一挠自己的后脑勺问自己:

“我能做 X 吗?”→ rules;

“这个坑怎么避?”→ references;

“我现在该做什么?”→ workflows。


文件大小参考值



一个良好的 skill,如果单个文件太大,不可避免会导致 Agent 无法读到正确的内容;所以在一定情况下自动拆分或合并规则文件是必须的。但也不是一定要触发:如果大于标准但都是同一个模块的,那么也不应该拆分。


行数是信号,不是命令。 超标触发评估,而非自动拆分——同一个模块的内容即使超过 300 行也不应该硬拆。





三、让 skill 谦虚而不是过度自信



同一会话里用着用着 Agent 突然"变蠢"——明明第一轮还按规则走,第三轮开始凭感觉写代码,读过的 SKILL.md 规则全忘了。这不是模型笨,是你少了一道强制再读的钩子。





过度自信的agent

[第 1 轮] 用户:"帮我修一下 UserService 里的空指针 bug"
→ Agent 读 SKILL.md
→ 匹配 Common Tasks 的 "Fix bug" 路由
→ 读 rules/coding-standards.md + rules/project-rules.md
→ 按 workflows/fix-bug.md 流程修好 ✓

[第 2 轮] 用户:"顺便加个导出 Excel 的接口"
→ Agent:"我已经知道这个项目的规则了"
→ 跳过 SKILL.md
→ 直接开写 Controller

问题:
- 新任务匹配的是 "Add Controller" 路由,要读的是 rules/backend-rules.md
- 这个文件里有一条 gotcha:"导出接口必须走 async 队列,直接响应会超时"
- Agent 没读到,写了同步接口
- 测试通过(小数据),生产炸(大数据)
- 两小时定位之后发现:规则一直在那里,只是 Agent 没读

为什么跳过了?



  1. 跨任务没重走路由:第 1 轮记住了"Fix bug 路由",误以为等于"所有任务的路由"

  2. 上下文可能已悄悄压缩:第 3 轮的时候 /compact 早就跑过,SKILL.md 早就不在 context 里了,Agent 只凭残留摘要干活


这不是 skill 内容的问题,是 harness 没给 Agent 重读触发。


三层强制再读(本项目的实际做法)


光写一句"请每次重读 SKILL.md"不管用——第一轮能记住,第十轮压缩后指令早就没了。必须结构化地多层冗余


为什么要三层冗余?因为每一层都可能被压缩器丢掉,留给你的是下一层。最坏情况下只剩薄壳——这就是为什么第六章说"Red Flags 必须塞进薄壳而不是只写在 workflow 里"。


嘴硬的 Agent


光有机制还不够,压力下 Agent 会自己编借口绕过。本项目的 workflows/update-rules.md § Rationalizations to Reject 就是一张从真实失败里抄来的借口表:

例:



硬约束:这张表只能从真实失败里抄,不能凭空想象。理由在第八章详细讲。


一条原则,一个检验


沿用第一章的"原则 + 检验句"格式(强烈建议):


## Session Discipline(同会话多任务必须重走路由)

每个新任务——即使是同一会话的第 N 轮——必须重读 SKILL.md、重新匹配
Common Tasks 路由、重读该路由列出的所有必读文件。

检验:问自己"这次任务我读的文件和 Common Tasks 里对应路由列的完全一致吗?"
如果有任何差异(少读 / 多读 / 凭记忆),立即回头重走路由。




四、skill的三要素



你精心写了一份 Prompt,措辞严谨,逻辑清晰,甚至还加了示例。但 Skill 跑起来之后,模型要么"触发不了",要么"触发了却不按规范做",要么"今天好用,明天又乱来"。问题出在哪?




如果你想做好一个 AI Skill,你需要同时想清楚三件事:Prompt、Context、Harness。它们分别解决三个完全不同维度的问题,缺任何一个,Skill 都只是"半成品"。





Prompt——定义做什么

Prompt 是你给模型的指令书。但在 Skill 体系里,Prompt 其实分为两个层次。


层次一:Description(触发描述)


非常重要!!!

skill里面最重要的就是description了,否则命中都命中不了!!!


description是写在 SKILL.md,是模型判断"要不要调用这个 Skill"的最重要依据


范例:


---
name: docx-writer
description: >
创建专业 Word 文档。当用户提到 .docx、Word 文档、
报告模板、正式文件时,必须使用此技能,即使用户
没有明确说"帮我做 Word 文档"。
---

Description 相当于 Skill 的"门牌号"——写得模糊,模型就找不到门;写得太窄,该触发的时候触发不了。


反直觉的设计:模型天然倾向 undertrigger(保守激活),所以 description 要主动覆盖用户可能的各种表达方式。


层次二:Body(执行指令)


这是 SKILL.md 的正文部分,告诉 Claude 具体怎么执行——步骤顺序、输出格式、注意事项、边界条件。


## 输出格式

始终使用以下模板结构:

# [文档标题]
## 执行摘要
## 关键发现
## 建议与下一步

写好 Body 的三个关键原则:



  • 用祈使句,而不是"你应该……"。「读取文件」比「你应该先读取文件」更直接有效。

  • 解释"为什么",而不只是"做什么"。让模型理解背后的逻辑,它才能在边缘情况下做出合理判断。

  • 控制长度,SKILL.md 正文建议 500 行以内。超出就拆分为引用文件,按需加载。





Context——决定知道多少

这是最容易被忽视的一环。


Context 是模型在生成回答时能"看到"的所有信息。你的 Prompt 写得再好,如果模型在执行时"看不到"它,一切都是零。


三级渐进式加载机制


Skill 系统用"Progressive Disclosure(渐进式披露)"来管理 Context,分三个层级:


这个设计解决了一个根本矛盾:信息越多越好,但 Context 窗口是有限的


解法是:只把"始终需要"的信息放在顶层,把"可能需要"的信息放在引用文件里,让模型在需要时再去读。


典型目录结构


my-skill/
├── SKILL.md ← 第 1 + 2 级
└── references/
├── aws.md ← 第 3 级,部署到 AWS 时才读
├── gcp.md ← 第 3 级,部署到 GCP 时才读
└── azure.md ← 第 3 级,部署到 Azure 时才读

agent 只读取当前任务相关的引用文件,而不是把所有内容都塞进 Context。这样既保证了信息完整,又不浪费窗口资源。


Context 设计的三个常见问题



  1. Context 太少:模型看不到规范,行为随意发挥

  2. Context 太大:超出窗口,后面的指令被静默忽略

  3. Context 设计混乱:无关信息干扰模型的判断,导致输出不稳定


这也是为什么本项目把 rules / workflows / references 严格分开——不是形式主义,是为了让每个任务只加载最小必要集合。





Harness——验证好不好用

Harness 是一层常被低估的结构:很多失稳问题的根因不是模型,是 harness 没给它正确的拦截和重试机制。


很多人写完 Skill 就直接上线,出了问题才去猜"是哪里写错了"。Harness 就是让这个过程变得有据可查——你改了什么,变好了还是变差了,一目了然。


对应到 skill 里,Harness 做三件事:


1. 结构性拦截(防失控)


Prompt 里写一百遍"必须做 AAR"都会被"就这一次"绕过。结构性拦截需要:



  • 薄壳里的 Red Flags STOP 块(见第六章 6.3)—— 把"就这一次跳过"前置拦截

  • workflows/update-rules.md 里的 Rationalizations 表(见第八章 8.3)—— 把 Agent 的真实借口抄进文件

  • SessionStart hook(见第七章)—— 压缩后自动重新注入 SKILL.md


这三者叠加才能扛住长会话的压力。


2. 自动化验证(防漏项)


templates/skill/scripts/smoke-test.sh 做 48 项自检:结构、行数、占位符残留、路由完整性、Cursor 一致性、薄壳一致性。见第十五章。

人类特别不擅长手动检查 48 项——脚本能抓住 80% 的"遗忘型错误"。


3. 真实压力测试(防纸面合规)


templates/skill/scripts/test-trigger.sh 会从 Common Tasks 里生成真实用户可能说的提示词,用来测 description 的触发率——单独读一遍 SKILL.md 觉得没问题,跑 test-trigger.sh 才发现一半的触发短语命中不了。


跳不过"自己看"这一步。 模型判断不了"读起来顺不顺",让 AI 自动改 prompt 最后会改成自我安慰。真实输出必须人眼看。





三者的关系:缺一不可


很多开发者把 90% 精力放在 Prompt 上,跑不对又只调 Prompt,从不审视 Context 设计,也没有 Harness 客观衡量"改好了还是改坏了"。


Prompt 定行为,Context 给视野,Harness 做质检。 三者缺一,skill 都只是"半成品"——



  • 缺 Prompt → 激活率低 / 行为漂移

  • 缺 Context → 规则写了读不到

  • 缺 Harness → 今天好用明天乱来,出错也不知道





五、SKILL.md:skill的导航中心



SKILL.md 不是百科全书,是目录。Agent 每次任务都要读它,所以它必须短、必须只讲"读什么 / 什么时候读"——而不是"这个 skill 有哪些规则"。





SKILL.md 的四个核心板块

SKILL.md 应该很短(<= 100 行),只负责告诉 Agent 读什么、什么时候读


---
name: {{NAME}}
description: > (触发条件,见 5.1)
primary: true
---

# {{NAME}}
{{SUMMARY}}

## Always Read ← 每次任务都读(2-3 个文件)
## Session Discipline ← 多任务会话的强制再读(见第三章)
## Common Tasks ← 按任务类型路由
## Known Gotchas ← 最关键坑点 + 指向 references/gotchas.md
## Core Principles ← 项目特有原则(每条带 ✓ Check)

一个 skill 里面的文件最重要的是什么呢?name?version?description?还是下面的内容? —— 答案一定是 description


Description = 触发条件


description 字段是 Agent 决定"要不要激活这个 Skill"的依据。它不是摘要,是触发条件。


# ❌ 错误 —— Agent 无法匹配
description: API development helper

# ✅ 正确 —— 明确触发短语 + 激活条件
description: >
This skill should be used when the user asks to "add a new API endpoint",
"write controller logic", "fix a backend bug", or "add a database migration".
Activate when the task involves REST routes, request validation,
service layer logic, or MyBatis mapper changes.

质量检查:



一个 Description 写不好的 Skill,等同于不存在。


^-^ 如果你有 Cursor 注册入口 .cursor/skills/<name>/SKILL.md,它的 description 必须和主 SKILL.md 完全一致。否则两边漂移 = Cursor 用一套判据,其它 harness 用另一套,激活随机化。


两层路由:Always Read + Common Tasks


第一层 —— Always Read(每次任务都读,2–3 个文件封顶):


## Always Read
1. `rules/project-rules.md`
2. `rules/coding-standards.md`

放什么?只放"任何任务都必须遵守"的约束——项目通用规则、编码规范。领域特定规则(backend / frontend / db)绝对不放这里,应该让 Common Tasks 按需路由。


第二层 —— Common Tasks(按任务类型路由):


## Common Tasks
- Add Controller → read `rules/backend-rules.md` + follow `workflows/add-controller.md`
- Fix bug → read task-relevant `rules/*.md` + follow `workflows/fix-bug.md`; ref: `references/gotchas.md`
- Multi-subtask / long autonomous run (≥ 3 independent subtasks) → follow `workflows/subagent-driven.md`
- **Other / unlisted task** → read `rules/project-rules.md` + `rules/coding-standards.md`, then match by workflow filename. If no match, proceed with Always Read rules.


规则:



  • 每条必须列精确文件路径,不能只写 “follow the workflow”

  • Common Tasks 控制在 5–10 条;超出按领域分组(frontend tasks / backend tasks / ops tasks)

  • 必须有 “Other / unlisted task” 兜底条目——没兜底 = 不在列表里的任务 Agent 会乱跑

  • 必须有 multi-subtask 路由指向 workflows/subagent-driven.md(见第九章)


Known Gotchas:最高价值板块




  • Filter 必须在 app init 之前注册,否则首次渲染空白 → see references/gotchas.md#filter-registration




  • 弹窗内 Tabs + service 只打首层接口 → see references/gotchas.md#nested-service-tabs




**为什么坑点的一句话要上 SKILL.md,详细要放 references?** 因为坑点是"价值密度最高 / 阅读成本最高"的内容——全量放 SKILL.md 会把路由中心变成坑点百科,全量放 references 又会让 Agent 在任务路径上看不到它。**一句话 + 锚点**是最佳平衡:Agent 每次都看得到哪些坑存在,真的踩到才 deep read。

**硬约束**(本项目 `SKILL.md § Core Principles #13` "激活优于存储"):

> 坑点只躺在 `references/` 里不算"捕获"——它必须同时出现在 Agent 的任务路径上(workflow 的完成检查 / SKILL.md 的 Known Gotchas / rules 摘要)。

这一条在第十章"录入知识库"里会详细讲。





六、跨工具兼容的基石:薄壳



一个 skill 怎么在 Claude Code / Cursor / Codex / Gemini 等多种工具里生效?答案不是把 SKILL.md 复制 N 份,而是在每个工具的"入口文件"里放一层薄壳(thin shell),把路由表内联进去





薄壳与跨工具兼容

Agent 长对话会压缩上下文,“去 scan skills/*/SKILL.md” 这种自然语言指令压缩后会丢。薄壳的作用就是把路由表内联进每个 harness 的入口文件,压缩后仍然活着。


为什么不能只靠 “去读 skills/*/SKILL.md”


先看一个真实的失败场景:



场景:用户在 CLAUDE.md 里写了一句"formal docs live under skills/, read skills/*/SKILL.md first"。对话进行到第 40 轮,Claude Code 触发 /compact,上下文被压缩成摘要。接下来用户开启新任务"加个分页功能",Agent 根据摘要里残留的模糊记忆直接写代码——SKILL.md 已经不在上下文里了,Always Read 的规则全部丢失,任务路由没有匹配



根因:自然语言指令(“去读 X”)在上下文压缩时被当成普通描述丢掉;但结构化的表格、清单会被保留更多。


薄壳的核心设计:不写"去读 SKILL.md",而是把最小可执行路由表直接内联到入口文件里——压缩后表格依然在,Agent 拿到新任务时可以当场查表。


各工具入口



**缺哪个入口,那个工具就完全看不见你的 skill。


薄壳的三块核心内容(≤ 60 行)


每个薄壳由三块组成,缺一不可。下面以本项目为例



# CLAUDE.md

Formal docs live under `skills/`. Read `skills/*/SKILL.md` — default to

`primary: true` skill; only switch when task clearly matches another.

## Quick Routing (survives context truncation)

| Task | Required reads | Workflow |

|------|---------------|----------|

| Fix bug | `rules/project-rules.md` + `rules/coding-standards.md` | `workflows/fix-bug.md` |

| Multi-subtask / long run (≥ 3 independent subtasks) | `rules/project-rules.md` | `workflows/subagent-driven.md` |

| <!-- FILL: task --> | <!-- FILL: `rules/<x>.md` --> | <!-- FILL: `workflows/<y>.md` --> |

| Other | `rules/project-rules.md` | Check `workflows/` for closest match |

## Auto-Triggers

- **New task in same session** → re-read `skills/{{NAME}}/SKILL.md`, re-match

Common Tasks route, re-read all required files. "I already read it" is not

valid — context compresses, routes differ.

- Before declaring any non-trivial task complete → run Task Closure Protocol

(see `skills/{{NAME}}/workflows/update-rules.md`)

- Skip only for: formatting-only, comment-only, dependency-version-only,

behavior-preserving refactors

## Red Flags — STOP

"Just this once I'll skip the AAR" → stop. See

`skills/{{NAME}}/workflows/update-rules.md` § Rationalizations to Reject.


三块各自的作用:




  1. Quick Routing——Task / Required reads / Workflow 三列,必须有兜底行 Other 和多子任务行。压缩后这张表是 Agent 查找"这次任务该读哪些文件"的唯一线索




  2. Auto-Triggers——事件→动作映射。最关键的是第一条 “New task in same session → re-read SKILL.md”(Session Discipline):多任务会话里 Agent 常靠"我前面读过了"的残缺记忆继续干活,这一条强制每次新任务重新路由。




  3. Red Flags — STOP——把"就这一次跳过 AAR"这类借口前置拦截。Karpathy Skills 没有这一块;我们加它是因为压缩后只有薄壳会留下,Red Flags 是最后一道防线




反例:soft-pointer-only 薄壳为什么会坏事


^-^ 常见的错误写法:


# CLAUDE.md
Please read skills/my-skill/SKILL.md before starting any task. It has all
the rules and workflows you need.


这种写法在短会话里能工作。但长会话里:



  • /compact 后,“Please read skills/…” 这句自然语言会被摘要掉

  • Agent 看到新任务,没有路由表可查,直接凭感觉动手

  • 用户察觉不到——输出看起来合理,只是少了 Always Read 的约束


^-^ 正确的做法就是 上面提到的的三块模板——路由表、Auto-Triggers、Red Flags 都是结构化内容,压缩器会保留更多。用通俗的话来说,多让Agent后面多自己提到,而不是仅仅靠上下文来维护内容





七、对抗 Agent 失控的两道防线



第六章的薄壳扛住了"压缩后 SKILL.md 消失"的 80% 场景,但还有 20%:/clear 直接把 context 擦干净,薄壳也得从磁盘重读。这时候需要 hook 来自动帮 Agent 把 SKILL.md 塞回来。



Hook 就是两道机制级护栏,不是靠 Agent 自觉,是客户端在调用工具前后物理地拦一刀:


SessionStart Hook —— 防遗忘(上下文压缩后自动把 SKILL.md 塞回去)


PreToolUse Hook —— 防违规(Agent 要编辑核心规则文件前,先过一道闸)




SessionStart Hook —— 对抗上下文压缩

借鉴 obra/superpowers


Claude Code / Cursor 会在几种情况触发 context 清理:



失忆之后 Agent 开始"合理地"走偏——没有 Always Read 约束、没有 Common Tasks 路由、没有 Known Gotchas——但是输出看起来仍然像那么回事,用户察觉不到。


解决:hook 在三个事件上自动重注 SKILL.md


SessionStart hook 监听 startup | clear | compact 三个事件,在事件触发时自动读取 SKILL.md 并注入 context。Agent 下一轮回答看到的就是完整的 SKILL.md,不用等用户手动 @SKILL.md


本项目 templates/hooks/ 已经备好了三个文件:



脚本内部只做三件事:


# 1. 定位 SKILL.md(支持多 skill 项目)
skill_md=$(find skills/*/SKILL.md | head -1)

# 2. 读文件内容 + JSON escape
content=$(jq -Rs . < "$skill_md")

# 3. 按 harness 输出不同字段名
case "$HARNESS" in
claude-code) echo "{\"hookSpecificOutput\": ...}";;
cursor) echo "{\"additional_context\": ...}";;
*) echo "{\"additionalContext\": ...}";; # fallback
esac

SessionStart hook 只负责把 SKILL.md 重新注入——它不会:



  • 替 Agent 执行 Always Read 里的文件(那是 Agent 拿到 SKILL.md 后的责任)

  • 自动触发 Task Closure Protocol(那是第八章的事)

  • 在会话中途检测 “Agent 已经走偏” 并纠正(那是第三章薄壳 Auto-Triggers 的事)


所以正确的分工



  • 第三章的 Session Discipline → 多任务会话里的重读触发

  • 第六章的薄壳 Auto-Triggers → 压缩后仍能看到的路由兜底

  • 第七章的 SessionStart hook → 清空 / 压缩事件发生后的自动补弹


三者叠加才能扛住长会话 + 多任务 + 多次 compact 的真实工作流。





PreToolUse Gate —— 对抗约定失守

我用10 个对抗性 prompt 测 Haiku 4.5 和 Sonnet 4.6 的遵守情况


根因:模型的注意力方向是"回答用户的请求",不是"遵守规则"。当用户说"我 leader 让我加"或"demo 5 分钟后要用",模型更倾向于帮用户,而不是帮规则。


做法:PreToolUse 闸拦在 Edit 之前


当 Agent 要对核心规则文件(比如 agent-behavior.md)做 Write/Edit 时,hook 脚本先拿到调用参数,自己判断要不要放行:


# agent-behavior-gate.sh 核心逻辑(简化)
if [[ 编辑会让文件变长 && (超出上限 || 没有 AAR 证据) ]]; then
echo "BLOCKED: 超过 100 行 / 没有 behavior-failures 证据" >&2
exit 2 # ← 关键:非 0 退出码让 Claude Code 直接取消这次 Edit
fi
exit 0 # 放行

Agent 根本没机会写到文件里,它只看到一个"Edit 被 hook 拒了"的错误。无论它多想加、给了多少理由,都不行。



所以这两道 hook 不是重复,是接力。前者保证信息能进去,后者保证读到了也跑不掉。


但是hook只能一定程度的避免这个问题,同时也面临一个很严肃的问题,对于低智模型来说,就是会有很多情况读不到,哪怕加了hook也不太行,但是太多的hook,其实一定程度反而会限制agent的发挥,所以还是建议用sonnet及以上,而不要用haiku处理一些复杂问题




八、完整的任务闭环



Agent 经常把"主体代码写完 + 测试通过"当作"任务完成"。但真实的任务结束还差一步:扫一遍刚才的工作,有没有踩到新坑、发现新规则、暴露已有规则的漏洞。这一步不是可选的 polish,它是任务定义的一部分。





如何做到任务闭环

8.1 协议定义(本项目 workflows/update-rules.md


一个任务在以下条件全部满足前不算完成:

1. 主体工作完成并验证(代码跑通、测试通过、功能交付)
2. 30 秒 AAR 扫描(4 个问题 —— 全部"否"则到此结束)
3. 如果任何一个"是" → 通过录入标准 → 通过则记录

任何 workflow 不得在跳过第 2 步的情况下声明"完成"。

8.2 AAR 的 4 个问题(30 秒扫完)



  • 新模式? —— 用了未记录的模式或约定吗?

  • 新陷阱? —— 遇到了不提前知道就会浪费大量时间的问题吗?

  • 缺失规则? —— 因为缺少某条规则导致走了弯路吗?

  • 过时规则? —— 发现现有规则已经不准确或不再适用吗?


触发门槛:判据从"行为变化"改成"非琐碎任务"——后者更容易正确判断。跳过条件窄且明确:仅格式化、仅注释、仅依赖版本变更、无新教训的重构。


8.3 Rationalizations to Reject:从真实失败抄来的借口表


光定义协议不够——压力下 Agent 会自己生成借口绕过。理论上应该维护一张原话捕获的借口表:



硬约束:这张表只能从真实失败里加行,禁止凭空扩写


为什么这么严?因为凭空想象出来的借口 Agent 不会真的说——它真实的借口往往更狡猾、更具体、更贴近当前场景。把真实借口和虚构借口混在一起,压力值就被稀释了,Agent 下次用稍微变形的借口就能绕过去。


8.4 Red Flags — STOP


以下任何一条出现,立刻停下,不要自我协商:



  • 发现自己在想"这次 AAR 就算了"

  • 任务声明"完成"但没跑 30 秒扫描

  • 把 gotcha 写进了 reference,但没更新对应 workflow 的完成清单

  • 修了同一类 bug 第二次,但规则文件没动过


这些 Red Flags 必须同时出现在薄壳里(第六章 6.3)——压缩后 workflow 文件读不到,薄壳是最后一道防线。


借口表不是凭空增长的,是被失败喂大的。 这正是第四章讲"Harness 做质检"的具体落地方式——压力测试抓到的借口是 harness 的诊断输出,Red Flags 和借口表是 harness 的拦截器。





九、多子agent保证主agent纯净



主 Agent 的上下文越用越脏——前面的 debug 日志、中间的探索、后面的实现全堆在一起,第 50 轮的时候它连自己最初的任务目标都模糊了。解法是把独立子任务派给干净上下文的 worker,worker 做完退出,主 Agent 只看最终产物。





Subagent-Driven Development —— 多子任务场景

这是 Superpowers 最重要的一个结构性发明:不是一个大 Agent 从头做到尾,而是每个独立子任务派一个新的子 Agent,带着干净的上下文窗口进来,做完就退出。

我们似乎也可以借鉴一部分来完成我们的skill


核心思想: 不是一个大 Agent 从头做到尾,而是每个独立子任务派一个新的子 Agent,带着干净的上下文窗口进来,做完就退出。收益:



  • 主 Agent 的上下文永远干净

  • 主 Agent 兼 reviewer,所有 worker 产物都过它审核

  • 可以自主跑几小时不偏离原计划——因为每个 worker 只看合约,不看历史


什么时候启用


满足任意一条:



  • 子任务 ≥ 3 个且互相独立

  • 单任务会吃掉 > 30% 剩余 context

  • 任务是"探索 + 实现 + review"混合形态

  • 即将多小时自动运行

    都不满足就直接内联做——派发有开销(写合约、开 worker、review),小任务不划算。


Harness 兼容性


只有 Claude Code 有原生 Task 工具。Cursor / Codex / Gemini / Copilot 只能降级:在单上下文里按 checklist 模拟,或每个子任务手动开新会话。降级模式仍然有价值——两阶段 review + 合约本身就能捕获大部分 drive-by 缺陷,只是跳过"字面派发"。


四阶段流程



  1. Plan —— 写完整任务清单,每条是一份子任务合约

  2. Dispatch —— 每份合约开一个干净 worker,合约原文作 prompt,不带主对话历史;无依赖就并行派

  3. 两阶段 Review ——

    • Stage A 查 spec 合规(Outputs 文件、Forbidden Zones、Acceptance 命令、是否有 drive-by 改动)

    • Stage B 查质量(代码、gotcha、AAR、Recording Threshold)



  4. Merge 或 Reject —— 两个 stage 都过才 merge。Stage A 不过就重派,不要在主上下文里内联补 —— 那正好把主上下文污染回去


子任务合约:五个字段


## Goal             <!-- 一句话,面向结果 -->
## Inputs <!-- worker 允许读的确切文件 -->
## Outputs <!-- worker 必须产出/修改的确切文件 -->
## Forbidden Zones <!-- 不许碰的文件/目录/副作用,不确定默认禁 -->
## Acceptance Criteria <!-- 可机械验证的命令,如 `yarn tsc --noEmit` -->

规则:任何字段不能空;Goal 面向结果不微管步骤;Acceptance 必须是可执行检查,不是散文;worker 不得改合约——合约错了是主 Agent 重写重派。

完整模板见 templates/protocol-blocks/subagent-contract.md


禁止项



  • 递归派发(worker 不能再开 worker)

  • 让 worker review 自己的产物

  • 中途往 worker 上下文塞"澄清"(合约错了就取消重写)

  • 跳过 Stage A 只跑 Stage B,或反之

  • “worker 基本对了,剩下 10% 我在主上下文补” —— 这是最常见的借口,也是最污染主上下文的动作。重派更紧的合约





十、录入知识库,让skill越来越聪明



第八章的 AAR 扫描出来"这是个新坑 / 新规则"之后,下一步是决定要不要记、记到哪里、怎么写。这三步都有硬约束——随便记就会把 skill 变成冗长的日记本。





录入标准、泛化规则、激活优于存储

一个好的 skill 必须既会记录又会筛选——这个模块决定了 skill 能不能随项目自动进化。


这个模块决定了一个skill是否有了自动进化的能力


Recording Threshold(2/3 录入标准)


不是所有发现都值得记录。录入前通过阈值过滤:



至少 2/3 通过才录入。


通过阈值的典型内容



  • 框架生命周期坑(注册时序、挂载 / 卸载陷阱)

  • 隐藏的路由依赖(注册顺序有影响)

  • 非显而易见的同步或状态重置要求

  • 跨层交互陷阱(对话框 + Tab + 嵌套服务)


不通过的典型内容



  • 一次性变通方案(只和当前 bug 相关)

  • 看代码就能明白的事情

  • 轻微的风格偏好

  • 官方文档已充分覆盖的内容


实战示例


Agent 完成任务:添加了一个新页面,用到 Recoil atom + 自定义 filter。

发现 1:Atom 命名约定(xxxAtom)
可重复? 是 → 通过
代价高? 否(命名不一致不会导致错误)→ 不通过
代码不可见?否(现有 atom 已经清晰展示了模式)→ 不通过
结果:1/3 → 不录入

发现 2:Filter 必须在 app init 之前注册
可重复? 是 → 通过
代价高? 是(首次渲染空白,30+ 分钟调试)→ 通过
代码不可见?是(时序依赖从代码中看不出来)→ 通过
结果:3/3 → 录入

Generalization Rule(泛化规则)


记录的内容必须脱离当前项目上下文也能看懂。


好坏对比



改写公式


具体发现 → 抽象为通用 pattern → 说明不遵守的后果


录入位置



录入格式选最轻的: 一句话 bullet → 一小段加到现有文件 → 新文件(通常不需要)。


激活优于存储


一个陷阱仅记录在 references/ 中是不够的。高代价陷阱必须同时:



  • 存储在正确的文件中

  • 激活在会触发它的任务路径上(workflow 检查项、SKILL.md 的 Known Gotchas、或 rules 摘要)


判断方法:“下次 Agent 走正常任务路径时,会自然读到这条经验吗?” —— 不会,就只是"记下来了",还没有"生效"。





十一、自我删除与迭代



只增不减的规则文档会变成屎山——3 个月前的坑现在已经不存在,但规则还挂在 rules/ 里误导新 Agent。skill 必须学会"忘记",而且这件事本身就需要一个 workflow。





错误学习与规则清退

Learn from Mistakes


Agent 犯错并被纠正后:



  1. 先搜索 — 确认规则是否已存在

  2. 分类根因:

    • 规则缺失 → 通过录入标准后新增

    • 规则过时 → 直接更新(无需门槛——过时规则比缺失规则更有害)

    • 规则废弃 → 走清退流程

    • 规则未被遵循 → 检查醒目度(可能需要从 references 上浮到 SKILL.md 的 Known Gotchas 或薄壳)




Rule Deprecation


规则只增不减会导致文档膨胀。清退条件:



  • 相关技术已移除 → 直接删除整条规则

  • 正在迁移中 → 加作用域标注(“仅适用于 legacy 模块”)

  • 不确定还有没有用 → 加 <!-- DEPRECATED: reason, date --> 注释,保留 1 个迭代周期再删





自维护机制

评估式拆分


文件超标时回答三个问题:



  1. 话题可分离?

  2. 导航困难?

  3. 拆后各部分能独立存在?

    三个都 Yes → 拆。任何一个 No → 不拆。


评估式合并


碎片文件过多时:



  1. 话题相关?

  2. 合并后更好找?

  3. 合并后不超标?

    三个都 Yes → 合并。


定期 drift 检查


用两个真实不同类型的项目跑同一套 Quick Start(比如 Go CLI + Next.js site),diff -r 对比结果:



  • 骨架文件(shells、hooks、protocol-blocks)应该几乎一样 —— 对了,这是预期

  • rules/coding-standards.mdgotchas.mdSKILL.md 的 Common Tasks 应该完全不同 —— 如果一样,说明模板越界了,把项目特定内容固化成了默认值


drift 检查的结果要记进 ANTI-TEMPLATES.md 的 Homogeneity Drift Log,这是反漂移的主要证据。





十二、来自各方大佬的建议



前面各章讲的都是"怎么组织 skill"——这一章是"写 skill 内容时的基本功"。四条原则,每一条违反都能让一个结构良好的 skill 变成废纸。





来自 Anthropic 的建议

不要陈述显而易见的事情


我们应该着重注意项目特有的约定、与主流做法不同的地方、Agent 默认行为会出错的场景

通用编程知识(比如"SQL 注入是坏事")不需要写进 Skill——模型已经知道了,写了只是浪费 token。


判断标准:“资深开发者第一次看你的项目,什么会让他踩坑?” 那个东西才值得写。


避免过度指令化


提供约束和上下文,不要把每一步都写死。


❌ 过度指令化:
添加按钮时使用 Tailwind class "bg-blue-500 hover:bg-blue-700..."

✅ 约束 + 上下文:
按钮使用项目的设计系统 token(见 `rules/frontend-rules.md`)。
交互元素必须有可见的 hover / focus 状态。

为什么?过度指令化会让 skill 在设计系统升级后全部失效——你换了一套 token 命名,几百条硬编码的 class 全要改。给约束而不给具体值,skill 能多活过几次重构。


利用脚本和代码库


Agent 调用已有脚本比从头写样板代码可靠得多。本项目的 templates/skill/scripts/smoke-test.sh 就是这个思路的产物——不是让每个下游项目自己写验证逻辑,而是共享一个 48 项自检脚本。


判断标准:“这段代码会在多少次任务里被 Agent 重写?” 超过 2 次 → 写成脚本;2 次以内 → 内联。


保持 Skill 聚焦


一个想做所有事的 Skill 什么都做不好。需要拆分的信号:



  • Description 列了 10+ 个来自不同领域的触发短语

  • Common Tasks 有 15+ 条覆盖不相关的工作

  • Agent 经常为只涉及一个子领域的任务激活整个 Skill


拆分路径:见本项目 references/layout.md § Multi-Skill Projects——什么时候一个 skill 该裂成两个、怎么处理公共内容。


Skill 也是代码,需要测试和迭代



  1. 写 Skill —— 触发条件 + 路由

  2. 测试激活 —— test-trigger.sh 验证 description 命中率(见第十五章)

  3. 测试路由 —— 每种任务类型读对了文件吗?

  4. 压力测试 —— 时间压力 / 规则冲突 / 模糊 spec 下跑任务,逐字抓借口(见第八章)

  5. 观察失败 —— Agent 在哪里仍然出错?

  6. 通过 AAR 更新 —— 用 Task Closure Protocol 改进 Skill(见第八章 8.5)


步骤 4-6 正是第四章讲的 Harness 要做的事——没有这一层,skill 永远停在"纸面合规"





十三、一个skill干一个事情



当一个项目有多个 skill(比如 skills/frontend/ + skills/backend/ + skills/ops/),Agent 怎么知道该激活哪个?两个 skill 有冲突规则怎么办?这些问题不解决,多 skill 架构会比单 skill 更乱。





如何保证skill直接不互相冲突呢?

不要在github上拉非常多的同类skill!!!不要在github上拉非常多的同类skill!!!不要在github上拉非常多的同类skill!!! 否则冲突是一定会出现的,可能暂时没有什么更好的解决方案,博主强烈建议使用少而精的skill,同时claude官网也说明过,建议使用自动触发的方式,而不是主动引用skill来调用,太多的skill,只会让正确命中率越来越低


一般来说,存在下面五条硬约束



  1. 独立入口 —— 每个 Skill 有自己的 SKILL.md,不共用

  2. 注册 —— 每个 Skill 都需要 .cursor/skills/<name>/SKILL.md 注册入口,缺一个 Cursor 就看不见

  3. 优先级 —— SKILL.md frontmatter 里用 primary: true 标记默认 skill;任务明确属于某个 Skill 时,该 Skill 的规则优先

  4. 共享规则 —— 跨 Skill 的通用约定放 skills/shared/,各 Skill 的 Always Read 指向它

  5. 不要强行合并 —— 不同领域保持独立更清晰,合并只会让 description 变成"什么都能触发的万金油"


什么时候该裂成多 skill


信号:



  • 两个领域的 Common Tasks 完全不相交(frontend 任务不读 backend 规则,反之亦然)

  • description 要列 10+ 个跨领域触发短语

  • gotchas 文件按领域自然分成两半


这时候 skills/<one>/ 拆成 skills/frontend/ + skills/backend/ 是对的——SKILL.md 各自 ≤ 100 行、description 各自精准、Agent 激活的是"正好对应当前任务的那一个"。


多 skill 项目的 SessionStart hook


第七章的 hook 脚本默认找 primary: true 那个 SKILL.md 注入——多 skill 项目里必须有且仅有一个 primary: true,否则 hook 会随机选一个,或者注入冲突内容。





十四、多个skill互相组合



之前讲的是隔离 —— 不同 skill 不互相污染。但还有另一个维度:组合 —— 你的 skill 主动调用另一个 skill 去完成一段工作。隔离防乱,组合能长。两个维度缺一不可。





产物是编排层,不是死胡同

项目架构简览



  • workflows/*.md —— 反复任务流程

  • rules/*.md —— 项目约束

  • references/*.md —— 背景 / 坑点

  • protocol-blocks/*.md —— 可复用小块(rationalizations / red-flags / reboot-check / subagent-contract)

  • hooks —— 前面讲的两道防线


其中我们可以在workflow里可以调用其他skill的工作流


这就是框架思维的关键 —— 你的 workflow 可以"外包"一段工作给通用 skill,比如把"做规划"外包给 obra/superpowers 的 planning skill,自己只承担项目特定的部分。(这里不得不吐槽一句,superpower有一些地方真的有一点重,如图所示,但是本身他的设计是没问题的,但是偶尔会导致一些简单问题被反复的问)


调用的三种组合模式


这个地方必须提前声明一个问题,首先,互相调用的前提是必须要有这个skill,所以也就是说要把调用项目也放在这个项目的某一个目录里面,比如跟下游项目的skill同级?这样就可以进行多skill之间的调用


模式 A:嵌入调用


# workflows/plan.md

## Step 1 — 收集项目特定的规划上下文(项目边界 / 用户画像 / 非目标)
## Step 2 — 调用通用规划 skill

1. Read `skills/superpowers/SKILL.md`,匹配到它的 "plan a feature" 路由
2. 跟完 `skills/superpowers/workflows/plan-a-feature.md` 全部步骤
3. 返回本 workflow Step 3,带上 Step 2 产出的规划文档

## Step 3 — 按项目规则审查规划产物
## Step 4 — Task Closure Protocol(AAR)

模式 B:直接路由


你的 SKILL.md Common Tasks 里某类任务直接指向另一个 skill 的 workflow,不写自己的包装。


- 做安全评审 → 跟 `skills/security-review/workflows/review.md`(不写项目 wrapper)

模式 C:子 Agent 委派


你开一个子 Agent,把另一个 skill 的执行整个隔离出去,只要结构化结果回来。


派子 Agent 跑 skills/web-research,contract:
Goal: 查某库 v3.2 是否向下兼容,有无已知 breaking change
Forbidden: 不在主仓写任何文件
Acceptance: 返回 10 行内摘要 + 至少 2 个源链接

可能出现的反模式



  • 隐式传递依赖 —— workflows/plan.mdskills/superpowers/ 但不保证下游项目有这个 skill → 静默失败。要么 vendor 一份,要么加 escape 条件:“缺 skill 就停下问用户”

  • 循环组合 —— A 调 B 调 A,Agent 进死循环。

  • 匿名调用 —— workflow 写"用安全评审 skill 处理" 不给具体路径 → Agent 自己猜,失去复现性。永远写具体 skill 路径

  • 组合当偷懒借口 —— 一直调 superpowers/plan 因为自己懒得写 workflow。一段时间后项目特定的规划规则会积累,得自己承起来

  • 跳过 Task Closure —— workflow 调完其他 skill 就结束,跳了自己的 AAR。



上一章和这一章的其实就是一个问题的两面吧,一面是隔离,一面是组合.缺一不可




十五、用Templates防止结构性遗忘



结构可复用,内容禁止预制。 —— 这是本项目 templates/ 目录的核心铁律,也是防止 skill 生成漂移的唯一办法。



该模块只试用于上游skill的编写,而成品skill其实可以忽略本章节




为什么需要预制脚手架,以及它和'千篇一律'的边界

这是博主在迭代过程中踩过的一个坑,单独拎出来讲:


问题: 老版本让 Agent 实时生成脚手架(heredoc 写入 SKILL.md、shells、workflows…),结果是每次生成都漏一两段——Agent 在压力下会"忘记"写 Red Flags 块,或者把 Auto-Triggers 写成纯自然语言。同一个协议,五次生成出五个版本。


解决: 上游项目里专门放一个 templates/ 目录,下游直接 cp -R + 一次 sed 替换占位符。Agent 不再"生成",只做"填空"。


templates/
├── skill/ → 复制为 skills/{name}/
│ ├── SKILL.md (带 {{NAME}} / {{SUMMARY}} / <!-- FILL: --> 标记)
│ ├── rules/ (stub 文件,≥60% 是 FILL 标记)
│ ├── workflows/ (update-rules / fix-bug / maintain-docs / subagent-driven)
│ └── references/gotchas.md (必须空着启动,禁止预填例子)
├── shells/ → 复制到项目根
│ ├── AGENTS.md / CLAUDE.md / CODEX.md / GEMINI.md
│ ├── .codex/instructions.md
│ ├── .cursor/rules/workflow.mdc
│ └── .cursor/skills/{{NAME}}/SKILL.md
├── hooks/ → SessionStart hook(见第六节)
│ ├── session-start
│ ├── hooks.json (Claude Code)
│ └── hooks-cursor.json (Cursor)
└── protocol-blocks/ → 可插拔的协议增强块
├── rationalizations-table.md
├── red-flags-stop.md
├── iron-law-header.md
└── subagent-contract.md

但是! 预制多了会不会导致所有项目长得一样?


会,如果边界没画清楚就会。所以这里要立两条铁律:


铁律 1:结构可以预制,内容禁止预制



实现方式是两种占位符区分对待:



  • {{NAME}} / {{SUMMARY}}机械替换(一次 sed 就搞定)

  • <!-- FILL: ... -->必须人工/Agent 判断,留空就是 bug


Quick Start 的最后一步是 grep -rn 'FILL:' skills/{name}/ ——每一个 match 都是必填项,不是可选项


铁律 2:“两个真实项目可以用同一份吗?”


templates/ 里加任何东西之前,先回答:



“一个 Go 后端微服务和一个 React 动画站都会复制这份模板。它们会同意这块内容吗?”




  • → 是协议/骨架,可以进 templates/

  • 不会 / 大概率不会 → 是项目特定内容,必须降级为 <!-- FILL: --> 或移到 ANTI-TEMPLATES.md 明确禁止


没有例外。这个测试被敷衍一次,templates/ 就会滑向"有主见的默认值",下游项目开始长得一样。


ANTI-TEMPLATES.md ——"我们故意不预制"清单


每次你决定 往 templates/ 里加某个东西,要把决策记下来:



  • 默认 lint/format 规则 → 语言特定,下游必须自己写

  • 默认 commit message 格式 → 团队偏好不同

  • 预填的常见坑点 → 坑是真金白银的调试换来的,不是想出来的

  • 默认目录结构 (src/test/docs) → 每个框架约定不同

  • 具体 subagent spec 样本 → 内容层,下游必须自己写


这个清单就是反漂移的压力器。清单越长,说明 review 越严肃。





防止结构性遗忘

Templates 存在的意义就是防止结构性遗忘


核心思路


Skill 不是创意写作,是工程基础设施。文件夹结构、frontmatter 字段、<!-- FILL: --> 标记、薄壳路由表——这些全是承重构件。漏一个,agent 就会静默退化。


Templates 把这些全编码成了一个可复制的起点:


cp -R templates/skill/ skills/$NAME/
cp -R templates/shells/ .
sed -i '' "s/{{NAME}}/$NAME/g" ...


跑完之后,每个 <!-- FILL: --> 标记就是一个 TODO。你不可能意外发布一个缺少 Cursor 入口或者没有 Always Read 的 skill——因为模板已经帮你占好了位,FILL 标记会一直叫到你替换掉为止。


Templates 防住了什么



Templates 不是限制


模板不限制你能做什么。模板只会给你骨架,防止你走偏,但是项目特定的内容,只有后期靠你自己来家


重点是:你不应该需要记住基础设施。你只需要想内容。





十六、自动化验证脚本兜底



你让 Claude 生成完整个 skill,它告诉你"全部完成"。那现在可以上线了吗?——大概率不能。人类特别不擅长检查自己的活,Agent 也一样。80% 的 skill 失败来自遗忘型错误,不是理解型错误,这些用脚本就能抓住。





人类特别不擅长检查自己的活

问题在哪


本文的demo项目有大约几十个可能出错的地方:



  • Common Tasks 里引用的文件不存在

  • SKILL.md 的 description 和 Cursor 入口对不上

  • 编辑的时候薄壳把路由表搞丢了

  • {{NAME}} 占位符在 sed 替换时漏网了

  • SKILL.md 悄悄超过了 100 行

    没有人每次都会检查这些。所以就有了脚本的必要性。


下面以测试项目为例,templates/skill/scripts/ 里备了两个脚本。


smoke-test.sh —— 自检测试


bash skills/my-project/scripts/smoke-test.sh my-project


关键设计:脚本把 SKILL.md 本身当作唯一数据源。 不需要配置文件,不需要手动写测试列表——你在 Common Tasks 里加了一条引用 workflows/deploy.md 的新任务,脚本就会自动发现这个文件还不存在。


test-trigger.sh —— 触发率测试


bash skills/my-project/scripts/test-trigger.sh my-project

它读你的 Common Tasks,自动生成真实用户可能说的提示词,然后检查 Agent 能不能找到你的 skill。这对 Cursor 用户最重要——Cursor 完全靠 description 的语义匹配发现 skill。


示例输出:可以看到哪怕是 Opus 4.6 也会有命中不到的场景,通过优化 description 自然语言可以提升命中率。



什么时候跑



  • 初次迁移完 —— 必须跑,把所有 FILL 标记都堵上

  • 编辑了 SKILL.md 或薄壳之后 —— 一行命令的成本,能抓住大部分手误

  • 从上游模板升级之后 —— 检查新模板和你的填充内容有没有冲突

  • 宣布 skill 迁移"完成"之前 —— 必须跑


脚本不能替代理解——它抓不到"description 写得不够精准"、"Common Tasks 路由设计不合理"这类语义问题。但这些是少数,80% 的失败来自遗忘而不是误解,脚本刚好补这一块。





十七、清晰的文件边界



用了一段时间后会发现 references/ 下突然多了 2026-04-14-session-notes.md2026-04-15-debugging-log.md——Agent 把"记录教训"解读成了"把会话也存档"。这种文件会毁掉整个 skill 的可维护性。





防止 Agent 把 skill 写成日记本


这是一个非常严重的问题——违反了第十章的三条核心设计:



  • 泛化规则原则 —— 会话日志是项目叙事,不是可复用知识

  • 激活优于存储 —— 没有路由,未来 Agent 永远命中不到

  • 自维护设计 —— 会无限膨胀,每次会话一个,1 个月后 references/ 下可能有 100 个同构文件


Agent 为什么会这样做?



  1. 过度解读"记录" —— 你说的是"把教训写进文档",Agent 扩展成"把整次会话也存档"

  2. 路径就近 —— 看见 references/frontend-pitfalls.md 在手边,懒得判断归属,顺手塞 references/

  3. 缺少明确的归档工作流 —— 没有 workflow 告诉它"会话级内容应该去哪里",就默认丢进 references


正确的记录方式


^-^ 应该做的:在 references/frontend-pitfalls.md加一段"ServiceStore / removeStore 卸载时的竞态"的泛化教训——什么时候触发、根因、怎么避免。这条通过 2/3 门槛(可重复 + 代价高 + 代码不可见)。


^-^ 应该删掉的references/2026-04-14-session-notes.md 这种文件整份删——每一行都是项目叙事,不是规则、不是工作流、不是可复用坑点。


如果真的需要"会话日志"


不是不能有,而是位置错了



  • 会话日志属于 docs/,不属于 references/(references 是规则级引用材料)

  • 如果真要加,需要显式在 SKILL.md 里加路由 + 新写一个 workflow(什么时候写、写什么、归档策略是什么)

  • 或者干脆用 git commit + CHANGELOG.md 代替——那才是正确的工具,skill 不是 git 的替代品


本项目 templates/skill/workflows/update-rules.md 里有明确的"记录位置判断表",就是为了防止这种漂移:




























内容类型 目标位置
稳定约束 / 通用原则 rules/
陷阱、架构笔记、生命周期坑 references/
有序步骤 / 完成检查清单 workflows/
会话历史 / 调试过程 不要写进 skill——用 git / CHANGELOG

Agent 每次触发 Task Closure Protocol 时会被要求查这张表。





附录1、踩坑清单




踩坑清单


踩过的坑,补几条图里没有的:




  • 让 Agent 每次实时生成脚手架 → 它会漏段。改成 cp -R templates/ + sed




  • 把"具体业务 spec 示例"预制进 templates/ → 下游会抄例子不写自己的。让 FILL 标记逼它思考




  • Rationalizations 表凭空扩写 → 稀释真实借口的压力值。只能从真实失败抄




  • 把 Auto-Triggers 只写在 workflow 里,不写进薄壳 → 压缩后薄壳是最后防线,薄壳丢了就全丢了




  • 薄壳坚持 ≤15 行不肯扩到 ≤60 → 加上 Red Flags + Auto-Triggers 15 行写不下,硬压导致协议碎片化




  • 多 harness 项目没有 GEMINI.md / Copilot 入口 → 这些 harness 读不到你的 skill,等于没有




  • Hook schema 用了 flat 格式 → Claude Code CLI v2.1+ PreToolUse 只认嵌套 hooks:[{type, command}],flat 写法看起来注册了(启动没报错、debug 里也能看到 hook 名字)但 Edit 时就是不触发。SessionStart 恰好两种都吃,所以初期你以为一切正常,实际 PreToolUse 那边静默失效




  • 想用 Claude Agent SDK 子 Agent 测 hook 效果 → 子会话根本不触发 PreToolUse hook(harness 设计),测出来永远是 false negative。真要验证 hook 效果只能开新的 Claude Code CLI 交互会话手动测,或者抓包看 hook_started 事件




  • subagent prompt 里用绝对路径绕过 isolation: worktree → 想用 worktree 隔离,但给子 Agent 的 prompt 里写了 /Users/xxx/project/...,子 Agent 用绝对路径改文件,直接写到了主仓,worktree 被当作"干净无变更"清理。结果是"感觉隔离了,实际污染了主仓",事后一看 git status 发现一堆不明改动




  • 把 Agent "漏读规则"归因成模型笨 → 先用 /context 确认文件到底在不在 Memory 里。Agent 能不能拿到 CLAUDE.md 是客户端行为(确定性的),能不能真的遵守是模型行为(概率性的)。把客户端路由表写全,比换模型便宜得多





附上一个自己写的开源项目



一个生产 skill 的 meta-skill。把它对准任何代码库,它会把这个项目的规则、流程、踩坑经验炼化进一个专属的一个项目 skill,成为所有 AI Agent(Cursor、Claude Code、Codex、Windsurf、Gemini)在每次任务前查阅的重要知识来源。重点是产物本身。




而是一个可路由、可自维护、能自动捕获经验、匹配任务时自动触发的skill框架。

你可以写你自己的 workflows,可以在workflow里面写制定plan的时候使用superpower等等,你往里塞的任何东西都会被每次任务里的每个 Agent 自动用上





Star History



实战实例




实战案例

案例一:

项目出现了一个bug,首先因为用的是cursor,命中了项目根目录.claude里面的薄壳,然后命中了工作流里面的fix-bug.md,然后读取到了之前出现过问题的记录的规则文档(这里不是初始化生成的,而是skill自己遇到bug,自己或者用户主动触发记录的),然后成功定位到了之前记录的规则.


案例二:

在codex里面改bug,因为薄壳的原因,所以可以读到项目的规则


最新回复 (19)
  • BlackCat 04-08 17:12
    1

    很好!看完之后,感觉我又可以正确的使用AI了

  • xiao 04-08 17:54
    2

    学习了,感谢分享。后面再试试。感谢感谢。

  • ltzh3530 04-08 17:55
    3

    看前面还在想这么多规则看起来挺复杂的结果最后有个项谢谢佬的分享

  • leesire 04-08 17:57
    4

    学习了,感谢分享。先mark一下 ,希望写出牛逼的skill

  • 这里是沃基 楼主 04-08 17:58
    5

    这个项目其实非常简单,你只要发给ai,然后让他实现在你的项目里面就可以了,会自动扫你的项目,然后建好一个名字是你的项目名的skill,

  • 路人甲 04-08 17:59
    6

    感谢佬友分享,写的很好很详细了,先mark一下

  • 这里是沃基 楼主 04-08 17:59
    7

    这个其实更多的是我吃公司的项目的屎的时候,踩坑踩出来的,被逼无奈

  • 白玉京 04-08 18:11
    8

    这个分享很棒,不仅有明确和详细的思路,还有可使用的工具,感谢大佬

  • liuqj2026 04-08 18:23
    9

    学习了,感谢分享,最近刚好在写一个skill

  • Fly040925 04-08 18:29
    10

    感谢佬友,这个要有空来慢慢学习,可以先尝试一个简单的项目

  • 这里是沃基 楼主 04-08 18:47
    11

    其实很简单,写skill就跟写书一样,写好目录索引,然后保证每一章的内容不多不少

  • 这里是沃基 楼主 04-08 18:49
    12

    其实有两种写法,如果是纯外接的技能,其实是一个生成好的skill文件夹,或者就像是我文章最后的项目里面的一样,是一个上游skill,用这个上游skill来生成项目的skill

  • TuringParty 04-09 13:47
    13

    感谢分享。先mark一下,后面用到的时候再看。

  • nevermorenight 04-09 14:14
    14

    感谢佬的分享,目前我还停留在写提示词,或者说规范别的skill,连workflow都是提示词里面写的,我后面按照你的教程封装一些skills,感谢。

  • 这里是沃基 楼主 04-09 14:36
    15

    基本上我都是用ai自己写skill,然后自己修改,这个项目还是有很多问题的,包括agent自己读不到,各种蠢蠢的问题,大家一起学习,一起进步!

  • wait_Until 04-09 16:40
    16

    看上去挺复杂的,就像写user story,码住后面看

  • 这里是沃基 楼主 04-09 16:57
    17

    其实简单的说,就是让agent更快更准确的找到要找的东西

  • Yovt 04-10 17:27
    18

    官方链接现在访问不了,出现 404 错误

  • 这里是沃基 楼主 04-10 17:28
    19

    github的网址吗还是什么链接呢???

* 帖子来源Linux.do
返回