1. 从一次"越写越差"的Skill说起
去年下半年开始,我陆续给自己常用的几个Agent写了不少Skill。所谓Skill,说白了就是给Agent预置的一套能力包——一段结构化的提示词、若干工具调用约定、加上一些边界条件说明,让它在特定任务上表现得更专业。刚开始写的时候特别兴奋,感觉每加一条规则,Agent就聪明一分。于是越写越多,一个负责代码审查的Skill,从最初的两百字膨胀到三千多字,里面塞满了各种"如果遇到X就做Y""注意不要Z""优先考虑A再考虑B"。
结果呢?效果不升反降。同一个任务,早期精简版能稳定输出,臃肿版反而开始犯迷糊:该调工具的时候不调,不该解释的时候长篇大论,甚至把我写在后面的规则和前面的规则搞混。我一度以为是模型能力问题,换了几个不同的模型试,症状类似。直到我把那个Skill砍掉三分之二,只留下最核心的几条,效果立刻回来了,而且比之前任何一版都稳。
这件事让我意识到一个反直觉的结论:Skill不是写得越多越好,而是越"瘦"越强。这跟很多人对提示词工程的直觉是相反的——大家总觉得要把所有情况都覆盖到,把所有坑都提前堵上。但Agent的上下文窗口是有限的注意力资源,你塞进去的每一条规则都在跟其他规则抢注意力。规则一多,模型就开始"平均用力",反而抓不住重点。
这篇内容我想聊的就是这件事:怎么给Skill做减法,减到什么程度,减的时候依据什么判断,以及瘦身之后怎么验证效果。适合已经在用Agent做实际工作、手里攒了一堆Skill但感觉效果开始下滑的人。如果你还在Skill的"从零到一"阶段,这篇可能稍微超前一点,但提前知道"会写多"和"会写少"是两种能力,对你后面少走弯路有好处。
2. Skill为什么会"虚胖":注意力稀释的真实机制
2.1 上下文不是仓库,是工作台
很多人把上下文窗口理解成一个仓库,觉得空间够大就能往里堆。但更准确的类比是工作台:台面就那么大,你摆的工具越多,每件工具能占的位置就越小,找起来也越慢。模型在处理你的Skill时,并不是逐条严格执行,而是在整体上形成一个"行为倾向"。规则越多,这个倾向就越模糊。
我做过一个粗糙但有效的测试:同一个代码审查任务,写三个版本的Skill。
| 版本 | 规则条数 | 字数 | 首次命中关键规则的比例 |
|---|---|---|---|
| A版 | 6条 | 约400字 | 约90% |
| B版 | 18条 | 约1200字 | 约65% |
| C版 | 40条 | 约3000字 | 约40% |
"首次命中关键规则"是我自己定义的一个土指标:给Agent一个必然触发某条规则的输入,看它第一次响应有没有按那条规则来。数据不严谨,但趋势非常明显——规则条数翻倍,命中率断崖式下跌。
2.2 三条典型的"增肥"路径
回头看我自己和身边朋友的Skill,虚胖基本来自三个地方。
第一条是"补丁式叠加"。今天发现Agent漏了一个case,加一条规则;明天发现它过度执行,再加一条"但是不要……"。补丁越打越多,规则之间开始互相打架。比如你先写"遇到不确定的情况要主动询问用户",后来又写"尽量一次性给出完整答案减少往返",这两条在具体场景里就是矛盾的,模型只能猜你更想要哪个。
第二条是"知识倾倒"。把一堆背景知识、领域术语、参考文档全塞进Skill,觉得这样Agent就"懂行"了。但Skill的职责是约束行为,不是灌输知识。知识应该放在检索层或者工具返回里,按需加载,而不是常驻在Skill里占着注意力。
第三条是"防御性写作"。怕Agent犯错,于是把所有能想到的错误都写成禁令。"不要编造""不要偷懒""不要跳过验证"……这些禁令单看都对,但堆在一起就变成了噪音。模型对"不要X"这类否定指令的处理本来就弱,你写十条禁令,它可能只记住两三条,还记岔了。
2.3 一个容易被忽略的成本:维护熵增
Skill虚胖还有个隐性代价——维护成本指数上升。一个40条的Skill,你改第37条的时候,很难判断它会不会跟第12条冲突。每次模型升级或者任务场景变化,你都要重新通读一遍,而通读本身就要消耗你的注意力。久而久之,这个Skill就变成了"不敢动"的祖传代码,谁改谁背锅。
我现在的习惯是:Skill超过15条规则或者超过800字,就强制自己进入瘦身流程。这个阈值不是科学结论,是我踩坑踩出来的经验线。超过这条线,收益递减开始明显大于成本。
3. 瘦身不是删字:判断哪条规则该留的四个筛子
知道要瘦身是一回事,知道删哪条是另一回事。乱删一通,把关键约束删掉了,效果会更差。我总结了一套四层筛选法,按顺序过一遍,基本能筛出该留的核心规则。
3.1 第一筛:这条规则是"行为约束"还是"知识背景"
先问自己:这条规则是在告诉Agent怎么做,还是在告诉它是什么?
- "输出代码审查意见时,按严重程度分三级:阻断、建议、提示"——这是行为约束,留。
- "本项目的代码规范参考了某大厂的内部标准,该标准强调……"——这是知识背景,删,或者挪到检索层。
行为约束是Skill的骨架,知识背景是肌肉。骨架不能少,肌肉可以按需长。把知识背景从Skill里剥离出去,是瘦身见效最快的一刀。
3.2 第二筛:这条规则有没有"可验证的触发条件"
一条好的规则,应该能回答"什么情况下它生效"。如果一条规则你写完之后,自己都说不清它在什么场景下触发,那它对模型来说就是一条永远在后台运行的模糊指令,只会稀释注意力。
举个例子对比:
- 模糊版:"要保证输出的质量。"——什么时候算质量不够?不知道。删。
- 可验证版:"如果输出中包含代码块,必须在代码块前用一句话说明这段代码的作用。"——触发条件明确,留。
我删规则的时候有个土办法:给每条规则编一个能触发它的测试输入。编不出来的,基本就是废规则。
3.3 第三筛:这条规则是不是在"替模型做它本来就会做的事"
这是最反直觉的一筛。很多规则其实是在重复模型的基础能力。
比如"要仔细阅读用户的问题再回答"——模型本来就会读,你写这条纯属浪费。再比如"输出要符合语法"——除非你的任务对语法有特殊要求,否则这也是废话。
判断方法:把这条规则删掉,跑十个测试用例,如果效果没变化,那它本来就是废话。我一开始舍不得删,觉得"写了总比不写好",后来发现写了反而更差——因为它占用了本该给关键规则的注意力预算。
3.4 第四筛:两条规则冲突时,留哪条
瘦身过程中一定会遇到规则冲突。这时候不要试图"调和",要做取舍。
我的原则是:留那条更接近任务本质目标的,删那条更接近防御性补丁的。
比如"遇到不确定的情况主动询问用户"和"尽量一次性给出完整答案",如果这个Skill的核心目标是"高效完成代码审查",那应该留后者,删前者——因为代码审查场景下,频繁反问会打断流程,而审查意见本身可以标注"此处存疑"来替代反问。
取舍的时候要写清楚理由,最好在Skill里留一行注释说明"为什么这里选择A不选择B"。这行注释不占多少注意力,但能帮未来的你(或者你的同事)理解这个Skill的设计意图,避免下次又把它加回去。
4. 实操:把一个3000字Skill砍到600字的完整过程
光说方法不够,我把最近一次真实的瘦身过程完整还原一遍。这个Skill是给一个代码助手用的,负责"根据需求描述生成单元测试"。
4.1 瘦身前的原始Skill结构
原始版本大约3000字,结构是这样的:
- 角色设定:你是一个资深测试工程师,有十年经验……(约200字)
- 背景知识:单元测试的重要性、常见测试框架介绍、断言风格……(约600字)
- 行为规则:一共32条,涵盖输出格式、覆盖度要求、命名规范、异常处理、边界条件、mock策略……(约1800字)
- 示例:三个完整的输入输出示例(约400字)
跑起来的问题很明显:生成的测试有时候覆盖很全但格式乱,有时候格式对但漏了边界条件,稳定性很差。
4.2 第一刀:砍掉角色设定和背景知识
角色设定那200字,我直接删了。原因很简单:"资深测试工程师"这个身份对输出质量没有可验证的影响。我做过A/B测试,带不带这句角色设定,生成结果差异在噪声范围内。与其花200字塑造人设,不如把这200字的注意力预算留给行为规则。
背景知识那600字也删了。测试框架的知识模型本来就有,不需要我教。真正需要项目特定知识的地方(比如这个项目用的是哪个测试框架、断言库的版本),我改成在任务输入里动态传入,而不是常驻在Skill里。
这一刀下去,3000字变2200字。
4.3 第二刀:32条规则合并成7条
这是最费劲的一步。我把32条规则逐条过筛,过程大致是这样:
先按主题聚类,发现32条其实集中在几个大类:输出格式(9条)、覆盖度(8条)、命名(5条)、异常与边界(6条)、mock(4条)。同一大类里的规则往往在互相打架或者重复。
然后对每个大类做合并。以输出格式为例,原来9条是:
- 用markdown代码块包裹
- 代码块标注语言为python
- 每个测试函数前加注释说明测试意图
- 注释用中文
- 测试函数之间空一行
- 文件顶部加import
- import按标准库、第三方、本地分组
- 不要输出解释性文字
- 如果用户要求解释,在代码块后单独说明
合并后变成两条:
- 输出为单个python代码块,import按标准库、第三方、本地分组,测试函数前用中文注释说明意图。
- 默认只输出代码;用户明确要求解释时,在代码块后补充。
原来9条的信息量,压缩进2条,而且因为不再互相干扰,执行稳定性反而提高了。其他大类同理。32条最终合并成7条。
这一刀下去,2200字变900字。
4.4 第三刀:示例从3个减到1个
三个示例里,有两个是"锦上添花"——展示不同风格的输出。但Skill里的示例作用是锚定格式,一个足够典型的示例就够了,多出来的示例反而会让模型在"模仿哪个"上犹豫。
我保留了那个最典型的示例,另外两个删掉。900字变600字。
4.5 瘦身后的效果对比
瘦身前后我各跑了20个测试用例,人工评估三个维度:格式合规、覆盖度、边界条件处理。
| 维度 | 瘦身前通过率 | 瘦身后通过率 |
|---|---|---|
| 格式合规 | 14/20 | 19/20 |
| 覆盖度 | 15/20 | 18/20 |
| 边界条件 | 11/20 | 17/20 |
边界条件这一项提升最明显,从11/20到17/20。我分析原因是:原来6条边界规则分散在32条里,互相稀释;合并后边界处理变成一条清晰的强规则,模型更容易抓住。
这里有个反直觉的点:瘦身提升最大的往往不是"格式"这种显性规则,而是"边界条件"这种需要模型主动判断的隐性规则。因为显性规则模型本来就能靠格式惯性执行,而隐性规则需要注意力,注意力被释放出来后,隐性规则才跑得动。
5. 瘦身之后怎么验证:别只看"感觉变好了"
瘦身最大的风险是自我欺骗——你花了两小时删规则,心理上已经倾向于认为"肯定变好了",于是随便跑两个case就下结论。这种验证等于没验证。我现在的验证流程固定成三步,虽然土,但能挡住大部分自欺。
5.1 建立固定的回归测试集
每次瘦身之前,先准备一组固定的测试输入,至少15条,覆盖这个Skill的主要使用场景,外加3到5条边界场景。这组测试集要存下来,下次瘦身还用同一组,这样才能横向对比。
测试集不用很精致,我一般就是从一个文本文件里一行一条,跑的时候批量喂进去,把输出存下来人工过一遍。关键是固定,不能这次用这组、下次用那组。
5.2 用"盲评"代替"我觉得"
跑完测试集之后,把瘦身前的输出和瘦身后的输出混在一起,打乱顺序,然后逐条评估。评估的时候不要看是哪个版本产生的,只看输出本身好不好。这一步能有效消除"我改的肯定更好"的偏见。
我吃过这个亏:有一次瘦身完自我感觉良好,盲评之后发现有两个场景反而变差了——因为我把一条其实很关键的规则当成废话删了。要不是盲评,这个退化可能要到线上出问题才发现。
5.3 关注"稳定性"而不只是"最好情况"
评估的时候,除了看"最好的那条输出有多好",更要看"最差的那条有多差"。Skill的价值在于稳定,一个偶尔惊艳但经常翻车的Skill,不如一个始终80分但从不低于75分的Skill。
我的做法是给每条输出打一个粗略的分(比如1到5),然后看最低分和方差。瘦身的目标是提高最低分、降低方差,而不是提高最高分。最高分往往靠运气,最低分才反映Skill的真实下限。
6. 几个容易踩的坑和我的应对
瘦身这件事,方法不难,难在执行时的几个心理陷阱。我把踩过的坑列出来,你对照着看。
6.1 "这条规则万一有用呢"
这是最大的坑。每次要删一条规则,心里都会冒出"万一以后遇到需要它的场景呢"。应对办法是:把删掉的规则存到一个"备选池"文件里,而不是直接销毁。这样你心理上没有损失感,删起来就果断多了。备选池里的规则如果三个月内没被重新启用,就真的删掉。
我现在的备选池里躺着大概五十多条规则,真正被重新捞回来的不到五条。这个数据本身就说明问题:大部分"万一有用"的规则,其实永远用不上。
6.2 把"瘦身"做成"重写"
有些人瘦身的时候,删着删着就忍不住开始重写——把原来的规则换个说法,或者重新组织结构。结果字数没降多少,还引入了新bug。
我的纪律是:瘦身阶段只做减法,不做改写。要改写,等瘦身完成、验证通过之后,单独开一轮。两件事混在一起做,出了问题你都不知道是删错了还是改错了。
6.3 忽略了Skill之间的相互影响
如果你有多个Skill,瘦身一个的时候要注意它跟其他Skill的边界。我遇到过这种情况:把A Skill里的一条"输出用中文"删了,因为觉得这是通用要求,结果发现B Skill里假设了A的输出是中文,导致B处理出错。
应对办法是:瘦身之前先画一张Skill依赖图,标出哪些Skill之间有输入输出关系。瘦身的时候,凡是涉及跨Skill接口的规则,谨慎处理,宁可先留着。
6.4 瘦身频率过高
瘦身有收益,但也有成本——每次瘦身都要跑回归测试、盲评,很费时间。我一开始尝到甜头,恨不得每周瘦一次,结果把自己搞得很累,而且频繁改动让Skill的稳定性反而下降。
现在的节奏是:Skill稳定运行两周以上,或者明显感觉效果下滑时,才触发一次瘦身。平时不动它。稳定本身就是价值,不要为了瘦而瘦。
7. 瘦身的边界:什么时候不该瘦
最后说个容易被忽略的点:瘦身不是万能的,有些Skill就不该瘦。
如果你的Skill承担的是安全约束或者合规检查的职责,那它的规则密度本来就该高一些。这类Skill的目标不是"灵活高效",而是"不漏掉任何一条红线"。对它们做瘦身,风险远大于收益。
还有一种情况是任务本身极其复杂、分支极多。这种时候,与其把所有分支塞进一个Skill,不如拆成多个小Skill,按场景调用。拆分的本质也是一种瘦身,只不过瘦的是单个Skill的复杂度,而不是总规则数。
我现在的判断标准很简单:如果一个Skill的规则删到10条以下就开始明显出错,那说明它承载的复杂度本来就高,应该考虑拆分而不是继续瘦。瘦身和拆分是两种工具,别拿锤子当螺丝刀用。
回到最开始那个反直觉的结论——Skill瘦身后效果倍增,背后的道理其实很朴素:Agent的注意力是稀缺资源,你写的每一条规则都在花这份资源。花在废话上的多了,花在关键约束上的就少了。瘦身不是让Skill变简单,而是让Skill把有限的注意力全部用在刀刃上。这件事我踩了差不多半年的坑才想明白,希望你看完能少走点弯路。