上一篇写 MCP 的时候留了个尾巴——工具多了之后,模型选不对工具。这一篇就写这个问题,以及我目前看到的解法:Skill 系统。
先说结论:Skill 这层抽象不是"新技术",它就是软件工程里的模块化思想搬到了 Agent 领域。但什么时候该加这层、什么时候不该,我有自己的判断——写在这篇的最后。
问题是怎么暴露出来的
我的 Agent 最初只有四五个工具,模型选工具基本不出错。后来用 MCP 接入更多 Server,工具列表膨胀到三四十个。问题开始出现:
- 模型偶尔选错工具——明明该用"文件搜索",它调了"数据库查询"
- 工具描述占上下文——每个工具几十个 token 的描述,40 个工具就是好几千 token,还没干活就先吃掉一大块窗口
- 模型不知道工具的组合用法——有些任务需要"先 A 再 B 再 C",模型不知道这个顺序,经常乱序调用
一开始我试图用"优化工具描述"解决——把每个工具的 description 写得更精确。有用,但天花板很低。描述写得再好,40 个工具的列表还是得全塞进上下文,模型还是要在 40 个选项里做选择。
这时候我意识到:问题不在工具本身,在工具的组织方式。
Skill 是什么:把"原子操作"打包成"能力包"
想到一个类比:操作系统的命令。
grep 是一个命令,find 是一个命令,xargs 是一个命令。但你日常工作不是一个个调命令——你用的是 shell 脚本。脚本把"grep 一批文件 → 提取结果 → 排序 → 去重 → 保存"这个组合封装成一个可复用的单元。
Skill 就是 Agent 世界的 shell 脚本。它把"调用哪些工具、按什么顺序、中间怎么处理"打包成一个有名字的能力。
一个 Skill 通常包含三部分:
1. 指令文档(给模型看的说明书)
一个 Markdown 文件,写清楚:这个 Skill 做什么、什么情况下用、接受什么输入、产出什么输出。注意——这个文档的读者是模型,不是人。所以写法上要"机器友好":条件判断清晰、输入输出明确、少用含糊的自然语言。
2. 脚本(可选的执行逻辑)
有些 Skill 需要跑具体代码——调 API、处理文件、执行一段 Python。这些实现在脚本里,被 Skill 系统管理。模型不用自己拼 API 请求,它只需要说"我要用这个 Skill,参数是这些"。
3. 参考资源(按需查阅的资料)
Skill 可以附一份参考文档——API 细节、领域知识、示例数据。关键点是:这些资源不塞进上下文。模型执行 Skill 时如果需要细节,再去查。这就是"按需加载"而不是"一次性全装"。
发现与路由:两级设计
有了 Skill 之后,"模型怎么知道有哪些 Skill"就成了新问题。
笨办法:把所有 Skill 的指令文档全塞进系统 prompt。Skill 少的时候行,多了照样爆窗口——和工具膨胀是同一个问题。
好一点的做法是两级路由:
- 第一级:注册表。常驻上下文的只有一个精简列表——每个 Skill 的名字 + 一句话描述 + 触发条件。很小,几十个 token 一个。
- 第二级:按需加载。模型从注册表里选中一个 Skill 后,系统才把那个 Skill 的完整指令文档和参考资源注入上下文。
这就像你不会把整个图书馆搬进书房——先看目录(注册表),找到要的那本书(选中 Skill),再从书架上取下来翻(加载全文)。上下文窗口就是书房,空间有限,必须精打细算。
Tool 回答"能做什么",Skill 回答"怎么做一件事"。前者是原子的,后者是有流程和上下文的。
Tool / Skill / Plugin 的边界
这三个词经常被混着用,我自己的区分方式:
- Tool:原子操作。一个函数,一个输入输出。最灵活,但模型要自己编排调用顺序。
- Skill:能力包。有指令、有流程、有参考资源。粒度适中,模型按需发现和调用。
- Plugin:完整子系统。有自己的 UI、配置、状态。独立性最强,灵活性最低。
三者是嵌套关系:一个 Plugin 可能包含多个 Skill,一个 Skill 可能调用多个 Tool。选哪个粒度取决于场景——简单操作用 Tool,复合任务用 Skill,完整产品用 Plugin。
设计 Skill 的几条经验(踩过坑之后的)
单一职责。一个 Skill 做一件事。我有个 Skill 的指令文档写到了 600 字,后来发现它在做两件事——拆成两个之后,模型的调用准确率反而上去了。
触发条件必须具体。"处理数据"这种描述必然导致误触发。要写成"当需要对 CSV 文件做统计汇总分析时使用"。模糊的描述就是给模型挖坑。
参考资源要结构化。附带的文档要有清晰的标题和关键词。一篇没有结构的"散文",模型检索效率极低。
给 Skill 加版本号。改了指令文档、加了新工具之后,版本号要变。不然出现"同样的调用昨天对今天错"时,你根本排查不出是哪次改动引入的。
留降级路径。Skill 内部失败时,要有明确的错误返回,让模型知道"这条路走不通,换一条",而不是拿一个空结果硬着头皮继续。
什么时候不该加 Skill 层
这是我最想说的部分。Skill 系统不是越早加越好。
如果你的 Agent 只有五六个工具、任务流程简单,加 Skill 层是过度设计——你引入了一个新的抽象层、一套新的管理机制,解决的问题却不存在。工具少的时候,直接精心写工具描述就够了。
我自己的触发标准是:当工具超过 15-20 个、模型开始频繁选错、上下文被工具描述吃掉超过 20% 时,才考虑引入 Skill 封装。
这个判断和软件工程里的经验完全一致——模块化是为了应对复杂度,但模块化本身也引入复杂度(接口设计、版本管理、路由逻辑)。只有当原始复杂度超过了模块化引入的复杂度,这层抽象才划算。
架构决策应该被问题驱动,不是被概念驱动。先痛了再重构,比预防性设计更靠谱。
还没想明白的问题
Skill 的粒度怎么拿捏?拆太细,Skill 数量膨胀,注册表本身变大;拆太粗,一个 Skill 干太多事,内部流程复杂、容易出错。"一个 Skill 做一件事"说起来容易,但"一件事"的边界在哪,我觉得还是要靠实践摸索。
Skill 之间能组合吗?现在我的 Skill 是独立调用的。但理论上一个 Skill 可以触发另一个 Skill(比如"分析报告"Skill 内部调用"数据查询"Skill)。这种嵌套会不会失控?目前没敢试,怕调试噩梦。
小结
Skill 系统的本质:给 Agent 加一层能力管理架构,解决"工具多了之后怎么组织、怎么发现、怎么复用"的问题。
它不是技术突破,是工程演进的必然——函数解决"能不能做",模块解决"怎么组织",Agent 领域走到了需要模块化的阶段。
如果你在搭自己的 Agent,我的建议是:先把 Tool 跑通,别急着建 Skill 系统。等"工具太多、模型乱选、上下文爆了"的痛真实出现了,再动手。这时候你知道要封装什么、怎么封装——因为痛过的地方就是边界该画的地方。