1. 为什么"能跑通"的SKILL离"好用"还差着十万八千里
我最早接触SKILL这套东西的时候,心态特别简单:能跑就行。写个脚本,把输入丢进去,拿到输出,任务完成,收工。直到有一次我把一个自己觉得"挺完善"的SKILL交给同事复用,对方用了十分钟就回来找我,说这玩意儿根本没法用——参数写死在代码里、报错信息全是天书、换个输入格式直接崩。那一刻我才意识到,能跑通和好用之间,隔着的不是一行代码,而是一整套工程思维。
Anthropic官方那套关于SKILL的最佳实践,我前后读了三遍,第一遍觉得"这不废话吗",第二遍觉得"好像有点道理",第三遍在自己踩了一堆坑之后再读,才真正品出味道来。它讲的三个技巧,表面上看都是些不起眼的小事,但恰恰是这些小事,决定了你的SKILL是一个"一次性玩具"还是一个"能长期复用的工具"。
这篇文章我想聊的就是这三个技巧背后的逻辑,以及我在实际项目里怎么把它们落地。不管你是刚接触SKILL的新手,还是已经写过几十个SKILL的老手,我相信都能从中找到一些之前忽略的细节。核心关键词就三个:渐进式披露、闭环控制、可组合性。这三个词听起来有点抽象,但拆开来看,每一个都对应着非常具体的操作。
先说清楚适用人群:如果你只是写个一次性脚本处理一下手头的数据,那这篇文章可能对你帮助有限,因为一次性脚本确实不需要考虑这么多。但如果你想让自己的SKILL被别人复用、被Agent调用、或者在未来某个时间点自己还能看懂,那这三个技巧就是绕不过去的坎。
我见过太多人(包括早期的我自己)把SKILL当成"代码片段"来写,写完就扔,下次要用再重新写一遍。这种模式下,你的每一份工作都在重复造轮子,而且造出来的轮子还都不一样。真正高效的SKILL工作流,应该是写一次、用一百次、每次都能稳定输出。这中间的差距,就是今天要聊的内容。
2. 渐进式披露:别让SKILL一上来就把所有底牌亮出来
2.1 什么是渐进式披露,为什么它决定了SKILL的可用性
渐进式披露这个词,直译自"progressive disclosure",是交互设计里的一个经典概念。放到SKILL的语境下,它的意思是:SKILL不应该在第一次被调用时就把所有能力、所有参数、所有分支逻辑全部暴露出来,而应该根据实际需要,一层一层地展开。
我举个生活化的例子。你去餐厅吃饭,服务员不会一上来就把整本菜单从头到尾念一遍,而是先问你"几位""有没有忌口",然后根据你的回答推荐几个招牌菜,你感兴趣了再详细介绍。这个过程就是渐进式披露。反过来,如果服务员一上来就念了二十分钟菜单,你大概率会直接走人。
SKILL也是一样的道理。我早期写的一个数据清洗SKILL,参数列表有十七个,从编码格式到缺失值处理策略到异常值阈值全都有。结果就是,每次调用它,我都得翻半天文档确认哪个参数该填什么。更糟糕的是,当这个SKILL被Agent调用时,Agent面对十七个参数直接懵了,经常填错或者漏填。
Anthropic官方实践里强调的渐进式披露,核心就是解决这个问题。它建议把SKILL的能力分成几个层次:
- 第一层:核心能力。这是SKILL最基础、最常用的功能,参数应该尽可能少,最好控制在三个以内。这一层要保证"闭着眼睛都能调对"。
- 第二层:扩展能力。在核心能力的基础上,提供一些可选的配置项,用于处理特殊情况。这些配置项应该有合理的默认值,不填也能正常工作。
- 第三层:高级能力。针对非常具体的边缘场景,提供细粒度的控制。这一层通常只有高级用户才会用到,文档可以写得更技术化一些。
这样分层之后,新手用第一层就能完成80%的任务,老手需要精细控制时再往下挖。每一层都是独立的、可用的,而不是说必须全部理解才能用。
2.2 我在实际项目里怎么落地渐进式披露
说个具体的例子。我做过一个"简历筛选工作流"的SKILL,最早版本把所有筛选规则都写成了必填参数:学历要求、工作年限、技能关键词、行业背景、薪资范围……一共十一个参数。结果就是,每次用都得填一大堆,而且很多参数其实大部分时候用不上。
后来我按照渐进式披露的思路重构了:
第一层只保留两个参数:resume_text(简历文本)和job_description(职位描述)。调用方只需要把这两样东西丢进来,SKILL会自动做基础匹配,输出一个匹配度评分和关键差异点。这一层覆盖了大概70%的日常需求。
第二层增加了strict_mode(是否严格模式)和focus_areas(重点关注领域)两个可选参数。如果调用方觉得基础匹配不够精准,可以开启严格模式,或者指定只关注某几个维度(比如只看技术栈匹配度,不看学历)。
第三层才是完整的规则配置,包括自定义评分权重、自定义关键词库、自定义排除规则等等。这一层我用一个单独的配置文件来管理,而不是塞进参数列表里。
重构之后的效果非常明显。日常使用我只调第一层,两秒钟搞定;遇到特殊需求再往下挖。更重要的是,当这个SKILL被Agent调用时,Agent只需要理解两个核心参数就能跑起来,成功率大幅提升。
这里有个实操心得:渐进式披露的层次划分,不是拍脑袋决定的,而是要根据实际使用频率来定。我的做法是,先记录自己一个月内调用这个SKILL的所有场景,统计每个参数被用到的次数,然后按频率从高到低分层。用不到的参数直接砍掉,用得少的放到第三层。
2.3 渐进式披露的常见误区
第一个误区是把渐进式披露理解成"藏起来"。有些人觉得,我把复杂参数藏到配置文件里,用户看不到就不用管了。这是错的。渐进式披露的核心是"按需展开",而不是"故意隐藏"。该有的文档还是要写,该有的提示还是要给,只是呈现的时机和顺序变了。
第二个误区是层次划分太细。我见过有人把SKILL分成七八层,每层就一两个参数。这就过度设计了。我的经验是,三层足够了:核心层、扩展层、高级层。超过三层,用户记不住,维护起来也麻烦。
第三个误区是忽略默认值的质量。渐进式披露能成立的前提是,每一层的默认值都是经过验证的、合理的。如果默认值很糟糕,用户被迫每次都去调第二层、第三层,那渐进式披露就形同虚设。所以,花时间打磨默认值,比花时间增加参数更重要。
3. 闭环控制:让SKILL自己知道"做得好不好"
3.1 开环SKILL和闭环SKILL的本质区别
闭环控制这个词我是从控制系统里借来的。在控制理论里,开环系统是指没有反馈的系统,输入进去,输出出来,中间不管结果对不对。闭环系统则会把输出的一部分反馈回来,和预期目标做比较,然后调整输入,直到输出达到预期。
大部分人的SKILL都是开环的。输入进去,处理一下,输出出来,至于输出质量怎么样,SKILL自己不知道,也不关心。调用方拿到结果后,如果发现不对,只能重新调用一次,或者手动修改。
闭环SKILL则不一样。它会在内部设置检查点,对中间结果和最终结果进行评估,如果发现不达标,会自动调整策略重试,或者至少给出明确的警告。
我举个实际例子你就明白了。我做过一个"Markdown转Word工作流"的SKILL,最早版本就是开环的:读Markdown,解析,生成Word,输出。看起来很顺畅,但实际用起来问题很多——有时候表格转换错位,有时候代码块格式丢失,有时候中文字体不对。每次出问题,我都得手动检查、手动修复。
后来我改成了闭环:在生成Word之后,增加一个"校验"步骤,检查表格数量是否一致、代码块是否完整、字体是否正确。如果校验不通过,SKILL会自动尝试修复(比如重新解析表格、重新设置字体),修复不了就明确报错,告诉我具体哪里出了问题。
改造之后,这个SKILL的可用性提升了一个档次。以前我拿到输出还得自己检查一遍,现在SKILL自己就检查了,我只需要处理它报出来的问题就行。
3.2 闭环控制的三个关键检查点
根据我的经验,一个闭环SKILL至少要在三个地方设置检查点:
第一个检查点是输入校验。在开始处理之前,先检查输入是否符合预期。比如输入是不是空的、格式对不对、必填字段有没有缺。这一步能拦截掉大部分低级错误,避免SKILL跑到一半才崩溃。
第二个检查点是中间结果校验。在关键的处理步骤之后,检查中间结果是否合理。比如解析出来的数据结构是不是完整的、关键字段有没有丢失、数值是不是在合理范围内。这一步能及早发现问题,避免错误累积到最后。
第三个检查点是输出校验。在最终输出之前,检查结果是否符合预期。比如输出格式对不对、关键内容有没有缺失、和输入是否一致。这一步是最后一道防线。
这三个检查点不需要很复杂,有时候就是几行断言代码。但就是这几行代码,能把SKILL的可靠性提升一大截。
实操技巧:检查点的校验逻辑,最好写成独立的函数,而不是散落在主流程里。这样一方面方便复用,另一方面也方便单独测试。我通常会写一个
validate_input、一个validate_intermediate、一个validate_output,每个函数只做一件事。
3.3 闭环控制里的重试策略
闭环控制不只是"发现问题",还要"解决问题"。最常见的手段就是重试。但重试不是简单地再跑一遍,而是要有策略。
我的做法是把重试分成三类:
- 瞬时错误重试:比如网络抖动、临时资源不可用,这类错误重试一两次通常就好了。重试间隔可以短一点,比如1秒、2秒。
- 参数错误重试:比如某个参数不合适导致处理失败,这类错误需要调整参数后再重试。调整策略可以是放宽阈值、切换算法、或者降级处理。
- 致命错误不重试:比如输入格式完全不对、依赖的服务彻底挂了,这类错误重试多少次都没用,直接报错让调用方处理。
这里有个坑我踩过:早期我做重试的时候,不管什么错误都重试三次,结果遇到致命错误时,白白浪费了三次重试的时间,还产生了一堆无意义的日志。后来我加了错误分类,只有瞬时错误和参数错误才重试,致命错误直接抛出,效率高了很多。
另外,重试次数也不是越多越好。我的经验是,瞬时错误重试两次,参数错误重试一次,足够了。重试太多次,一方面浪费时间,另一方面可能掩盖真正的问题。
3.4 闭环控制如何和Agent配合
如果你的SKILL是给Agent调用的,闭环控制就更重要了。因为Agent不像人,它不会"感觉"到结果不对,它只会根据SKILL返回的信息来决定下一步。
一个闭环良好的SKILL,应该给Agent返回结构化的反馈信息,包括:任务是否成功、如果失败是什么原因、是否已经重试过、建议的下一步操作。这样Agent就能根据反馈做出合理的决策,而不是盲目地重试或者放弃。
我做过一个"GIS空间分析"的SKILL,最早版本只返回一个结果文件路径。Agent拿到路径后,如果文件是空的或者格式不对,它完全不知道该怎么办。后来我改成返回一个结构化的结果对象,包含status、message、retry_count、suggestion等字段。Agent拿到这个对象后,就能判断是继续、重试还是换一种方式。
这个改动看起来很小,但对Agent的整体成功率影响很大。Agent的智能程度,很大程度上取决于它拿到的反馈质量。SKILL作为Agent的工具,反馈质量直接决定了Agent的表现。
4. 可组合性:让SKILL像积木一样拼起来
4.1 为什么可组合性是SKILL工作流的终极形态
单个SKILL再强大,能力也是有限的。真正复杂的工作流,往往需要多个SKILL协作完成。这时候,SKILL的可组合性就成了关键。
可组合性的核心是:每个SKILL只做一件事,做好一件事,然后通过标准化的接口和其他SKILL拼接。这其实就是Unix哲学"do one thing and do it well"的翻版。
我见过很多人写SKILL,喜欢把一堆功能塞进一个SKILL里。比如一个"文档处理SKILL",既负责读取、又负责解析、又负责转换、又负责输出。这种SKILL看起来很强大,但实际上很难复用。因为下次我只需要"解析"这个功能,却不得不把整个SKILL都搬过来。
正确的做法是拆开:读取是一个SKILL,解析是一个SKILL,转换是一个SKILL,输出是一个SKILL。每个SKILL的输入输出都是标准化的,可以自由拼接。这样,我需要什么功能就拼什么功能,灵活度大大提升。
4.2 标准化接口的四个要素
可组合性的前提是接口标准化。一个标准化的SKILL接口,应该包含四个要素:
第一个要素是明确的输入格式。输入应该是一个结构化的对象,字段名清晰、类型明确。避免用位置参数,因为位置参数一旦顺序变了就会出错。我通常用JSON对象作为输入,字段名用下划线命名法,比如input_text、output_format。
第二个要素是明确的输出格式。输出也应该是结构化的对象,包含结果数据和状态信息。结果数据放在data字段里,状态信息放在status、message等字段里。这样调用方可以统一处理。
第三个要素是明确的错误格式。错误也应该是结构化的,包含错误码、错误信息、错误位置。这样调用方可以根据错误码做不同的处理,而不是去解析错误字符串。
第四个要素是明确的依赖声明。SKILL依赖哪些外部资源、哪些其他SKILL,应该在文档里写清楚。这样组合的时候才知道有没有冲突。
我做过一个"动画工作流"的项目,里面涉及十几个SKILL,从分镜生成到关键帧提取到中间帧插值到最终合成。因为每个SKILL的接口都是标准化的,我可以自由调整它们的顺序和组合方式。比如有时候我只需要"分镜到关键帧"这一段,有时候我需要完整的流程,切换起来非常方便。
4.3 可组合性带来的意外好处
可组合性除了提升复用率,还有几个意外的好处。
好处一是测试更容易。每个SKILL都是独立的,可以单独测试。测试通过了再组合,组合出问题的概率大大降低。我以前写大SKILL的时候,测试特别痛苦,因为一个地方出错,整个流程都跑不起来,很难定位。拆成小SKILL之后,每个都能单独验证,问题定位快了很多。
好处二是调试更容易。组合式的工作流,可以逐个SKILL检查中间结果。哪个环节出问题,一目了然。而大SKILL的中间状态是隐藏的,调试起来像黑盒。
好处三是替换更容易。如果某个SKILL效果不好,可以直接换掉,不影响其他部分。比如我最早用的一个"文本摘要"SKILL效果一般,后来换了一个更好的,只需要改一行配置,其他部分完全不用动。
好处四是并行更容易。独立的SKILL可以并行执行,提升整体效率。比如"简历筛选工作流"里,简历解析和职位解析是两个独立的SKILL,可以同时跑,节省时间。
这里有个经验:拆SKILL的时候,不要拆得太细。太细会导致SKILL数量爆炸,管理成本上升。我的经验是,一个SKILL的代码量控制在50到200行之间比较合适。太短说明功能太单一,太长说明功能太杂。
4.4 可组合性和渐进式披露、闭环控制的关系
这三个技巧不是孤立的,而是相互支撑的。
渐进式披露让每个SKILL的接口更清晰,这是可组合性的基础。如果每个SKILL的接口都乱七八糟,组合起来就是灾难。
闭环控制让每个SKILL的输出更可靠,这也是可组合性的基础。如果每个SKILL的输出质量都不稳定,组合起来的结果就更不可控。
反过来,可组合性也让渐进式披露和闭环控制更容易实现。因为SKILL拆小了,每个SKILL的渐进式披露层次更简单,闭环控制的检查点也更明确。
我在实际项目里的做法是:先按可组合性的思路拆SKILL,然后对每个SKILL做渐进式披露,最后给每个SKILL加闭环控制。这个顺序很重要,反过来做会很别扭。
5. 三个技巧在实际项目里的组合应用
5.1 一个完整案例:从零搭建简历筛选工作流
我把这三个技巧用在一个"简历筛选工作流"的项目里,效果很好,这里完整讲一下。
第一步是拆SKILL。我把整个流程拆成了四个SKILL:resume_parser(简历解析)、jd_parser(职位解析)、matcher(匹配打分)、reporter(报告生成)。每个SKILL只做一件事。
第二步是设计接口。四个SKILL的输入输出都是标准化的JSON对象。比如resume_parser的输入是{"resume_text": "..."},输出是{"status": "success", "data": {"name": "...", "skills": [...], "experience": [...]}}。
第三步是渐进式披露。每个SKILL都分了三层。以matcher为例,第一层只需要resume_data和jd_data两个参数,自动做基础匹配;第二层可以指定focus_areas;第三层可以自定义评分权重。
第四步是闭环控制。每个SKILL都有输入校验、中间校验、输出校验。matcher在打分之后,会检查分数是否在0到100之间,如果不在就报错。reporter在生成报告之后,会检查报告是否包含所有必填字段。
第五步是组合。四个SKILL按顺序拼接,形成一个完整的工作流。因为接口标准化,我还可以灵活调整顺序,比如先解析职位再解析简历,或者并行解析。
这个工作流跑下来,稳定性比之前的单体SKILL高了很多。以前经常出现的"跑一半崩了""结果不对但不知道哪里错"的问题,现在基本没有了。
5.2 组合应用时的注意事项
注意事项一:接口版本管理。SKILL的接口一旦被其他SKILL依赖,就不能随便改。要改的话,要么加新字段而不是改老字段,要么升版本号。我吃过这个亏,改了一个SKILL的输出字段名,结果依赖它的三个SKILL全挂了。
注意事项二:错误传播。组合式工作流里,一个SKILL的错误会传播到下游。所以每个SKILL都要明确自己的错误处理策略:是抛出错误让上游处理,还是自己降级处理。我的做法是,能自己处理的就自己处理,处理不了的才抛出。
注意事项三:性能监控。组合式工作流里,每个SKILL的耗时都要监控。哪个环节慢,一目了然。我通常会在每个SKILL的输入输出里加上时间戳,方便统计。
注意事项四:日志规范。每个SKILL的日志格式要统一,方便排查问题。我通常用[SKILL_NAME][LEVEL] message的格式,比如[resume_parser][INFO] parsing started。
5.3 从单体SKILL迁移到组合式SKILL的路径
如果你现在手里有一堆单体SKILL,想迁移到组合式,我的建议是渐进式迁移,不要一次性全改。
第一步,先挑一个最常用的单体SKILL,把它拆成两到三个小SKILL,验证一下效果。如果效果好,再继续拆其他的。
第二步,把拆出来的小SKILL的接口标准化。这一步可能需要改一些代码,但值得。
第三步,给每个小SKILL加闭环控制。这一步可以慢慢来,先加输入校验,再加输出校验。
第四步,把拆好的小SKILL重新组合成工作流,替换原来的单体SKILL。
整个过程可能要花几周时间,但迁移完之后,你的SKILL工作流会脱胎换骨。我自己的项目迁移花了大概三周,之后维护成本降低了一半以上。
6. 我踩过的坑和总结出的经验
6.1 渐进式披露踩过的坑
最大的坑是默认值设计得太随意。我早期做渐进式披露的时候,第一层的默认值就是随便填的,结果用户用第一层跑出来的结果质量很差,被迫每次都去调第二层。这就违背了渐进式披露的初衷。
后来我改了做法:第一层的默认值,必须是我自己实际用下来觉得最好的配置。我会花时间测试不同的默认值组合,选效果最好的那个。这样用户用第一层就能拿到不错的结果,只有特殊需求才需要往下调。
第二个坑是文档和实际行为不一致。渐进式披露的层次多了,文档容易写漏或者写错。我的做法是,文档直接从代码里的注释生成,保证一致性。
6.2 闭环控制踩过的坑
最大的坑是校验逻辑本身有bug。我写过一个校验函数,检查输出里的数字是否在合理范围内,结果范围写错了,把正常结果也判成了异常。这种bug很隐蔽,因为校验函数本身不常被测试。
后来我的做法是,校验函数也要写单元测试。用正常的、异常的、边缘的输入分别测试,确保校验逻辑本身是对的。
第二个坑是重试导致重复副作用。有些SKILL有副作用,比如写文件、发请求。如果重试的时候不检查副作用是否已经产生,就会重复执行。我的做法是,重试之前先检查副作用是否已经产生,如果产生了就跳过。
6.3 可组合性踩过的坑
最大的坑是接口设计得太早。我早期拆SKILL的时候,急着定接口,结果后来发现接口设计得不合理,改起来很麻烦。
后来我的做法是,先写两个SKILL的实际调用代码,再根据调用代码反推接口。这样设计出来的接口,一定是好用的。
第二个坑是SKILL之间的隐式依赖。有些SKILL看起来独立,实际上依赖了另一个SKILL的副作用。比如A SKILL写了一个临时文件,B SKILL读这个文件。这种隐式依赖很危险,一旦顺序变了就出问题。我的做法是,所有依赖都显式声明,要么通过参数传递,要么通过明确的配置文件。
6.4 三个技巧的优先级
如果非要排个优先级,我的顺序是:可组合性 > 闭环控制 > 渐进式披露。
可组合性是基础,没有它,另外两个技巧的价值大打折扣。闭环控制是保障,没有它,SKILL的可靠性上不去。渐进式披露是锦上添花,有了它,SKILL更好用,但没有它,SKILL也能用。
当然,这只是我的个人经验。不同的项目、不同的场景,优先级可能不一样。关键是要理解这三个技巧背后的逻辑,然后根据实际情况灵活应用。
最后分享一个我自己的习惯:每写完一个SKILL,我都会问自己三个问题——这个SKILL能不能被拆得更小?它能不能自己发现自己的问题?它的接口是不是足够简单?这三个问题,对应的就是可组合性、闭环控制、渐进式披露。养成这个习惯之后,我写的SKILL质量明显上了一个台阶。