最近很多人在搜“扣子创建skill”,也有不少朋友在社群里问我,扣子里的技能(Skill)和插件、工作流到底是不是一回事?为什么我照着教程建了技能,智能体却怎么都不调用?这些问题其实都指向同一个核心:你对Skill的理解还停留在“填个表单”的层面。
我在扣子上试了很多次,踩了不少坑之后,逐渐摸清了Skill的定位、边界和正确的打开方式。这篇就结合我自己的实际操作,把创建Skill的完整流程、脚本写法和那些文档里不会明说的经验一次讲清楚。不管你是刚接触扣子的小白,还是已经建过几个工作流的老玩家,这篇都能帮你少走弯路。
1. 先分清一个事:扣子里的Skill到底是什么
先说结论:Skill在扣子里是一个“可以被智能体按需调用的能力单元”,它本质上就是一套“触发条件 + 调用协议 + 返回结果”的组合。很多人在这一步就绕晕了,因为扣子同时还有“插件”和“工作流”两个相似概念,三者的边界一旦模糊,后面全乱套。
1.1 从“技能和插件到底啥区别”说起
我在各个技术群里看到最多的提问就是“Skill和Plugin有什么区别”。这俩在早期确实很像——都是给智能体加工具。但实际用下来,你会发现它们侧重点完全不同。
插件更像一个“封装好的现成工具箱”,比如扣子插件商店里的搜索插件、图片识别插件,你装上去就能用,内部逻辑是黑盒,你不太需要关心它怎么实现。而Skill更像是“你自己定义的一套API契约”,你告诉智能体:当用户想查快递时,去请求这个快递查询接口,把返回的数据解析成用户能看懂的话。你可以完全控制接口地址、参数格式、返回结构,甚至可以在中间加一段脚本做数据清洗。
所以我的理解是:插件是别人做好给你用的,Skill是你自己定义给智能体用的。技能可以引用插件,但插件的核心能力不是让你自定义协议。
1.2 Skill和Workflow:一个偏单点动作,一个偏流程编排
另一个高频误区是拿Skill和工作流对比。经常有人说“我直接用工作流不就行了,创建的技能好像也就是个工作流”。
这句话对了一半。工作流的核心是“多步骤串联”,比如“先获取用户输入的网址 -> 抓取正文 -> 用大模型总结 -> 输出摘要”,这是流程编排。而Skill的核心是“一个可以被自然语言触发的外部能力”,它通常对应一个相对独立的动作,比如“查天气”“算税费”“发邮件”。
但两者确实有交集:你可以把工作流整个打包成一个技能暴露给智能体。这也是扣子平台非常实用的一个玩法——先在工作流里把所有逻辑编排好,再把工作流“技能化”,让智能体在对话中自动调用。反过来,Skill内部也可以不直接写API,而是指向一个工作流。所以你可以理解为:Skill是“接口”,工作流是“实现”,两者不是对立关系,而是层次关系。
1.3 Agent Skill和MCP的区别,一句话说清
热搜里还有一条特别扎眼:“agent skill和mcp有什么区别”。这个也确实值得单独讲讲。
MCP(Model Context Protocol)是一个标准化的“模型-工具通信协议”,它解决的是“大模型如何统一地发现和调用各种工具”的问题。而Agent Skill更偏“一种可复用的能力描述单元”,它包含了工具的调用方式、参数说明、使用场景描述等。
放到扣子场景里,你可以这么理解:扣子原生的Skill创建方式,本质上是让智能体通过一套“OpenAPI风格”的协议定义来理解你的工具,这很像MCP的思路;但Agent Skill往往还包含“在什么场景下用、怎么用更好”的语义信息,而不仅仅是接口定义。实际开发中,两者不是非此即彼,很多平台正在互相借鉴融合。你在扣子创建技能时,只需要记住一条:你写的描述越清楚,智能体才越知道什么时候调用它。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 创建一个真实Skill的全流程:以“查快递”为例
说再多概念都不如动手建一个。我这边用一个最典型的例子来走一遍全流程——创建一个“查快递”技能。选这个例子是因为它的逻辑足够清晰:用户提供快递单号,智能体调快递查询API,返回物流轨迹。
2.1 先想清楚这个Skill要解决什么
创建任何技能之前,第一件事不是打开控制台,而是先在纸上写清楚两件事:输入是什么,输出是什么。
我的输入是“快递单号”,输出是“快递公司 + 物流轨迹列表”。如果你使用的快递查询API需要“快递公司编码 + 快递单号”才能查,那技能在设计时还要考虑:用户可能不知道快递公司编码,这时候智能体需要先猜测或询问。这些边界如果你不提前想清楚,后面配置API参数时会非常痛苦。
我后来总结出一个口诀:一个Skill只做一件事,把输入边界划得越小越好。
2.2 创建技能的基本配置:名称、描述、指令
在扣子的控制台左侧找到“技能”入口,或者通过“创建技能”按钮进入。第一步填名称和描述。
名称好说,叫“快递查询”。描述是重中之重,因为智能体是根据描述来判断什么情况下调用这个技能的。我最初的描述写的是“查询快递物流信息”,结果智能体在用户问“我买的手机到哪了”时完全没反应。后来改成:
当用户想要查询快递/包裹/物流的当前位置、物流轨迹、签收状态时,可使用此技能。用户需提供快递单号。调用前请先向用户确认快递单号是否为10-15位数字。
这段描述里包含了“触发条件(物流相关查询)”、“前置要求(需要单号)”、“参数校验(单号格式)”。智能体看到后,就知道什么时候该出手,什么时候该追问。
如果你创建的Skill对标的是Agent Skill那套玩法,还可以在“指令/提示词”里补充调用步骤,比如“第一步解析单号,第二步调用API,第三步将返回的轨迹按时间倒序整理成中文列表”。这个字段对智能体的行为约束特别有用。
2.3 配置API:选择合适的API服务商并编写协议定义
这是最核心的一步。在扣子创建技能时,通常会让你选择“API服务”方式,你需要填写完整的接口定义,一般是OpenAPI格式(YAML或JSON)。
我用的快递查询API通常是这样的接口:
yaml复制openapi: 3.0.0
info:
title: 快递查询接口
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/express/query:
get:
summary: 查询快递物流信息
parameters:
- name: expressNo
in: query
required: true
schema:
type: string
description: 快递单号
- name: companyCode
in: query
required: false
schema:
type: string
description: 快递公司编码,如SF、YTO
responses:
'200':
description: 查询成功
content:
application/json:
schema:
type: object
properties:
code:
type: integer
data:
type: array
items:
type: object
properties:
time:
type: string
context:
type: string
这里有几个细节特别容易踩坑:
第一,required字段一定要想清楚。如果你把companyCode设为required,但用户不知道快递公司,智能体就会反复追问,体验很差。我后面专门做了一版“自动识别快递公司”——把单号交给一个内部工作流先用大模型判断公司编码,再传给快递API,这样用户只需要给一个单号就够了。
第二,每个参数的description要写清楚。这个描述不是给开发者看的,是给智能体看的。它会读这些描述来生成URL和参数值。比如“expressNo”的description写得模糊,智能体可能把用户的订单号当成快递单号传进去。
第三,响应的schema也要写清楚。如果接口返回的字段是result.traces,你不在响应定义里写明白,智能体可能再编造一个字段名出来。给模型越少自由发挥的空间,结果越可控。
2.4 在智能体里挂载并测试
技能创建好之后,回到智能体的编排页面,在“技能”一栏里点添加,把刚建好的“快递查询”技能挂载上。然后在调试面板里发一句“帮我查一下这个快递单号SF1234567890”。
这时候你观察智能体的行为:
- 它是否识别出你要查快递
- 它是否正确调用了技能
- 它是否把API返回的一串JSON翻译成了人话
我第一次测试时,智能体确实调用了技能,但它把返回的JSON原样丢给了我,里面还有一堆status_code。后来我在技能指令里加了一句“请将返回结果中的物流轨迹按照时间从最新到最早排列,并翻译成简洁的中文,不要展示原始JSON”,情况立刻好转。
3. Skill脚本怎么写:从“skill脚本”热搜谈起
热搜里有个词很显眼:“skill脚本”。很多人以为Skill一定要写脚本,或者说Skill就是一个脚本。要我说,脚本只是Skill的一种实现方式,不是必需项。但在某些场景下,光靠API定义确实不够,必须加脚本才能把数据处理好。
3.1 纯API型Skill和带脚本型Skill
纯API型Skill适合那种“接口返回什么,用户就要什么”的场景。比如你调用一个天气接口,返回的温度、风力、湿度直接展示就可以。
但更多时候,API返回的数据格式和用户想要的表达方式差距很大。比如快递查询API返回的是:
json复制{
"code": 0,
"data": {
"status": "在途",
"traces": [
{"time": "2025-06-01 10:00:00", "desc": "【北京】快件已到达北京转运中心"},
{"time": "2025-06-01 08:30:00", "desc": "【广州】快件已从广州发出"}
]
}
}
如果不做任何处理,智能体虽然能读懂JSON,但它可能把status和traces之间的关系讲得很绕。这时候你可以用一个脚本把数据“前处理”成更友好的结构,甚至直接生成一段摘要文字。
3.2 用云函数/代码节点处理返回数据
在扣子里处理这种逻辑,最常见的做法有两个:一是在工作流里加“代码节点”,二是直接在技能配置中调用一个带脚本的服务。
我试过在扣子工作流中用代码节点来承担“数据清洗”的职责。代码节点支持Python或JavaScript,你可以把API返回的data传进去,做字段重命名、时间格式化、排序、过滤,然后输出一个干净的结果给大模型。
举个例子,我写过一个Python代码片段:
python复制def main(traces: list):
# 按时间倒序排列
sorted_traces = sorted(traces, key=lambda x: x['time'], reverse=True)
# 只保留时间和描述字段,并重命名
result = []
for item in sorted_traces:
result.append({
"时间": item["time"],
"物流动态": item["desc"]
})
return {
"轨迹": result,
"最新状态": result[0]["物流动态"] if result else "暂无轨迹"
}
这个函数在代码节点里定义好之后,智能体调用的就是处理后的结果。它给用户回复时,信息密度和质量都会高很多,而不是把所有原始字段一股脑甩出去。
我一直觉得一个很重要的原则是:能在代码里做的,就不要指望大模型“理解后再处理”。 大模型擅长的是表达和推理,而不是精确的格式转换。数据清洗、字段过滤、状态映射这些脏活累活,交给脚本最稳。
3.3 把脚本挂在工作流里再暴露成Skill
还有一种更高级的玩法:不直接创建“API型技能”,而是先搭一个工作流,在工作流里用代码节点、条件节点、甚至是另一个大模型节点来处理数据,然后把整个工作流“发布为技能”。
具体操作是:先在扣子的工作流编辑器中把“接收快递单号 -> 识别快递公司 -> 调用API -> 清洗数据 -> 输出结构化结果”一步步搭好,最后在右上角点击“发布为技能/插件”。这样智能体调用的就是这个工作流,而不是某个裸API。
这种做法的好处是:你可以在工作流里加“判断分支”,比如当API返回code=500时,走一个兜底提示;当识别到快递公司不在支持列表时,直接告诉用户“暂不支持该快递公司”。这些都是纯API型技能很难优雅实现的。
而且在扣子中,一个工作流还可以被多个技能复用。我搭过一个“通用HTTP请求 + 响应解析”的工作流,后面建其他Skill时直接引用它,省了很多事。
4. 调试和发布:真正上线前必须走完的几步
创建完Skill只是开始,真正决定它能不能用、好不好用的,是调试和发布这两个环节。我在这个阶段栽过最多的跟头。
4.1 在调试台里用真实对话验证触发
创建好技能并挂载到智能体后,一定要在调试台里多轮对话测试。注意“多轮”这个词,很多人只测一次“帮我查快递”,发现能用就以为完事了。但用户实际上会这样问:
- “我的快递到哪了?”(没有单号)
- “帮我看看这三个单号分别到哪了”(批量查询)
- “昨天到的那个包裹是圆通还是中通?”(隐含语义)
第二种和第三种情况需要智能体先判断是否需要调用技能、调用几次。我亲眼见过智能体在一个对话里只调用了第一个单号,却把另外两个单号的问题用一个编造的结果敷衍过去。这种问题在调试阶段很容易暴露。
我在测试时习惯准备一组“刁钻问题集”,覆盖:
- 信息缺失(没有单号)
- 信息模糊(“快递”不是“单号”)
- 批量需求(多个对象)
- 组合需求(查询+翻译成英文)
4.2 检查参数映射和异常返回
第二个重点就是拉日志看每次调用的参数。扣子的调试台会显示每一次技能调用的请求参数、响应结果。你要确认:智能体传给API的expressNo到底是什么?
我遇到过一种情况:用户提供了快递单号,但智能体在传参时把单号前后的空格也传进去了,导致接口报“单号格式错误”。这个在日志里看起来特别隐蔽,因为单号本身没错,就是多了个空格。
解决方式有两种:一种是在API服务端做参数trim,另一种是在技能的“指令”里明确写“传参前请去除两端空格”。我个人更推荐前者,因为在扣子这边写指令属于“期望模型遵守”,而API端trim是强制性的。能强制的就不要靠期待。
4.3 发布到团队空间或商店
调试通过之后,发布就简单了。你可以把技能发布到自己的团队空间,供其他智能体使用;也可以按平台要求提交到技能商店。发布前我一般会再做一遍“从零开始验证”——就是用一个新的测试智能体,不写任何额外系统提示词,只挂这个技能,重新测试一遍完整链路。这样做是为了排除“我的主智能体提示词掩盖了技能描述缺陷”的可能性。
5. 我在实操中踩过的坑
这部分我特意单独列出来,因为有些坑不亲自踩一遍,光看文档完全发现不了。
5.1 技能描述写得太抽象,智能体根本不调用
最早我做了一个“文本润色”技能,描述写的是“这是一个文本处理能力,可以帮助用户优化文字”。结果呢?智能体从头到尾没调用过它——因为用户在对话里说“帮我改下这段话”时,智能体觉得自己直接改就行了,没必要调一个工具。
后来我把描述改成:“当用户明确要求对一段文字进行专业性润色、改写、修正语法错误时,请使用此技能。注意:日常对话中的文本不做处理。”加了这个边界后,调用才变得可控。
这个道理和其他Agent开发是一致的:工具描述不只是“功能说明”,更是“调用决策条件”。你写“可以帮忙”,模型就觉得“我也能,不用它”;你写“必须当用户出现XYZ明确意图时才调用”,模型的判断才清晰。
5.2 OpenAPI Schema里required字段设错了
这个错误非常隐蔽。我有一次把companyCode设成了required,因为我调用的API文档说这个参数必填。但我没考虑到:用户给不出公司编码怎么办?
实际测试时出现了这样的对话链:用户说“帮我查快递”,智能体反问“请问快递公司是哪家”,用户说“不知道”,智能体继续穷追不舍:“您需要先知道快递公司才能查询哦”。
这种体验显然是失败的。后来我改成了“非必填”,并在技能指令里写:“如果用户不知道快递公司编码,请先调用公司识别逻辑,或尝试常见的快递公司编码进行查询。”
5.3 同步调用超时,长任务直接失败
第一次做“网站内容分析”类Skill时,我把目标网站的URL直接传给一个同步HTTP接口,结果接口要跑30秒才返回。扣子的同步调用有超时限制,我连续两次看到“技能调用超时”的报错。
后来改成两步式:第一步调用接口提交任务,拿到task_id;第二步等几秒再调用结果查询接口。但技能本身是无状态的,第二次查询怎么找到task_id?我的方案是把这个逻辑封装在工作流里:开始节点接收URL -> 发起任务 -> 等待10秒 -> 查询结果 -> 返回。工作流里的等待节点让整个流程变成异步的,智能体最终拿到的仍然是同步的结果。
这类问题在真实业务中非常常见,建议你在设计Skill时就想清楚:你的接口能不能在几秒内返回?如果不能,务必设计异步轮询方案。
5.4 对“技能发布范围和权限”理解不到位
扣子里的技能可以设置在团队内可见、私有或者发布到商店。我一开始把调试中的技能设成了“团队可见”,结果团队成员乱用、改坏了参数,我还排查了半天。后来学乖了,调试期一律设“私有”,稳定后再放开权限。
6. 让Skill更聪明的进阶优化
当你把基础流程跑通了,可以开始琢磨怎么让Skill更好用。下面这几个方向是我自己实践下来收益最大的。
6.1 在技能描述里加“示例调用”
给技能描述加few-shot示例,是提升调用准确率最立竿见影的方法。比如:
code复制示例:
用户:帮我查一下顺丰单号 SF1234567890
调用参数:{"expressNo": "SF1234567890", "companyCode": "SF"}
用户:快递到哪了?
回复:请提供快递单号
别看这个示例简单,它实际上告诉了大模型两件事:如何把用户输入映射成参数,以及在信息缺失时应采取什么行为。很多“技能不触发”的问题,加一个示例就好了。
6.2 多个Skill的调用优先级
当智能体同时挂了五六个技能时,你可能会发现它选了错误的那个。比如同时有“查天气”和“查快递”,用户说“明天广州能收到包裹吗”,智能体可能先调了天气。因为“明天”关联了天气的触发条件。
解决方法是:在技能描述里加“互斥约束”。比如查快递技能里写“当用户提到包裹时,优先于天气类技能使用”;查天气技能里写“当用户询问快递配送时,不要使用本技能”。虽然这并不完美,但能明显减少调用错乱的情况。
6.3 把“被多个工作流复用的业务逻辑”沉淀为Skill
我后来发现一个更有价值的用法:同一段业务逻辑(比如“解析一个自然语言时间表达式为时间戳”)被多个工作流用到时,与其复制粘贴节点,不如把这段逻辑单独做成一个Skill,然后在工作流里通过“调用技能”节点来使用它。
这对于整个智能体项目的可维护性帮助很大。你改一处,所有引用它的工作流都生效,不用再担心哪里漏改了。
6.4 结合知识库做边界澄清
有些技能判断依赖专业知识。举个例子,创建一个“计算个人所得税”的技能,它需要知道个税起征点、税率表。这些信息放在技能描述里太长,放在代码里又太死。我的做法是:建一个知识库,把最新税率表传进去,然后在技能指令里写“计算前请先从相关文档中获取税率”。
知识库+技能的配合,能让你的Skill在“参数处理”之外多一层“信息检索”能力,特别适合政策法规、产品规格、内部制度这类需要随时更新内容的场景。
最后再分享一个我自己的体会:Skill这个功能的本质,不是让你把接口地址填进平台那么简单,而是帮你重新思考“哪些能力应该交给工具,哪些逻辑应该交给模型”。每次创建Skill之前,多花十分钟把输入、输出、边界条件写清楚,后面能省出几个小时去调试那些莫名其妙的调用问题。现在很多人天天找“好用的Skill”“Skill推荐”,但真正好用的Skill,往往是根据自己业务打磨出来的那个。
