这两年做大模型应用,我听到最多的一个比喻就是:大模型是大脑,Skills是手足。这个比喻很形象,但它不止是画面感强——它准确点出了很多项目从 Demo 到落地之间总有一段翻不过去的坎:模型确实能说,但不会做。
我们项目组前阵子给某内部业务系统做对话助手,模型本身表现不错,常识问答、文档总结都过得去。但一遇到“帮我查一下最近三个月的订单量,跟去年同期比一比”这种需求,它就卡壳了:既不知道订单数据存在哪个库,也不知道怎么拉出来算同比,最后只好一脸诚恳地编一个数字给我。那一刻我就意识到,纯靠参数里压缩的知识,大模型永远只是一个擅长表达的大脑;要让它在真实业务里干活,必须给它配上能伸出去的手。这套“手”就是 Skills。
这篇文章我想把这套东西彻底讲透:大模型为什么需要 Skills,Skills 到底是个什么结构,以及落地一个 Skill 体系时你会踩到哪些坑。不聊虚的,全是项目里摸出来的经验。
1. 纯推理的边界:为什么大模型不能只靠参数过日子
1.1 模型不是数据库:知识是压缩存储而非精确检索
很多人对模型的第一个误解,是把它当成一个超大的数据库,以为问什么都能从中“查”到准确答案。实际上,大模型在训练时做的是一件非常“暴力”的事情——把海量文本压缩成几十亿、几百亿个浮点参数,存进神经网络里。这个过程的本质是记忆规律,而不是保存原文。
所以你会发现一个有意思的现象:问模型“三角形的面积公式是什么”,它能答得又快又准。这是因为它从训练语料里学到了这个高频知识。但如果你问“我们公司上个月销售额是多少”,它就彻底没辙了——这条信息从来没出现在任何公开语料里,也不可能靠“思考”猜出来。更麻烦的是,它不会老老实实说“我不知道”,而是会根据上下文平滑地把一个看起来合理的数字“编”出来。这不是模型笨,而是生成模型的天性:它始终在预测最可能的下一个词,而不是查证事实。
这让我想起一个很实在的类比:一个人哪怕智商再高、知识再渊博,他也不能凭空知道你家门口今天停了几辆车。想要这个数字,要么他自己探头去看,要么你给我提供。大模型也一样,凡是训练数据之外的信息,它唯一能做的就是推测和编造。这不是缺陷修复问题,而是能力边界问题。
1.2 四种“手不够长”的典型场景
如果只把模型当聊天机器人用,它的边界还不那么明显。可一旦进入真实业务流程,下面这四类场景几乎是必现的绊脚石。
| 场景类型 | 纯模型的失败方式 | 加上 Skill 后的解法 |
|---|---|---|
| 实时数据查询 | 编造不存在的数字或“看起来合理”的结论 | 通过 Skill 调用真实数据源,返回实际数值 |
| 精确计算 | 步骤对但结果不对,多位数乘法尤其不靠谱 | 把计算逻辑放进 Skill,用代码完成确定性运算 |
| 业务操作 | 只能描述“怎么做”,无法真的去执行 | Skill 直接触发下单、审批、发送等动作 |
| 多系统联动 | 不知道数据在哪、接口怎么调,只能瞎猜 | Skill 封装接口细节,模型只负责语义理解与编排 |
第一类最典型。聊天助手问“今天北京天气怎么样”,模型如果不知道当前时间,也没有实时接口,它会怎么回答?它只能结合训练数据中“北京五月的平均气温”来猜,再配上“可能”“大概”这种模糊表达。这在闲聊层面还能接受,但在订单查询、库存盘点、财务对账这种业务场景里,一次编造就足以毁掉用户对系统的全部信任。
第二类精确计算也很有意思。模型在数学题上表现得像一个“读过很多例题但没真正动过笔”的学生——它知道要用哪个公式,但最后一步算出 3947 还是 3497,完全看概率。因为它的内部没有执行逻辑运算的机制,只有词元级别的概率预测。所以凡是对精确性有要求的计算,都不该交给模型直接出结果,而是应该让它解析出参数,把计算扔给一个确定性系统。
第三类和第四类则更根本:模型本身不具备任何副作用。它不能改数据库、不能发 HTTP 请求、不能给你同事发消息。大模型只能输出文本,而真实世界的“操作”恰恰需要执行实体。你当然可以通过提示词让模型“认为自己”做了某件事,但那只是在模拟,不是在做。
2. Skills的本质:把“会做事”变成可调用、可复用的能力单元
2.1 Skill与提示词、插件到底有什么区别
很多人第一次接触 Skills 时,会把它当成“功能更强大的提示词”,或者“某种插件机制”。这个理解不能说错,但没有抓住要害。
我倾向于这样定义:一个 Skill 是对“某项做事能力”的完整封装,它最重要的特征是可调用、可复用、接口化。所谓“可调用”,是指模型在推理过程中,根据用户意图主动决定是否调用它,而不是每次都由用户侧写死;所谓“可复用”,是指这个能力定义一次、多处使用,不存在换一个场景就重写一遍提示词的问题;所谓“接口化”,是指它对外暴露的是明确参数和返回结构,内部实现可以随时替换。
普通提示词是什么?它是给模型的行为指令。你可以在系统提示词里写“当你需要天气信息时,请调用某接口”,这在简单场景下确实能跑通。但问题在于,真实项目的需求是几十个甚至几百个的,如果把所有指令都堆在提示词里,很快就会出现两个后果:上下文长度被工具说明占满,以及模型在意图识别上出现混乱——它不知道该在什么时候用哪个工具,甚至把工具描述里的内容当成知识来回答用户。
插件机制则通常指一个更重的集成框架,更多是外层调配模型。而 Skill 更轻、更聚焦,它描述的是“一件独立可执行的事”。
2.2 一个Skill的五个组成部分
一个设计良好的 Skill,在我看来必须包含五个部分,缺一个都会在后续迭代里补课。
第一是触发场景。它的本质是给模型一个判断依据——在什么情况下应该调用这个能力。写得好的触发描述,是一个带关键特征的意图判断器。比如“当用户询问某个具体日期或时间段的销售数据时”,这比“用于查询销售”要清晰得多。模型是靠它来决定“我现在该不该伸手”的。
第二是输入契约。这是最容易被轻视、却最要命的部分。Skill 的参数用什么名字、什么类型、哪些必填、哪些可选,必须用结构化方式定义(通常是 JSON Schema)。你如果不把“城市”和“日期”的格式约定清楚,模型传参的时候就可能出现“北京”“北京市”“beijing”三种完全不同的值,接口根本没法处理。
第三是执行逻辑。这指的是 Skill 内部真正做事的部分——调用哪个下游接口、做什么计算、走什么顺序。这块对模型是黑盒,但对外部系统是实打实的业务逻辑。我们在实践中喜欢把执行逻辑拆成“拉数据”和“算结果”两层,方便单独测试和缓存。
第四是输出格式。模型调用完 Skill 后,需要拿到一个结构化的返回结果,才能把它整合成最终回答。这个返回不建议是自然语言长段落,而应该是结构化数据,比如 JSON 字段列表、聚合结果、错误码。否则模型在转述结果时很容易丢信息,甚至偷偷“润色”一下。
第五是回退路径。这是很多人忽略的:如果接口超时、权限不足、参数映射失败,Skill 应该返回什么?我给出一条很笨但很有效的约定——所有失败都必须返回明确的错误码和可读的原因,同时模型在收到错误结果后,应该向用户如实说明“查询失败,原因是什么”,而不是硬着头皮把错误信息包装成正常结果。
这里我可以给一个非常简单的例子。假设我们做一个“查询志愿者活动报名人数”的 Skill,接口定义大概长这样:
{ "skill_name": "query_event_signups", "description": "查询某场活动的当前报名人数与报名名单摘要,当用户询问参与人数、报名情况时使用", "parameters": { "type": "object", "properties": { "event_id": { "type": "string", "description": "活动ID,格式为EV-开头加数字" }, "date_range": { "type": "string", "description": "日期范围,如2025-04-01至2025-04-30" } }, "required": ["event_id"] } }模型看到这个定义,就会在用户说出“三号那场运动会报名了多少人”时,尝试把“三号那场运动会”解析成对应的 event_id,然后发起调用。整个过程,模型就像一个接到任务的调度员,它不需要知道报名数据存在哪张表里,只需要知道把哪个参数交给哪个能力。
3. 认知外包与接口化:Skills为什么会成为应用落地的关键一跳
3.1 认知外包:让确定性的工作回到确定性的系统
大模型擅长的是模糊、开放、需要理解的任务;不擅长的是精确、闭合、需要执行的任务。Skills 的核心逻辑,就是把后者从模型的“大脑”里卸载出去,外包给更可靠的外部系统。这就是我理解的“认知外包”。
你可以这样想:人的大脑记忆能力远不如笔记本,但人并没有因此升级大脑去背下所有电话号码,而是学会了用通讯录。模型也一样,它不需要把业务数据、计算逻辑、接口细节全部记忆在参数里,它只需要掌握一个更高层的元能力——识别什么时候该查通讯录、怎么查、查到之后怎么复述。
这种分工是效率最优解。模型保留语义理解、意图识别、多轮对话、内容组织这些真正的“智能”部分;而查询、计算、写库、调用外部服务这些确定性的部分,交给确定的系统。两者通过接口衔接,既保证了模型的泛化能力,又获得了系统的精确性。
3.2 工具调用训练:模型如何学会请求外部能力
这里有个关键的底层问题:模型凭什么知道“此时该调 Skill,而不是直接回答”?这要追溯到模型的工具调用训练机制。
在训练阶段,模型会见到大量这样的数据:用户提问,模型在应当调用工具时输出一个特殊的调用标记,并携带参数;随后系统返回工具结果,模型基于结果组织最终回答。反复训练后,模型就内化了一个行为模式——有些问题不能靠自己的知识直接回答,必须触发工具。
这也是为什么你在实际使用时会发现一个现象:模型对“该不该调工具”的判断,绝大部分时候是准的,但偶尔会误判。这其实是统计模型的固有属性,它本质上还是在做概率预测,只是这次预测的对象变成了“该调用哪个 Skill”。所以我们在做 Skill 体系时,从来不说“模型 100% 会正确判断”,而是通过设计良好的触发描述和参数约束,把判断准确率拉高到可用的水平。这部分不是玄学,靠的是提示词工程、上下文裁剪和反复的验证集迭代。
3.3 从知识库到调度器:模型角色的根本转变
Skills 的出现,实际上改变了模型的角色定位。传统用法里,模型是一个“知识输出者”,你问它答;引入 Skills 之后,模型变成了“资源调度者”,它理解你的目标、拆解任务、决定调用哪些能力,再把多个能力的结果组织成一份完整的应答。
这个转变怎么夸大都不过分。比如用户说“我想给上周报名未满的活动补发一条通知,顺便统计一下这周还有哪几个活动的报名人数不足十人”。如果没有 Skills,这是一个纯语言任务,模型只能描述方案,告诉你“你可以先查 A 再查 B 再发通知”;有了 Skills,模型可以真的去查报名数据、比对人数、触发通知接口,一气呵成地完成业务闭环。
我自己做项目时有个很深的体会:大模型能不能从“演示玩具”变成“生产工具”,分水岭就在这里。没有行动能力的大模型,始终是一个成本不低的问答百科;有了行动能力,它才真正进入了业务流程,成为能交付结果的一环。
4. 从零搭一套Skill体系:需求拆解、接口设计与模型接入
4.1 第一步:能力盘点与需求分级
我们接手某跨平台系统时,最初是接到一堆碎片化需求。“做个助手,能查订单,能算毛利润,最好还能自动回邮件。”听起来很多,但别急着开写,先做能力盘点。
我习惯把所有需求按“模型能不能直接做到”分个级。第一类是纯语言任务,比如“帮我把这段产品说明改写得更简洁”,模型本身就能做,不需要 Skill。第二类是需要外部数据的“读”型任务,比如查订单、查库存、查成员列表,需要做一个查询类 Skill。第三类是带着副作用的“写”型任务,比如发通知、改配置、提交审批,这类 Skill 要做更严格的权限控制和确认机制。
这个分级特别关键。因为 Skill 不是越多越好,而是越精越好。一个团队最常见的问题是一上来就定义了三四十个 Skill,结果模型在工具选择时晕头转向,用户随便问一句,它可能一连触发五六个无关的调用,又慢又费钱。我们第一期的原则是:只做高频的、刚需的、纯模型确实做不到的那批能力,宁可少而精。
4.2 第二步:Skill接口设计(命名、Schema、错误约定)
Skill 接口设计是整个体系里最考验基本功的地方。先说命名。Skill 名称要用动词+对象的结构,比如 query_event_signups、calculate_gross_margin、send_notification。这样模型在看到名称时就能快速理解它的功能范围。别用那种模糊的缩写,模型猜起来费劲,错了还得背锅。
参数 Schema 要遵循几条原则。一是参数名直接用项目里大家通用的说法,避免模型在“city/city_name/cityCode”之间反复试错;二是必须在 description 里写清楚格式和示例,比如日期一律用 ISO 格式,ID 前缀是什么,这样模型填参时才有据可依;三是能合并的参数就合并,比如查订单需要“开始时间”和“结束时间”,可以合成一个 date_range,减少模型出错的机会。
错误约定同样不能省。我见过太多团队,Skill 内部报错了,返回给模型的是一大段堆栈信息。模型根本看不懂,于是就开始瞎编:“看起来是一个连接问题,请稍后重试”。多试几次用户就疯了。正确做法是给每个可能出现的错误类型定义一个错误码,并附一句用户能直接看懂的话。比如“ERR_DATA_UNAVAILABLE:所选时间范围内没有数据”,模型拿到这句话,就能诚实地告诉用户原因,而不是猜测。
4.3 第三步:接入模型与调用策略
接口定义好了,接下来就是让模型学会用。不同模型平台的接入方式略有不同,但核心思路一致:在系统提示词或函数调用定义里把 Skill 列表描述清楚,让模型在推理时自行决定是否调用。
我个人的做法是三步走。第一步,只接入两三个 Skill,跑通端到端链路,确认模型能在正确时机触发正确工具。第二步,把 Skill 描述的措辞优化,比如触发场景写得更具体,参数示例补得更完整,这一步主要解决“该调不调”和“不该调乱调”的问题。第三步,才逐渐把其余 Skill 加入清单,并且每组新增都做回归测试。
在调用策略上,我强烈建议默认采用“计划先行”的方案:模型先输出一个调用计划,系统校验计划的合法性和参数完整性,再执行调用,最后把结果交给模型组织回答。这个方案看着多了一步,却能把错误率压下一个量级。因为模型先写计划时,犯错是可控的,计划不对可以拦下来;真要直接调接口,错了就是实实在在的副作用。
下面这段伪代码描述了我们的调用流程:
1. 用户输入 -> 模型生成意图与计划 2. 系统校验计划: - Skill 名称是否存在于白名单? - 参数是否符合 Schema? - 该用户是否有此 Skill 的调用权限? 3. 校验通过 -> 执行外部调用(带超时与重试) 4. 校验失败 -> 返回错误码,要求模型修正计划 5. 执行成功 -> 结构化结果回传模型 6. 模型基于结果生成最终回复这套流程跑下来,我们的工具调用错误率从最初的百分之二十几降到了百分之三四,靠的就是这个“先计划再执行”的缓冲层。
4.4 离线集与线上指标的双轨评估
Skill 体系上线前,一定要准备一套评估集。它的形式不是“一组问答测试题”,而是一组“问题—期望调用序列”的配对。比如输入“帮我看看上周报名人数少于十人的活动有哪些”,期望结果是先调用 query_event_signups 拉报名数据,再做过滤统计。我们把这类用例积累到一两百条,每次改动 Skill 定义或提示词,就跑一遍,看有多少用例的调用序列与期望吻合。
线上指标同样重要。我重点盯三个号:工具调用成功率(成功拿到合法结果的比例)、工具幻觉率(模型编造出不存在的结果或断言“已执行”但实际未执行的比例)、以及用户追问澄清率(用户因为答案不对而再次提问的比例)。这三个指标单独看都有噪音,合在一起就能反映 Skill 体系健康度。
5. 踩坑实录:调用失控、幻觉放大和上下文污染的排查链路
5.1 权限失控:模型不会自动分辨“能做什么”
第一个坑,也是最危险的坑:模型在调用 Skill 时,不会自己判断这个操作在当前语境下是否合理。它只会根据描述决定“该不该调”,至于“这个用户”有没有权限、“这个操作”是否被允许,它一概不管。
我们当时做了一个“批量发送通知”的 Skill,测试时一切都好,直到某天 A 同学在测试环境里让助手给所有活动成员群发一条测试消息。助手老老实实执行了——但它没有意识到,测试环境的数据里面有真实用户的手机号。那一刻我们才明白一件事:权限判断绝对不能依赖模型,必须由 Skill 内部强制校验。
后来的规矩很简单。每次调用执行前,系统层先校验用户身份、调用权限和操作范围;Skill 内部再做一次兜底拦截,比如“发送数量超过 50 条需要二次确认”。模型的角色只是提出请求,批准权永远在系统手里。
5.2 幻觉放大:编造的Skill名、编造的参数
第二种坑更隐蔽——模型会“一本正经”地调用一个根本不存在的 Skill。有一次用户问“帮我查一下某同学上次的培训记录”,助手回了一句“好的,正在调用 query_training_history 查询”,但我们的 Skill 列表里压根没有这个名字。它自己造了一个。
这不是它坏,而是它把训练时见过的某种模式套用在了新场景上:既然用户要查东西,我就编一个听起来合理的工具名。副作用是,用户以为自己被服务了,实际上什么都没有发生。
解决办法有三层。第一层是给所有 Skill 名加统一前缀,比如全部用 “sys_” 开头,然后在系统层做硬校验,遇到不在白名单里的调用直接拒绝。第二层是禁止模型在没有拿到真实返回结果之前,用“已完成”“查询成功”这类话术向用户承诺。第三层是加格式约束,要求模型调用时输出结构化计划,系统解析失败就让它重写。这三层叠起来,幻觉率的下降是肉眼可见的。
5.3 上下文污染:工具清单塞满了上下文
再后来我们往系统里塞了二十几个 Skill,新问题又来了。模型开始频繁误触发——用户明明只是闲聊一句“今天挺热的”,它都恨不得去调用天气查询接口。我查了日志,发现根因是每个 Skill 的描述加起来占了大量上下文,把系统提示词里其他重要指令都给稀释了。
这里有个反直觉的点:Skill 描述写得越详细,模型越容易误判。因为那些前沿描述里包含了太多可能触发匹配的词,比如天气 Skill 里写了“温度”“气候”“热”,闲聊里出现“热”字,它就被带跑了。
优化方向有两个。一是精简触发描述,只保留最典型的触发特征词,避免过度泛化。二是引入相关性预筛层,在模型推理前先做一个轻量匹配,只把可能与当前问题相关的五六个 Skill 的定义注入上下文,其余暂时隐藏。这两招同时用,误触发率下降得非常明显,模型的回复质量也回升了。
5.4 一次订单查询故障的全链路排查
最后分享一次我们实际遇到的故障排查过程,希望能帮你省点时间。某天线上反馈:助手查订单时报错,用户追问“是不是系统坏了”。
排查第一步,先分锅。打开调用日志,看用户输入之后模型生成了什么计划。结果发现计划里调用的是 query_order_summary,参数 date_range 传的是“2025年4月”。看起来没问题,那第二步就是验证参数解析。再看模型与 Skill 之间的中间层日志,发现这一步把“2025年4月”成功转换成了标准的起止日期,也没问题。
第三步就只能查 Skill 内部了。跑了一遍指令,发现下游订单库里四月份的订单量很大,查询接口超时了。于是问题不在模型,而在外部接口的稳定性。我们给查询加了一层缓存,把高频日期范围的结果缓存二十分钟,同时给接口增加超时重试。最后再看线上的错误率,这个问题就消失了。
这个案例想说明的是:Skill 体系一旦出问题,根因可能是模型、参数、中间层、下游接口任意一环。排查时一定要先看日志,把链路每一层的“输入输出”都打开,一层层往下定位。不要一上来就怀疑模型,很多项目事故的真相都在外部依赖上。
6. 把Skills当产品养:迭代节奏、灰度发布与团队分工
6.1 Skill也需要版本管理与回滚
很多人喜欢把 Skill 当成一次性代码写,写完就跑、跑完就忘。这在初期可以,等体系一复杂,你就会发现每个 Skill 都是活物:参数改了、下游接口换了、触发描述调了,任何一个改动都可能影响线上表现。
我们的做法是给每个 Skill 单独做版本管理,每次修改都记录一个变更说明。这样一旦线上行为变差,可以单点回滚到上一版,不用整个系统动手术。另外,Skill 之间不允许悄悄互相依赖——A 技能不能直接绕过接口去调用 B 技能内部函数。所有协作都必须通过公开接口,否则排查问题时你会疯掉的。
6.2 灰度发布与调用观测
Skill 上线最好走灰度。我们通常先让新 Skill 在内部测试群跑几天,观察两个东西:触发率是否合理,返回结果被采纳率怎么样。如果触发率极低,大概率是描述对触发场景覆盖不足;如果触发率很高但结果经常被用户追问,那可能是参数解析不准或返回结果形式不对。
观测侧,我强烈建议每个 Skill 单独记日志:调用时间、模型输入、传入参数、返回结果、耗时、错误码。这笔投入非常值得。没有观测,你面对的是一个黑盒,出了问题只能靠猜;有观测,你才能像外科医生一样精准切到病灶。
6.3 给刚开始搭Skill体系的人一个建议
如果你从零开始搭第一个 Skill 体系,我的建议是:不要追求大而全,先做一条最小的业务闭环。挑一个用户高频遇到的、纯模型确实做不到的需求,做出一个 Skill,把从意图识别、参数解析、调用执行到结果回传的整条链路跑通。然后在这条链路上把上面提到的拦截、日志、评估都补齐,再往下一个 Skill 扩展。
这样做的原因是,Skill 体系的复杂度是乘法级的。一个 Skill 跑通,你学到的是通用的方法论;十个 Skill 同时上,你面对的则是成倍的相互干扰和排查压力。先把一个做到位,比摊大饼重要得多。
大模型确实像大脑,但大脑的价值从来不体现在孤零零地思考,而体现在它能指挥手去执行、能通过眼睛获取信息、能借助工具完成单靠自身无法完成的工作。Skills 就是大模型的眼和手。把这个体系搭好,你手里的模型才算真正从“能聊天的知识库”变成了“能干活的项目成员”。