☰
Agent Skills核心解析:从函数调用到能力封装的进化
2026/10/22 16:28:08 网站建设 项目流程

1. 先回答那个最先被问到的问题:Agent Skills 到底是个什么概念

最近大半年,我被问得最多的一句话就是:Agent Skills 是什么?

一开始我挺不屑的,心想这不就是把原来的插件换个说法,又是包装出来的新词。直到我真的把一个项目拆成 Skills 跑了几轮之后,才意识到这个东西和插件、函数调用、工作流确实不是一回事。准确说,它是一种重新组织 Agent 能力的封装思路:把一个个可以被复用的具体能力,打包成有声明、有描述、有入口、有校验条件的独立模块,让 Agent 在运行的时候可以自己判断该在什么场景下加载哪一个。

如果你熟悉以前的 Agent 开发方式,应该知道那种“把所有工具写进一个长长的调用列表”的做法有多痛苦:几十个函数塞进上下文,模型还没开始干活,先把上下文撑爆了。后面大家开始做分组、做白名单、做托管,但本质上还是“我有什么工具,就都告诉模型”。Agent Skills 的思路正好反过来,它更像是给 Agent 一本目录册,目录里的每一项不是简单罗列,而是“你在什么情况下打开这一页、打开后怎么操作、操作完会得到什么”。模型不是被动地遍历所有能力,而是先看目录,再选择性地展开某个 Skill。

这个转变很关键。它意味着 Agent 不再需要同时记住所有工具,而是只需要记住“什么时候去翻哪本手册”。在实际运行中,这种“按需加载”带来的收益比想象中大得多。我见过不少团队把工具全部灌进上下文,结果模型老是选错函数、输错参数,问题不在模型笨,而在“注意力被稀释了”。Agent Skills 相当于给模型做了一次注意力聚焦,把无关能力先收起来,只在需要的时候展开。

所以如果你要我用一句话给 Agent Skills 下个定义,我会说:它是一种让 Agent 在正确时机、按正确方式、调用正确封装的“可复用能力包”的组织方式。它既包括能力本身的实现代码,也包括帮助模型识别和使用这套能力的描述、约束和验证逻辑。

另外提醒一点,现在“Agent Skills”这个词已经被各种平台用得很泛滥。有的平台把商店里的预置 Prompt 插件叫 Skills,有的把一套完整的多步骤自动化叫 Skills,有的干脆把所有工具都改了名叫 Skills。实际做项目的时候,别被名字带偏,你要关注的是三件事:这个能力是否可以被独立描述、是否可以按需加载、是否有清晰的输入输出边界。满足这三条的,无论它叫什么,本质上都是合格的 Skill。

2. Skill 内部到底封了什么:一个可被结构化消费的能力单元

我在拆解了一个又一个 Skill 之后,发现无论它用什么格式、什么运行时,内部基本都跑不掉四层东西:声明层、描述层、执行层、校验层。理解这四层,比单纯抄一个示例文件重要得多,因为绝大多数 Skill 失效的问题,都出在“声明和实际行为对不上”。

2.1 声明层:给模型一张“身份证”

声明层是模型最先看到的部分。它通常包含 Skill 的名称、适用的场景标签、参数签名、依赖条件和版本信息。这里最核心的字段不是名字,而是“何时使用”的触发条件描述。很多人写声明的时候,把名字起得很宽泛,比如“数据分析”,结果模型在用户聊天气的时候也把数据分析 Skill 调起来了——因为描述没有写清楚边界。

我建议在声明层里就把触发场景写成句子,而不是几个孤立的标签词。例如不要只写“数据分析”,要写“当用户上传或引用了包含多行数字的表格,并希望做汇总、分组、趋势判断时使用”。我做过一次对比测试,同样的 Skill,用短标签做声明的版本,误调用率接近三成;改成完整句子描述之后,误调用率降到了个位数。这个数据不一定普适,但方向和道理是明确的:模型依赖描述做判断,描述越像人话,决策越稳。

一个典型的声明文件,我见过最简单也最有效的写法大概长这样:

name: "report_cleaner" description: > 当用户提供销售周报、月报等结构化文档,需要把乱格式、重复行、 合并单元格清理成规整表格时,使用本技能。 version: "1.2.0" inputs: file_path: type: string required: true description: "待清洗文件路径" outputs: cleaned_path: type: string description: "清洗后文件路径" tags: ["report", "excel", "clean"]

这里面的描述不是写给程序员看的,是写给模型看的。它不关心你怎么实现清洗,只关心“什么情况应该亮出这张身份证”。

2.2 描述层:不是“说明文档”,而是模型的操作手册

描述层和声明层很容易混淆。声明层回答“什么时候用”,描述层回答“用了之后怎么执行”。描述层通常包含执行步骤、注意事项、常见报错和处理方式。它不要求模型背下来,而是让模型在调用 Skill 之后知道第一步做什么、第二步做什么、哪些环节容易出错。

我用一个生活化的类比来解释:声明层像厨房门口的菜单招牌,写的是“本店供应酸菜鱼”;描述层像后厨的操作卡,写的是“鱼片要斜切、腌料要抓匀、酸菜要先炒出味”。模型不是厨师,但它是调度员,它需要确保后厨按正确流程出菜。

描述层里最容易写过头,也最容易写不足。写过头,把大量无关背景、历史原因塞进去,模型容易抓不住重点,上下文也被白白占用;写不足,只写一句“调用清洗函数”,模型不知道在什么情形下需要做二次校验,遇到边缘输入就会不知所措。我实践的折中方法是:描述控制在两千字以内,分步骤写,每步都以动作开头,例如“先读取表头”“再定位空行”“最后输出新文件”。这样模型在编排时,能更准确地把参数传给底层函数。

2.3 执行层:真正的代码或脚本入口

执行层是 Skill 落地的部分,可以是一个 Python 脚本、一个 Node 函数、一个容器命令,甚至是一段可以被解释器执行的 Shell 逻辑。关键在于它的边界要足够封闭:输入什么、输出什么、运行时限是多久、是否有副作用,都必须在执行接口上写清楚。

我做过一个内部项目,叫“模拟项目X”,里面有一个发邮件的 Skill。最初它的执行层直接调公司内部邮件接口,后来发现测试环境和线上环境用的凭据完全不一样,导致 Skill 在测试环境跑得很欢、一上生产就静默失败。问题不在 Skill 逻辑,而在执行层没有把环境依赖当参数。后来我在执行层统一加了环境标识参数,要求调用方显式传入“测试”或“生产”,才把这个坑填上。这是执行层最容易忽略、但价值极高的一点:把所有外部依赖都显式化。

2.4 校验层:输出不校验,等于没封装

校验层是很多人会漏掉的部分。Shell 一个脚本跑完,返回一个字符串,Agent 就直接拿去答用户了,这是很危险的。合格的做法是在执行层之后加一个输出约束:字段齐不齐、格式对不对、数值范围合不合理。校验不过就返回一个结构化的错误码,而不是让模型去猜。

从工程化角度说,Skill 其实就是一个小型服务,只不过服务的消费者主要不是人,而是模型。既然是服务,就得有入参校验、出参校验、错误处理、超时机制。我在实际开发时,会强制要求每个 Skill 返回统一格式 JSON,至少包含三个字段:status、data、message。这样无论下层发生了什么,上层编排逻辑都能稳定处理,不会因为某个 Skill 返回了裸字符串而中断整个 Agent 流程。

3. Agent Skills 和函数调用、插件、工作流的真实边界

很多人把 Agent Skills 和以前的概念混在一起,其实它们在设计粒度、触发机制、可复用程度上差得挺远。我在团队里经常用一张边界表帮新人理清关系,这里也分享出来。

维度函数调用Agent Skills插件工作流
粒度单个操作一组完整能力一组能力的集合多步骤业务路径
触发方式模型直接选函数模型先定位描述再展开通常由用户显式安装或唤起由事件或条件驱动
复用性低,通常函数级高,跨场景复用中,依赖平台生态低,绑定具体流程
状态管理无状态可以有内部状态有独立生命周期依赖流程上下文
对模型上下文占用全部可见按需展开启用后可见按阶段暴露

3.1 函数调用是一只手,Skill 是一本手册

函数调用解决的是“模型如何调用一个工具”的问题,本质上是把一组参数映射到一个函数入口。它的优点是轻量、直接,缺点是一旦数量多了,模型的选择压力就变大。

Agent Skills 解决的则是“模型在什么情形下应该知道有一个特殊工具”的问题。它给每个能力包了一层描述,相当于给模型提供了一本手册。模型不需要同时看到所有手册的详细内容,只需要看到目录和摘要。这就是我前面说的“按需展开”的核心价值。

举个例子:一个函数“send_email”的函数调用,模型只需要知道参数是收件人和正文。但如果把“给客户发周报邮件”这个能力做成 Skill,它可以包含选择模板、拉取数据、渲染正文、检查附件、发送、记录发送结果这一整套流程。模型在对话中一旦判断用户意图匹配“周报邮件”,就展开整个 Skill,而不是自己一步步调五个八个函数,这既省上下文,也减少出错点。

3.2 插件重在生态,Skill 重在能力内聚

插件这个词在浏览器、IDE、聊天应用里用得太多,通常指外部扩展包,需要在宿主环境里安装、启用、配置。插件和 Agent Skills 的边界不是技术决定的,而是生态位决定的:插件一定绑定某一个宿主平台,离开了这个平台,插件没有意义;Skill 则更自由,它可以跟随 Agent 的配置迁移到另一个运行时,前提是你按标准方式描述。

我见过有人把一个绘图插件硬说成“Skill”,其实它就是一个普通插件:用户先在界面安装,再在对话里触发,它不认识其他 Agent 框架。真正称得上 Skill 的东西,至少应该把“何时用、怎么调、返回什么”写在一个自包含的包里面,让不同 Agent 都能读取。换句话说,插件是给“某个具体的 App”用的,Skill 是给“任意符合标准的 Agent 执行器”用的。

这个话题往下深挖就要牵扯到 Agent 互操作性了。目前市面上已经有一些项目在做统一的“能力描述格式”,不管背后的执行语言是什么,只要遵守描述规范,就能被多个 Agent 运行时识别。我个人很看好这个方向,Serverless 时代的函数接口标准化过一轮,Agent 时代的“技能接口标准化”也会来一轮。

3.3 工作流是流程,Skill 是能力

工作流解决的是“多步骤如何按顺序执行”,强调流程控制:先做 A,再做 B,如果遇到情况 C 就跳到 D。典型的例子是把“读取邮件—提取附件—解析数据—写入表格—发送通知”固定成一个自动化流程。

Skill 不强调固定的执行顺序,它更像一个“可以被随时调用的资源包”。它可以内部有流程,但对外暴露的只是“给我一个输入,我还你一个结果”。两者之间的关系是:Skill 可以成为工作流中的一个节点,而工作流可以编排多个 Skill。我见过有人非要二选一,其实没必要。正确姿势是按照复用频率来划分:需要固定复用、跨场景抽出的是 Skill;只在某个特定业务里跑一遍的是工作流。

3.4 一个容易混淆的细节:Skill 和 Prompt 的关系

还有一类东西也经常被叫做 Skill,实际上就是精心设计的 Prompt 模板。Prompt 模板解决的是“模型该按什么规则思考”,没有可执行的代码,没有真正的输出校验。Skill 则更完整,它既包含模型需要知道的规则描述,也包含实际能跑的工具脚本。

我在做内部培训时常说:如果你的所谓 Skill 只是几段话术,没有可执行模块,那它本质上是一个“角色包”,不是 Agent 的能力包。Agent 的能力包一定包含两个并行的通路:一个通路是给模型看的描述,另一个通路是给机器执行的代码。只有描述没有代码,模型就只能“嘴上说会干”,干不成实事。这个区分如果拎不清,后面设计和评审都会跑偏。

4. 我实测过的三种 Skill 实现路径,以及各自的选型逻辑

关于 Agent Skills,我早先也走了不少弯路。这里说说我真刀真枪跑过、也踩过坑的三种实现路径。它们不是谁取代谁的关系,而是适用场景不一样。我按“从轻到重”的顺序来讲。

4.1 路径一:把单个函数直接声明成 Skill

最轻量的做法是把一个已有函数包装成 Skill,不改代码,不启动服务,只加一层 YAML 声明和一个入口描述。适合那些参数简单、行为单一、没有内部状态的场景,比如“将文本转成 Markdown 表格”“提取卡片中的日期字段”。

我用一个常见的“清理 CSV 重复行”功能举例,传统函数大概是这样:

def remove_duplicate_rows(file_path: str, key_column: str) -> str: ... return output_path

把它声明成 Skill,只需要额外提供一份 YAML,把描述写清楚,再让 Agent 框架通过约定好的“技能中心”索引到它:

name: "csv_dedup" description: "当用户需要删除CSV中某列的重复行时使用" inputs: file_path: "string" key_column: "string" outputs: output_path: "string"

这个路径的好处是改动小、见效快,特别适合验证某个能力是否值得做深。缺点是不适合复杂流程,函数内部的逻辑一旦超过一屏,模型很难通过声明理解全部上下文。

我的选型建议是:能力逻辑稳定、长期不变、并且只有一两个入口的场景,直接用路径一。如果这个函数以后要扩展多个子步骤,就别硬塞,趁早升级到路径二。

4.2 路径二:脚本包加描述文件,做成相对独立的 Skill 包

当能力开始包含多步逻辑,比如“下载文件—预处理—分析—生成图表—汇总截图”,我就把它从单函数升级成目录包。目录的结构一般长这样:

report_skill/ manifest.yaml run.py requirements.txt README.md assets/

manifest.yaml负责对外的声明和描述,run.py是执行入口,requirements.txt管依赖,assets放模板或静态资源。Agent 运行时在遇到匹配意图时,会把整个目录交给执行器,执行器按 manifest 里定义的入口跑起来。

这种路径的好处是清晰、可维护、可版本化。把它丢进 Git 仓库,系统可以通过更新记录做版本回滚。如果团队里有多个 Agent 需要同一套能力,整个目录复制过去就能用,比来回复制函数经历要好维护得多。

我做完第一版报表清洗 Skill 之后,最大的感受是:目录包天然给了我做工程约束的边界。哪个 Skill 用了哪些依赖、更新过哪些版本,都是可以审计的。单函数模式做不到这一点,你只能靠代码 review 猜它改了啥。

4.3 路径三:用平台专用格式做声明式注册

再往上走一步,就是使用某些 Agent 平台推出的专用 Skill 格式。这类格式通常有一套更严格的 schema 校验,甚至提供可视化调试面板,可以在线测试每个 Skill 的输入输出。

这类路径适合已经有成熟 Agent 产品、需要做团队级或商业级能力管理的团队。它背后的设计逻辑是:Skill 不仅仅是一个代码包,还是一种“产品能力”。在线注册、在线分发、在线审计,都是单机脚本包做不到的。

但我得诚实地提醒:平台专用格式也意味着绑定。假如你换一个 Agent 运行时,这类格式是否兼容完全取决于平台方。我建议你先把能力本身打磨好,再考虑是否迁移到专用格式。不要开头就奔着“标准化”去搞一套重量级注册体系,等 Skill 数量超过三十个再规划也不迟。

4.4 选型要同时看三个变量

我最后总结出来的选型逻辑是三个变量:能力的复杂度、复用的范围、团队对审计的要求。

  • 能力复杂度高(多步骤、有分支、有状态),选目录包。
  • 复用范围广(多个 Agent 都调),选标准化的 Skill 包,别和单个宿主强绑定。
  • 审计要求强(需要可以追溯谁在何时调用了什么),选平台专用注册方式。

这三个变量不是并列的,而是递进关系:先说清楚能力本身,再谈复用范围,最后再决定审计方案。顺序反了,很容易出现“为了上平台而设计 Skill”的怪圈——能力还没想清楚,先被框架格式绑死了。

5. 我在落地 Agent Skills 时踩过的那几个坑

好的概念讲起来都很顺,落地的时候全是细节。我在这里把真正踩过的坑摊开讲,每一坑背后都对应一次线上事故或者一次返工,你不一定全遇到,但遇到了能少走好几天的弯路。

5.1 描述写得像广告词,模型反而视而不见

第一个坑就是描述过度包装。我一开始做 Skill 描述的时候,总觉得写得越详细、越华丽,模型就越容易理解,于是把“用户如果对文件格式不满意,我们可以帮他清洗成规整表格”这种话写进了描述。

结果非常讽刺:模型真把它当成“如果字眼里出现‘规划’就触发”的歧义源,误调用率反而提高了。后来我才明白一个关键点:模型在意图匹配阶段不是在做阅读理解,而是在做“候选排序”。描述里头的信息熵太高,真正的触发特征反而被淹没了。

我后来的写法是:把触发条件放到最前面,说清楚边界,再用一个负例说明不要触发。比如:

当用户明确提到“清理重复行”“删除重复项”“去重CSV”时使用。 不要仅因为表格存在其他格式问题而主动调用。

这种写法实测效果比长篇大论好很多,因为模型终于知道哪些信号是“强触发词”,哪些是“干扰项”。

5.2 技能之间互相抢占,上下文出现“伪重叠”

第二个坑来自 Skill 之间的边界不清晰。我曾经同时注册了两个 Skill:一个叫“给文件排序”,另一个叫“合并表格”。结果用户说“帮我把这两个表格按日期排一下再合并”,系统同时调用了两个 Skill,两个模块各自跑了半截,最后给出的结果非常诡异。

表面上看是模型选错了,往深里看是这两个 Skill 的描述边界有重叠:“排序”可以操作表格,“合并”也可能需要排序。解决方案不是增加更多描述预防,而是重新界定能力粒度:把“表格排序并合并”做成一个独立 Skill,或者让“合并表格”这个 Skill 显式说明“如果你还需要排序,请先调用排序技能再调用我”。这本质上是模块依赖管理,和写微服务时处理服务边界是一个道理。

5.3 权限和安全边界没写清楚,差点出大问题

第三个坑最要命。我的某个 Skill 里有一段逻辑,可以根据传入的路径读取文件,然后执行一个很复杂的清理脚本。测试环境一直没问题,但后来在线上演示时,用户故意传了一个特殊构造的路径,脚本试图读取系统目录下的配置文件。

幸亏当时文件系统权限收得比较紧,没有真读出敏感内容,但那一下把我吓出一身冷汗。问题不在脚本本身,而在于 Skill 的声明里没有声明“哪些路径是允许访问的”。任何 Skill 只要是处理文件的,就必须在内部做路径白名单校验,不能相信来自模型或用户的参数。

我给自己的团队成员定了三条铁律:

  • 所有涉及文件或外部资源的 Skill,必须在入口处做路径归一化和白名单检查。
  • 所有可以触发网络请求的 Skill,必须限制目标域名列表。
  • 所有可以写数据的 Skill,必须默认走“先预览后提交”模式。

这三条现在写进了我的团队规范,不是靠模型自觉,而是靠 Skill 自身的执行层去强制校验。

5.4 只测一两个 happy path 就上线,风险极大

最后一个坑是关于评测的。很多团队测试 Skill 的方式,就是找两个正常用例跑一跑,发现“看起来还行”就上线。我也干过这事,直到一个线上用户发来一份包含合并单元格、隐藏行、日期格式混乱的表格,清理脚本直接返回了一个看似正常、实则丢数据的文件。如果没有人眼核对,错误数据就被当成真相发给客户了。

打那以后,我养成了一个习惯:每个 Skill 上线前必须准备一份“脏数据测试集”。不是测正常流程,而是专门测表格里有空白、有乱码、有超大文件、有空列表、有恶意路径的情况。Skill 能不能给出合理错误提示不重要,重要的是不能静默返回一个错误结果。

这里可以用最简单的方法来降低风险:在 Skill 返回结果前加一道“人理解校验”,把关键结果字段抽取出来,做一个“确认摘要”,让 Agent 在回答用户前先描述自己做了什么。这个摘要不是为了给用户看,而是为了让关键信息对用户可见,减少黑箱输出的概率。

6. 从一个 Skill 开始:我建议的完整上手路径

最后这部分是给正准备动手搞 Agent Skills 的人一点具体建议。别一上来就想着建平台、设计 Schema、做注册中心,我见的多数项目都是被这种“大而全”的规划拖垮的。正确路径是从一个最小的能力开始,先跑通,再横向扩展。

6.1 用“三个一”标准挑出第一个 Skill

挑第一个练手 Skill 的时候,我推荐“三个一”标准:一个明确输入、一个明确输出、一个独立场景。不要贪多,不要挑那些要联动多个数据源的场景。

比如你现在每天要做的一件事是把会议录音转成文字摘要,这是一个很好的首练场景:输入是一段录音或文字记录,输出是一段结构化摘要,使用场景也非常独立。你要是选“管理用户全生命周期”这种东西当第一个 Skill,那基本等于自杀式练手,因为边界根本切不开,你会被各种交叉依赖拖死。

6.2 编写顺序建议:先写描述,再写实现

我习惯先写描述,再写执行代码。因为这个顺序能倒逼你把边界想清楚:如果你连“什么情况下触发这个 Skill”都写不清,那代码写出来大概率也是乱摊子。

只提一个实操细节:描述文档写完,先拿给同事看,不要看实现,只看描述问他们一个问题:“你根据这段描述,能判断什么时候应该调用这个 Skill 吗?”如果同事答案模棱两可,那模型也不可能判断对。这是成本最低的验收方式,比任何测试框架都高效。

6.3 接通 Agent 主循环时,牢记三个“显式”

把 Skill 接入 Agent 主循环时,有三个信息必须显式暴露给主循环:

  • 预期耗时:模型要决定是同步等,还是异步轮询。
  • 副作用说明:会不会写库?会不会发消息?这影响模型在调用前的谨慎程度。
  • 失败模式:哪些情况会返回错误?错误码有哪些?这影响模型在卡住时怎么补救。

早期我没把失败模式写清楚,模型遇到 Skill 报错时只会说“抱歉出错了”,完全没有下一步动作。后来我把常见错误码都列在描述层,模型开始能根据错误码提示用户“文件路径不存在,请重新上传”,体验完全是两个档次。

6.4 渐进式扩展的节奏:从单体到 Skill 的拆分信号

最后说说扩充的时机。你的第一个 Skill 可能是一大坨逻辑,只要能跑通就不用急着拆。等出现下面三个信号中的任何一个,再考虑拆分成更小的 Skill:跨场景复用了同段逻辑;不同团队想独立迭代某个子模块;上下文或超时费用已经大到不可忽略。

曾经有人问我,是不是一开始就该把所有工具都拆成很细的 Skill,我说不要。Modularity is a technique,不是一个目标。拆得太细,Agent 要在十几个候选之间做选择,上下文依然会被大量目录描述占满,效果反而更差。合理的拆分时机,永远是“当前的粒度开始阻碍扩展”的时候,而不是“听起来更优雅”的时候。

在我自己手上这个“模拟项目X”里,最初一个大的“报表助手”被拆成了三个 Skill:“报表清洗”“报表生成”“报表分发”。每一次拆分,都是因为出现了明确的独立使用场景,而不是因为想“做得更规范”。这个节奏走下来,技能库看起来简洁,维护成本也低,Agent 选择时也不会眼花缭乱。

回到开头的问题,Agent Skills 到底是什么?它是一套能力组织的思路,也是一个工程化的封装手段。你可以从今天手头正在做的重复性工作里挑一个,用描述先行的方式写出来,也许一个上午就发现,Agent 干活的方式和以前不太一样了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询