1. 从一次"全都会、全办砸"的实战翻车说起
如果你也是做Agent落地的人,最近应该会高频撞见"agent-skills"这个词。我第一次认真研究它,不是因为在哪篇技术博客里看到了高大上的理论,而是被一次实实在在的线上事故逼的。
去年我做内部数据助手时,后端接的模型能力其实很强,常规问答、摘要、翻译都很顺,但一碰到公司内部的经营分析周报,它就开始一本正经地胡说八道。它不知道周报系统有一套私有的接口,不知道每个指标的统计口径要从哪个数据源取,更不知道月底结账日之后的历史数据不允许再修正。我最初的解决方案很朴素:把这些规则全部塞进系统提示词。结果提示词膨胀到几千字,模型倒是"记住"了不少,但副作用马上来了——它在做其他通用任务时也会被这些规则带偏,连回答用户闲聊的口吻都变得像在写述职报告。更折磨人的是,业务口径每隔几周就调整一次,我每次都要改那几大段提示词,排错和回归全靠肉眼,撑了两个版本就彻底崩了。
让我真正转向agent-skills思路的,是一次连续翻车后的复盘。我发现问题根本不在于模型不够聪明,而在于我把"知识"和"能力"混在了一个筐里。模型知道很多常识,但它并不知道"完成这份周报"这件事应该按什么步骤来、每一步调用什么工具、输出长什么样、遇到什么情况必须停下来问人。这些恰恰不是提示词该干的事,而是操作手册该干的事。
1.1 问题浮现:模型很强,系统很废
所谓"模型很强,系统很废",是最让人憋屈的一种状态。通用对话、常识推理、文本生成,模型都做得不错,可一旦落到某个具体业务场景,它就像个读了无数驾驶理论书却从没摸过方向盘的人——你说什么它都能接上,真要开起来就压线、熄火、错过路口。
这个现象的根本原因在于:业务任务的完成质量,往往不取决于模型"知不知道",而取决于系统"有没有把操作路径交代清楚"。模型天生擅长的是"续写最可能的下一段文本",而不是"按照预定规程执行一系列带约束的动作"。你给它一个模糊目标,它就会用流畅的语言掩盖执行细节的缺失,最后产出一个看起来合理、实际完全不能用的结果。
1.2 转机:把"知识"和"能力"分开封装
那次复盘我得出一个结论:Agent落地的问题,十有八九不是模型能力的天花板,而是工程组织方式的问题。我需要一种机制,能把某类具体任务的完整执行规范——触发条件、操作步骤、工具接口、输出模板、示例、边界规则——独立封装起来,让模型在需要的时候按需加载,而不是把所有东西都堆在通用Prompt里。
这就是agent-skills的核心思想。一个Skill本质上是一份"可以被智能体按需加载和执行的操作说明书",它把任务从"模型自由发挥"变成"按手册执行"。后来我逐步把内部多个高频场景都改造成这种模式,失误率肉眼可见地下来了,排查问题也终于从"猜模型在想什么"变成了"看Skill在哪一步跑偏"。
1.3 阅读地图与适用人群
这篇文章不是概念科普,而是我在这个方向上从踩坑到沉淀的全过程记录。你会看到:Skill和传统Prompt到底怎么分工、一个合格Skill的七要素是什么、我如何从零封装一个"会议纪要自动归档"的真实Skill、上线之后的调度与版本问题,以及那些文档里绝对不会写的坑。
如果你正在做Agent相关项目,被"模型什么都能聊、一干活就不靠谱"折磨得头疼,或者你已经试过Skill但总觉得不太得劲,这篇文章应该能给你一些能直接拿去用的思路。
2. Skill与传统Prompt工程的分水岭:知识是地图,技能是驾校
2.1 为什么提示词撑不住复杂任务
要理解Skill的价值,得先承认一个事实:提示词本质上是一种"静态知识注入"。它在告诉模型"世界是什么样的",而不是"你应该怎么操作"。
举一个生活化的例子:你可以花十分钟向一个人讲清楚汽车是什么、有几个轮子、方向盘怎么打,但讲完之后他依然不会开车。开车需要的是肌肉记忆、操作顺序、对突发路况的应激反应——这些没法靠一次描述完成,只能靠一套固定的练习流程反复强化。提示词就是那十分钟的"汽车知识讲解",而Skill是驾校课程。
对应到具体场景,以整理会议纪要为例:提示词可以告诉模型"要提取决策、负责人、时间点",但一次真实的归档流程里,还需要识别参会人名单、判断会议类别、套用公司文档命名规则、把任务清单按优先级排序、遇到保密内容时打标签、发现日期缺失时立刻停止而不是瞎编。这些步骤有严格的先后顺序,有的要调内部工具,有的要遵循特定输出格式。把它们全堆在提示词里,模型很容易"记了这头忘那头"。
2.2 提示词、工具调用与Skill的分工边界
我在实际项目里逐渐沉淀出一套分工原则,用表格来看比较直观:
| 维度 | 系统提示词 | 工具/函数调用 | Skill |
|---|---|---|---|
| 职责 | 定义模型身份与通用行为边界 | 提供原子能力 | 定义完成一类任务的完整操作流程 |
| 粒度 | 全局、常驻 | 单个动作 | 多步骤、带状态 |
| 内容 | 角色、禁令、通用知识 | 接口签名、参数约束 | 流程、示例、规则、应急预案 |
| 更新频率 | 低 | 中 | 高 |
| 失效影响 | 影响所有任务 | 影响单次动作 | 影响一类任务 |
这个表是我反复调过的版本。刚开始我走了另一个极端,把工具封装得特别细:一个"读取文件"工具、一个"写文件"工具、一个"搜索知识库"工具,结果模型每次都要自己编排一串调用,一旦步骤复杂就跑偏。后来我才意识到,工具是积木,Skill是图纸。积木本身再可靠,没有图纸的施工队也会把承重墙砌到阳台上。
2.3 一个合格Skill的七要素
以我维护的Skill格式为例,我要求每个技能模块包含七部分,缺一不可:
- 触发描述:什么意图、什么场景下应该激活这个技能,用于路由判断;
- 上下文通路:技能运行需要哪些外部信息,如归档目录地址、命名规则、权限级别;
- 工作流步骤:按顺序执行的操作清单,每步明确输入、处理和产出;
- 工具接口说明:步骤里可能调用的函数签名,包含参数类型和返回结构;
- 输出模板:最终结果的固定格式,方便下游解析与入库;
- 少量示例:至少两组完整的输入输出对,最好覆盖常规和异常两种情况;
- 边界规则:遇到什么情况必须停下来、什么数据绝对不能碰。
第七点最容易被忽略,但它恰恰是生产环境里最值钱的部分。没有边界规则的Skill,就像一条没有刹车系统的自动化流水线,正常情况跑得飞快,一遇到异常就会带着错误一路狂奔,直到把整个生产线都污染了才停。
3. 手把手封装一个Skill:以"会议纪要自动归档"为例
3.1 场景与能力边界
为了让思路可落地,我用一个具体的模拟场景来演示:某个产品团队每周有例会,会后需要把会议原始记录整理成统一的归档文档,存进团队知识库,并自动产出任务清单。
第一步不是写代码,而是划清能力边界。我给这个Skill定的输入是"会议原始文本或录音转写稿",输出是"一份符合团队规范的Markdown归档文件 + 一份结构化任务清单"。它不做的事情也很明确:不负责发邮件、不负责更新日历、不负责替管理者判断人事问题。边界写得越清楚,模型就越不容易越界干活。
3.2 收集业务约定:Skill里的"国情"
Skill能不能跑赢,往往取决于你有没有把那些"团队里默认大家都知道"的约定写进去。这个场景里我收集了三类:文档命名规则(日期-团队名-会议类型)、归档目录结构(按季度分目录)、标签体系(决策/待办/风险/知会)。
这些约定看似简单,但不写进去,模型就会自己发明一套。我见过最离谱的输出,是模型自创了一个"会议纪要-归档版-最终-更新版"这样的文件名,把团队命名规范全扔了。这个问题的根源不是模型笨,而是它根本不知道有这么一套规则存在。
3.3 技能清单的骨架写法
下面是我在这个场景下维护的Skill清单主干,省略了部分字段,只保留结构感:
name: meeting_minutes_archive version: "2.1" description: 将会议原始记录整理为团队归档文档并输出任务清单 trigger: intent_tags: [会议纪要, 会议记录, 例会整理] scope: 仅当输入是会议原始文本或转写稿时启用 context: naming_rule: "{YYYY-MM-DD}-{团队名}-{会议类型}.md" archive_root: /data/shared/meetings quarterly_dir: true tag_pool: [决策, 待办, 风险, 知会] workflow: - step: extract_meta action: llm_extract fields: [meeting_date, team_name, meeting_type, attendees] - step: check_required action: validate required: [meeting_date, attendees] on_fail: emit_error_code "MISSING_REQUIRED_FIELD" - step: summarize action: llm_extract fields: [decisions, action_items, risks, info_items] - step: format_doc action: render_template template_path: ./templates/meeting_minutes.md - step: write_archive action: tool:file_write base_dir: {context.archive_root} path_pattern: "{quarter}/{naming_rule}" output_schema: type: object properties: doc_path: { type: string } action_items: { type: array, items: { person, deadline, desc } } examples: - input: | 今天产品组周会,参加人:A同学、B同学、C同学。讨论了新版首页改版进度,决定下周三前完成视觉稿,B同学负责对接设计,风险是设计资源可能不够。 output: | meeting_date: 今日日期 decisions: ["下周启动首页视觉评审"] action_items: [{"person": "B同学", "deadline": "下周三", "desc": "完成视觉稿对接设计"}] risks: ["设计资源可能不足"] guardrails: - 当会议日期缺失时,禁止自动猜测,必须输出MISSING_REQUIRED_FIELD并向用户索要 - 只允许写入archive_root目录内部,禁止修改归档以外的任何文件3.4 示例与异常项为什么要成对写
清单里的examples部分我特意放了一组正常输入和一组异常输入。只写正面示例,模型可能不理解输出格式;但只写正面示例,模型就会默认"所有输入都能成功归档"。我把异常示例称为"反面刹车片"——它让模型知道在什么情况下应该亮红灯,而不是硬着头皮编一个日期出来。
实际测试中我发现,模型面对异常输入时最自然的反应就是"补全":日期缺失就推断日期,人员缺失就从上下文猜人员。这种行为对人类来说叫"合理推测",但在归档场景里就是数据事故。所以异常示例用词必须非常强硬,我通常直接写"任何猜测都是违规行为"。
3.5 验证:先跑单步骨架再跑端到端
Skill写完后,我不会直接接到主流程上。我搭了一个很简单的干跑测试台:把一段真实会议记录喂进去,让运行时只加载这个Skill,然后把每一步的中间结果全部打印出来。先验证步骤二能否正确识别"缺日期",再验证步骤五的文件路径是否符合命名规则。全部通过之后,才挂到带完整路由的Agent环境里做端到端测试。
这一步帮我筛掉了至少一半的早期问题。很多错误根本不是模型能力问题,而是清单里某个字段写错了、路径模板拼错了、或者示例里的输出和output_schema对不上。这些低级错误在端到端环境里极难定位,但在干跑测试台里一眼就能看见。
4. Skill调度与运行时设计:不是越多越好,而是用对时机
4.1 选路:关键词、向量召回还是让模型自己选
Skill多了之后,第一个问题就是"任务来了,该加载哪个技能"。我试过三种方案。第一种是纯关键词匹配,简单但脆弱,换个说法就漏匹配;第二种是把每个Skill的描述做成向量索引,用语义检索召回,泛化能力好一些;第三种是把路由决定权交给模型本身,让它在系统提示词里看到技能清单后自行选择。
我最终采用的是混合路由:系统提示词里常驻一份"技能索引",每个技能只有一两行描述;运行时先用关键词做粗筛,再用向量召回做细筛,最后把候选技能的完整清单交给模型裁决。这样既避免了全部技能都加载造成的上下文浪费,又保留了模型对歧义任务的处理能力。
这里有个容易被忽略的点:技能索引本身要控制篇幅。如果一百个技能全塞进去,索引可能比任务上下文还长,模型光看索引就开始犯迷糊。我目前的经验是索引描述严格控制在二十字以内,并且按使用频率排序,高频技能永远排在前面。
4.2 上下文预算:技能不是越多越聪明
一个完整Skill清单动辄上千字,如果在一次会话里并行加载五六个技能,token消耗会直接起飞,而且模型在长上下文里的指令遵循精度会明显下降。我实测过同一个任务,当上下文里额外塞入三个无关技能时,输出格式错误率从百分之三涨到了百分之十几。
所以我的原则是:前置加载最可能命中的一到两个技能,其余技能只在模型确认需要时再加载。所有技能文件放在独立目录里,运行时通过索引路径按需读取,而不是一股脑全灌进上下文。这个设计和数据库索引的思路很像——索引文件小、查询快,数据文件大、按需读。
4.3 版本管理与冲突裁决
Skill一定会迭代。我维护的规则是每个技能清单里带version字段,运行时在日志里记录每次执行加载的版本号。这样复盘时能精确知道"这个错误是版本2.0引入的"还是"版本1.9本来就有"。没有版本管理的Skill库,时间一长就是一团乱麻。
另一个常见问题是技能冲突。我遇到过一个真实场景:一个是"通用文档整理"技能,一个是"会议纪要归档"技能,同时输入一份会议原始记录后,两个技能都认为自己该干活。最后的解法不是削弱任何一个,而是在两个技能里都加一条互斥规则:会议类输入优先走归档技能,非会议类文档才走通用整理。裁决规则写在guardrails里,模型看到冲突时就能自动按优先级执行。
4.4 不该做成Skill的四类东西
最后聊聊边界。不是所有任务都适合封装成Skill,下面四类我坚持用别的方式处理:
- 每次输入都不一样的临时任务,直接用对话完成,封装反而增加负担;
- 只涉及单次确定性操作的场景,写成工具函数更合适;
- 需要强权限管控的操作,不要让模型自由调用,应由外部流程强制审批;
- 业务规则每天都在变且无法收敛的任务,先固化数据模型,别急着封装流程。
把这条想清楚,能少走很多弯路。Skill不是万能的,它是"高频、重复、规则相对稳定"这类任务的专属工具。
5. 我在生产环境里踩过的坑:冗长反噬、强迫完成、只测正路径
5.1 坑一:技能清单太详细,模型被"说明书"压垮
第一个大坑来自我的过度设计。当时为了让Skill"更智能",我一股脑写了两千多字,把每一步的思考提示、注意事项、历史变更全塞进去。结果上线后接口延迟明显上升,模型经常在第一步就开始犹豫不决,输出质量反而下降。
复盘之后我把清单拆成两层:一层是"激活卡",只保留触发描述、关键约束和工作流步骤,控制在五百字以内,用于模型加载;另一层是完整参考文档,模型遇到清单内未覆盖情况时按需读取。这个改动效果立竿见影,延迟降了接近一半,错误率也明显回落。我现在写任何Skill都默认遵循"激活卡从简、参考文档从全"的原则。
5.2 坑二:模型宁可编造数据也要"完成任务"
这是最危险的一个坑。有一次归档测试里,我故意输入了一段没有明确日期的会议记录,期望模型触发MISSING_REQUIRED_FIELD。结果模型直接根据上下文"推断"了一个日期,还像模像样地写进了归档文档。原因很直接:模型被训练得倾向于"完成任务",而"承认自己做不到"需要额外的激励。
我的解决办法有两层。第一层是在guardrails里用强指令写明:缺失必填字段时,唯一允许的输出是错误码,任何猜测都是违规行为。第二层是在运行时增加独立的字段校验器,归档前发现日期与参会人等必填字段为空时直接拦截并改写错误码,不让错误文档落盘。靠代码兜底,而不是靠模型自觉。
提示:凡是涉及数据写入、资金操作、权限变更的Skill,都必须配套独立的运行时校验逻辑。模型的"自觉"是概率事件,不该作为唯一防线。
5.3 坑三:只测正路径,几乎让我上线当天返工
刚开始我建测试语料时清一色是"格式规整的正常会议记录",正路径跑得特别好。结果真上线半天就收到了投诉——有个会议转写稿里时间用的是"周三下午",人员用的是"产品组所有人",跟示例里的格式完全不同。Skill本身正常启动了,但后续所有步骤全都跑歪。
现在我的回归语料里固定包含几类"毒样本":时间描述模糊的、参会人用群体称呼的、内容涉及敏感信息的、空文档、以及中英文混杂的输入。这些样本不追求全部成功归档,追求的是"要么正确处理,要么明确报错,绝不能静默产出错误结果"。
5.4 沉淀下来的调试套路
踩过这些坑后,我整理出一套相对固定的调试流程:
- 用原始输入原样复现,打开完整工具调用日志;
- 把执行过程与Skill清单的步骤逐一比对,找出第一个偏差点;
- 每次只改一个变量——要么改清单内容,要么改示例,要么改运行时校验逻辑;
- 跑一遍回归语料,确认修复没有破坏其他正常场景。
"每次只改一个变量"这条,是我最想强调的。我早期经常一次改三四处,问题确实消失了,但搞不清是哪一处起了作用,下一次遇到类似问题照样抓瞎。后来强制自己一次只动一处,调试效率反而高得多。
5.5 一点个人体会
做了这么多Skill的封装和调试之后,我最深的感受是:agent-skills这套思路真正的价值,不在于让模型"更聪明",而在于让工程团队对模型行为有了可控、可测、可回滚的抓手。每一个技能都是一份被反复打磨的操作手册,模型的任务从"自由发挥"变成了"按手册执行,遇到手册没写的场景就停下来问人"。
如果你现在也被"大模型什么都能聊、一干活就不靠谱"困扰着,我建议从你团队里最高频、最重复的一类任务开始,先做一个极小的Skill,把触发、流程、示例、边界四件套写全,然后用真实数据跑一轮干测试。这个过程本身,就是理解agent-skills最好的起点。