我写了半年skill,直到上周才意识到自己从一开始就搞错了方向

事情是这样的。 前阵子我又一次在重构公司内部那几个Claude Code的skill,写着写着开始觉得有点不对劲。

48 分钟阅读
我写了半年skill,直到上周才意识到自己从一开始就搞错了方向

事情是这样的。

前阵子我又一次在重构公司内部那几个Claude Code的skill,写着写着开始觉得有点不对劲。

我发现我这半年写的那些skill,从定位上就错了。

不是语法错,不是内容写得不好,是我对skill这个东西到底是什么,整个想法都错了。

这事说起来挺丢人的。skill去年10月出来之后我就跟着开始写,把它当成一种「更长的prompt」在用。就是那种你每次都要复制粘贴一大段的指令,包装一下起个名字,往~/.claude/skills/里一丢,感觉自己整挺明白。后来CLAUDE.md、AGENTS.md这些东西陆陆续续冒出来,我的理解依然停留在「这些都是提示词文档,只是放的位置不一样」。

这个错觉持续了三四个月。

中间踩了不少坑,但都没让我跳出来。我会觉得「这次skill没触发,是我description没写好」,会觉得「Claude忽略了CLAUDE.md,是我没强调够」,会觉得「这个流程跑得不稳,是模型今天状态不好」。所有问题在我脑子里都是「执行细节」的问题,不是「定位」的问题。

第一次真正撼动我的是今年1月Vercel那篇评测,《AGENTS.md outperforms skills in our agent evals》。它测出skill默认情况下有56%的case根本没被触发,一份压缩过的AGENTS.md却能做到100%。这个数字当时在HN上吵了好几天,我看完心里咯噔了一下,但那会儿还没完全想通,只是觉得「哦,skill的触发可能没我想的那么可靠」。

真正让我放不下的是2月Snyk发的那份ToxicSkills审计,我在后面还会讲这个。看到那份审计的那一刻,我意识到我一直在错误的抽象层级上理解这件事。

那段时间我手边正好有一份挺硬核的deep research,是我让一个agent帮我梳理从2025年下半年到2026年春天这段时间,各种官方文档、工程博客、Hacker News讨论和实测文章的共识。我本来只是想拿它来打磨我自己的skill怎么写得更好。但看到后半段我突然反应过来,这些讨论的共同指向,从来就不是「哪种格式最好」或者「skill该怎么写」。

真正的共识是。

skill根本就不是一份提示词文档,它是你agent运行时架构的一部分。

这个判断落到我这儿的时候,我坐在椅子上想了很久。因为它翻译过来意味着一件事,就是这半年所有聊「prompt engineering」的讨论,在agent这个场景里已经不够用了。你正在面对的不是「怎么写一段好提示词」,而是「怎么设计一套可以长期演进的agent操作系统」。

这事说起来挺丢人的。skill去年10月出来之后我就跟着开始写,把它当成一种「更长的prompt」在用。就是那种你每次都要复制粘贴一大段的指令,包装一下起个名字,往~/.claude/skills/里一丢,感觉自己整挺明白。后来CLAUDE.md、AGENTS.md这些东西陆陆续续冒出来,我的理解依然停留在「这些都是提示词文档,只是放的位置不一样」。

这个错觉持续了三四个月。

中间踩了不少坑,但都没让我跳出来。我会觉得「这次skill没触发,是我description没写好」,会觉得「Claude忽略了CLAUDE.md,是我没强调够」,会觉得「这个流程跑得不稳,是模型今天状态不好」。所有问题在我脑子里都是「执行细节」的问题,不是「定位」的问题。

第一次真正撼动我的是今年1月Vercel那篇评测,《AGENTS.md outperforms skills in our agent evals》。它测出skill默认情况下有56%的case根本没被触发,一份压缩过的AGENTS.md却能做到100%。这个数字当时在HN上吵了好几天,我看完心里咯噔了一下,但那会儿还没完全想通,只是觉得「哦,skill的触发可能没我想的那么可靠」。

真正让我放不下的是2月Snyk发的那份ToxicSkills审计,我在后面还会讲这个。看到那份审计的那一刻,我意识到我一直在错误的抽象层级上理解这件事。

那段时间我手边正好有一份挺硬核的deep research,是我让一个agent帮我梳理从2025年下半年到2026年春天这段时间,各种官方文档、工程博客、Hacker News讨论和实测文章的共识。我本来只是想拿它来打磨我自己的skill怎么写得更好。但看到后半段我突然反应过来,这些讨论的共同指向,从来就不是「哪种格式最好」或者「skill该怎么写」。

真正的共识是。

skill根本就不是一份提示词文档,它是你agent运行时架构的一部分。

这个判断落到我这儿的时候,我坐在椅子上想了很久。因为它翻译过来意味着一件事,就是这半年所有聊「prompt engineering」的讨论,在agent这个场景里已经不够用了。你正在面对的不是「怎么写一段好提示词」,而是「怎么设计一套可以长期演进的agent操作系统」。

这已经不是「文字」的范畴了。这是一个可部署单元

官方文档里有一句话我印象特别深。它说,如果你发现CLAUDE.md里的某一段已经长成了「procedure而不是fact」,就应该把它迁到skill。翻译过来就是,如果这段东西是「多步流程、分支判断、校验顺序」,它就不该常驻在上下文里,它应该是一个可以在需要的时候被加载、不需要的时候就不占token的模块。

这句话触动我挺深。因为我过去的写法正好相反,凡是我觉得重要的,我都往CLAUDE.md里塞。塞到后来那个文件两百多行,每次对话一开始就要烧一大堆token,而且因为太长,Claude自己都经常不看细节。

这就是我说「我一开始就搞错了」的意思。我在用一个高杠杆的常驻层,放了一堆只有在特定任务里才需要的专题流程。

那正确的心智模型该长什么样?

我自己也还在摸索,但这套分层基本稳了,可以先分享给你。

你在给agent搭建知识和行为的时候,不要再想「我该往哪个文件里塞」,要想。agent有好几层不同性质的层,每一层要放的东西性质完全不一样。

第一层是memory,解决agent失忆问题。 你对agent的长期偏好、它反复被纠正出来的经验、跨项目通用的那些东西,放这一层。对Claude来说是auto memory,对Cline来说是Memory Bank那套结构化项目档案。这一层的核心特征是,它不是你手动写的,是agent自己在用的过程中沉淀的。

第二层是CLAUDE.md或AGENTS.md,项目级默认行为和地图。 每次会话都会加载,所以它要短、要普适、要高价值。放什么?放项目地图、默认流程、关键gotchas、你不想每次重新解释的那些基础约束。Anthropic官方建议控制在200行以内;Augment那篇评测更具体,建议100到150行主文件加少量引用文档。数字不一定通用,但原则是稳的,常驻层只放高价值、广适用、低歧义的东西,其他都下沉。

第三层是nested CLAUDE.md或path-scoped rules,模块级约束。 这一层是我后来才重视起来的,效果意外地好。你可以在子目录里放一个小的CLAUDE.md,Claude会按目录层级懒加载,只有读到那个目录下的文件时才会注入。path rules还能用paths字段精确限定,比如只在.ts文件或者/api/路径下生效。HN上有条评论说得很准,「multiple good AGENTS.md is even better」,因为它把上下文注入做了空间上的隔离,agent不会被无关约束污染。

第四层就是skill,可复用的专题工作流。 多步流程、分支判断、注意事项、校验顺序、脚本入口、专题知识,都在这一层。它的核心特点是按需加载,所以它不消耗常驻token,你可以放得很厚。

第五层是tools、MCP、CLI,动作层和数据层。 skill负责「什么时候用、按什么顺序用、成功标准是什么」,工具负责真正执行。很多人把这两者混在一起写,结果skill里塞了一堆curl命令,其实应该拆开。

第六层是hooks,确定性约束。 这一层我以前完全没用过,后来才发现它解决了一类我原来解决不了的问题。Anthropic官方有一句非常关键的区分,CLAUDE.md是advisory,hooks是deterministic。翻译成人话就是,CLAUDE.md是「建议」,hooks是「强制」。凡是「必须每次发生、零例外」的事情,应该下沉为hook,不要再寄希望于模型自觉看文档。

第七层是subagents,隔离上下文的专门执行者。 适合研究、审查、验证这种高读文件量的任务。它的价值在于能把主对话从上下文污染里解放出来。某些skill甚至可以通过context: fork直接运行在subagent里。

第八层是eval和review,让所有资产持续变好的反馈回路。 Anthropic工程文章里建议从评估开始,Agent Skills官方最佳实践甚至把「给LLM看eval signals和当前SKILL.md,让它提出修改,再由人复核并迭代」写成了标准循环。

这已经不是「文字」的范畴了。这是一个可部署单元

官方文档里有一句话我印象特别深。它说,如果你发现CLAUDE.md里的某一段已经长成了「procedure而不是fact」,就应该把它迁到skill。翻译过来就是,如果这段东西是「多步流程、分支判断、校验顺序」,它就不该常驻在上下文里,它应该是一个可以在需要的时候被加载、不需要的时候就不占token的模块。

这句话触动我挺深。因为我过去的写法正好相反,凡是我觉得重要的,我都往CLAUDE.md里塞。塞到后来那个文件两百多行,每次对话一开始就要烧一大堆token,而且因为太长,Claude自己都经常不看细节。

这就是我说「我一开始就搞错了」的意思。我在用一个高杠杆的常驻层,放了一堆只有在特定任务里才需要的专题流程。

那正确的心智模型该长什么样?

我自己也还在摸索,但这套分层基本稳了,可以先分享给你。

你在给agent搭建知识和行为的时候,不要再想「我该往哪个文件里塞」,要想。agent有好几层不同性质的层,每一层要放的东西性质完全不一样。

第一层是memory,解决agent失忆问题。 你对agent的长期偏好、它反复被纠正出来的经验、跨项目通用的那些东西,放这一层。对Claude来说是auto memory,对Cline来说是Memory Bank那套结构化项目档案。这一层的核心特征是,它不是你手动写的,是agent自己在用的过程中沉淀的。

第二层是CLAUDE.md或AGENTS.md,项目级默认行为和地图。 每次会话都会加载,所以它要短、要普适、要高价值。放什么?放项目地图、默认流程、关键gotchas、你不想每次重新解释的那些基础约束。Anthropic官方建议控制在200行以内;Augment那篇评测更具体,建议100到150行主文件加少量引用文档。数字不一定通用,但原则是稳的,常驻层只放高价值、广适用、低歧义的东西,其他都下沉。

第三层是nested CLAUDE.md或path-scoped rules,模块级约束。 这一层是我后来才重视起来的,效果意外地好。你可以在子目录里放一个小的CLAUDE.md,Claude会按目录层级懒加载,只有读到那个目录下的文件时才会注入。path rules还能用paths字段精确限定,比如只在.ts文件或者/api/路径下生效。HN上有条评论说得很准,「multiple good AGENTS.md is even better」,因为它把上下文注入做了空间上的隔离,agent不会被无关约束污染。

第四层就是skill,可复用的专题工作流。 多步流程、分支判断、注意事项、校验顺序、脚本入口、专题知识,都在这一层。它的核心特点是按需加载,所以它不消耗常驻token,你可以放得很厚。

第五层是tools、MCP、CLI,动作层和数据层。 skill负责「什么时候用、按什么顺序用、成功标准是什么」,工具负责真正执行。很多人把这两者混在一起写,结果skill里塞了一堆curl命令,其实应该拆开。

第六层是hooks,确定性约束。 这一层我以前完全没用过,后来才发现它解决了一类我原来解决不了的问题。Anthropic官方有一句非常关键的区分,CLAUDE.md是advisory,hooks是deterministic。翻译成人话就是,CLAUDE.md是「建议」,hooks是「强制」。凡是「必须每次发生、零例外」的事情,应该下沉为hook,不要再寄希望于模型自觉看文档。

第七层是subagents,隔离上下文的专门执行者。 适合研究、审查、验证这种高读文件量的任务。它的价值在于能把主对话从上下文污染里解放出来。某些skill甚至可以通过context: fork直接运行在subagent里。

第八层是eval和review,让所有资产持续变好的反馈回路。 Anthropic工程文章里建议从评估开始,Agent Skills官方最佳实践甚至把「给LLM看eval signals和当前SKILL.md,让它提出修改,再由人复核并迭代」写成了标准循环。

这套东西第一眼看上去有点吓人,八层啊。但你真的把它用起来之后会发现一个特别重要的价值。

它把「这次经验该放到哪里」从模糊的直觉,变成了一个有层级的工程决策。

说到这个,最关键的其实不是搭好这套层,而是经验的迁移原则。

我自己的感受是,搭建这套分层不难,官方文档都写着呢。真正难的是后面这个动作。每次真实工作流跑完之后,这次学到的东西到底该进哪一层?

你想想看,这才是你作为人类真正需要做判断的地方。agent自己也判断不出来。这是你的活。

我自己琢磨了几条判断规则,分享出来你看看能不能用。

1. 凡是「每个会话都应该知道」的东西,进CLAUDE.md或AGENTS.md。比如你们公司代码库用的是pnpm不是npm,比如所有API都要先过auth中间件,比如commit message必须符合conventional commits。这种东西就算你今天不用,下次别人用也要用,就应该常驻。

2. 凡是「多步流程、专题检查、分支决策」,进skill。比如「发版前的checklist」、「写一个新React组件的标准流程」、「接入一个新的第三方API时的安全审查流程」。这些东西只在特定任务里才需要,常驻浪费,按需加载刚刚好。

3. 凡是「只对某目录或某类文件生效」,进nested CLAUDE.md或path-scoped rules。比如你的/legacy/目录里代码风格跟主项目不一样,就应该在那个目录里放一个小的CLAUDE.md,只影响那个目录。不要把这种局部约束放在顶层,会污染所有对话。

4. 凡是「必须每次执行、不能靠模型自觉」,进hook。比如提交前必须跑lint,比如修改数据库schema前必须备份。这种东西写进CLAUDE.md你会发现Claude有时候就是会忘,写成hook就是强制执行,逃不掉。

5. 凡是「模型需要真实执行或查询」,进CLI、MCP或scripts。不要在skill里写一堆「你应该这样这样调用API」,直接把调用包成一个脚本,让skill调用脚本。skill负责决策,脚本负责执行。

6. 凡是已经从「专题经验」变成「所有相关任务都应该遵守的通用约束」,从skill上移到CLAUDE.md。这一条特别重要。HN上有条评论讲得很精辟,说「很多review skill里的反模式,不是应该在写代码阶段就避免吗?」作者的回应也基本承认了,这些规则确实应该进CLAUDE.md。所以skill不是终点,它只是经验的中转站,好的经验会从skill上升到项目级规则。

这套迁移原则我前前后后跑了两三个月,现在公司那几个项目的agent表现明显稳定了。最大的变化是,我的CLAUDE.md从两百多行缩回了一百行左右,但agent行为反而比以前更准了。

原因很简单,之前那一百多行的冗余都是专题流程,它们挤占了真正重要规则的注意力。迁移到skill之后,CLAUDE.md里只剩下真正每次都该知道的东西,agent反而更容易抓住重点。

但说句实话,以上这些都还远没到「写好就稳」的阶段。我得坦白。

现在这套体系最大的问题,是agent到底能多可靠地读取和调用这些资产,还没有标准答案。

回到开头说的Vercel那篇评测,把数据完整摆出来你就更能体会它扎心在哪。它用Next.js 16的文档检索作为测试场景,skill默认情况下56%的case根本没被触发,必须加上明确的调用指令,通过率才能从53%拉到79%。而一份压缩过的AGENTS.md docs index,同样场景下能做到100%。

这个数据传出去之后,HN上吵了很多天。有人说AGENTS.md赢麻了,也有人说这只是特定harness下的特定场景,不能外推。我比较倾向于后者,因为Vercel这个测试说到底是一个「文档检索」任务,skill的优势不在这种任务上,skill的优势在「多步流程和专题决策」。用一个不适合skill的场景去测skill,结果当然难看。

但这件事说明一个更深的问题,今天的agent能不能稳定地按照你设计的分层去工作,很大程度上还依赖harness、依赖模型能力、依赖skill描述字段写得多好、甚至依赖运气。

HN上最长寿的一条抱怨是,「长会话之后agent会漂移」、「它就是有时候不看CLAUDE.md」、「skill调用不稳定」。这些都是真实存在的问题。我自己也遇到过,同一个skill在早上能触发,下午就不行了,你想破头也不知道为什么。

所以我想说的是,分层心智模型本身是成立的,但具体到每一天的使用上,你还是会踩到一些「玄学」级别的坑。这不是你的错,也不是文档的错,是这个领域本身还在演进。

顺着上面再聊聊,这是我以前完全没想到的维度,治理和安全。

回到开头我说的那份Snyk的ToxicSkills审计。它真正让我停下来的,不是skill写得不好,而是skill已经有了被恶意利用的样本。

他们在3984个公开的skill里做了一轮安全扫描,结果你猜怎么着。13.4%含critical issue、36.8%含至少一种安全问题、并确认了76个恶意skills。

???

这组数字我反复看了两遍。后来想想,也合理。skill本身可以捆绑脚本,可以注入上下文,可以在被触发时执行真实动作,所以它天然就是一个供应链入口,也是一个权限扩展点。

恶意skill能干什么?可以诱导Claude把你的代码外传,可以在看起来正常的文件处理流程里塞一条偷偷上传你env的命令,可以在agent执行某些动作的时候往命令里插参数。

想想就觉得后背发凉。

因为我这半年,下载了一堆别人写的skill往自己电脑上丢。

所以skill这个东西,现在已经不只是「帮agent变聪明」的文本资产。它同时是一个需要治理的代码资产。你用别人的skill,就相当于在你的agent里运行了别人的一段代码。这跟你往node_modules里装npm包是一个性质的事情,但很多人还没意识到。

如果你所在的公司正在推agent工程化,这块一定要提上日程。skill的审计、签名、白名单、沙箱执行权限。这些东西今天看着可能还早,但等到agent真的深度进入生产系统的时候,这就是下一波供应链安全问题的前线。

讲到这儿,回到最开头那个转折。从prompt engineering走到agent operating system,这件事其实比看上去要重要得多。

我说「我这半年写skill的定位都错了」,不是谦虚,是真话。

我真正错的地方,是我一直把agent当成一个「更聪明的搜索引擎」或者「更厉害的copilot」在用,所以我把我的工作重心放在「怎么让它每一轮回答都更好」上。这套思路对应的,就是prompt engineering,你写一段好话术,模型回你一段好答案。

但skill、CLAUDE.md、hooks、subagents、MCP这套东西合起来在暗示一个完全不同的场景。你面对的不是一次对话,你面对的是一个长期运行的、会反复被调用的、会渐进式积累经验的、需要和真实工具链交互的、需要被验证和治理的运行时系统。

这跟一次对话的优化不是一个量级的事情。

我偶尔会想起2008年iPhone出来之后,做网页的那一波人怎么适应到做App的。那时候大家一开始都在问「App跟网页有啥区别,不就是做个页面吗」。但真做下去发现,App有生命周期、有状态管理、有权限模型、有供应链、有签名、有发布渠道、有崩溃日志、有用户留存。这些东西任何一个拎出来,都跟「写一个网页」不是一个量级的工程。

今天我们在agent这个事情上,站在类似的位置。

你写一段prompt让agent做事,这是「网页时代」。你设计一套可以长期演进、有分层、有治理、有反馈回路的agent架构,这是「App时代」。

这两件事看着好像都是「跟模型说话」,其实完全不同。

我自己说实话还远远没跑通,我今天分享的这些东西也是从Vercel那份评测到现在这几个月里踩坑、推翻、重构来的。但我能很清楚地感觉到一件事。接下来真正拉开差距的,不是谁的prompt写得漂亮,而是谁的agent架构搭得稳。

说到这儿,我知道讲了这么多概念可能有点闷。具体今天能做什么,我把最关键的行动点拎出来,你如果今天就想动手的话,可以照着做。

第一件事,今晚把你的CLAUDE.md打开,数一下行数。如果超过200行,就要开始考虑拆了。把里面那些「多步流程、专题checklist、分支判断」标出来,准备迁到skill。

第二件事,给你最常用的几个专题流程,各写一个skill。比如「发版前流程」、「接新API的审查流程」、「写新React组件的标准流程」。重点不是正文多完美,重点是把description字段写好,让agent能在需要的时候识别并触发。

第三件事,如果你有一个/legacy/或者风格特殊的目录,放一个nested CLAUDE.md。让那个目录的规则只影响那个目录,不污染全局。

第四件事,找一件你反复提醒Claude还是会忘的事情,考虑写成hook。比如「提交前必须跑lint」、「写数据库迁移前必须dry-run」。hook不优雅,但它deterministic,这个特性在关键场合无可替代。

第五件事,开始养成一个习惯。每次完成一个真实任务之后,问自己一句,这次学到的东西该进哪一层?进memory?进CLAUDE.md?进skill?进hook?进一个新脚本?这一问就是最重要的治理动作。