news 2026/10/14 9:17:23

Agent Skills 实战指南:技能描述、参数 Schema、调度与监控全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent Skills 实战指南:技能描述、参数 Schema、调度与监控全解析

最近在复盘一个智能体项目的整体架构时,我发现自己反复撞到同一个问题:同一套大模型底座,有的场景效果很稳定,有的场景效果完全看运气。起初我怀疑是提示词写得不够好,后来一路往下追,才发现根子出在 agent-skills 这层。所谓 agent skills,简单说就是智能体真正“干活”的能力单元——模型负责想,技能负责做。很多项目做砸了并不是模型不够聪明,而是技能层做得太粗糙:描述写得含糊、参数定义随意、返回结果没有结构化、多个技能互相打架。这篇文章,我想把实际项目中设计、实现、调优 agent skills 的完整心得拿出来认真聊一聊。

如果你是刚开始做智能体应用,或者已经在做了但总感觉“效果不稳定”,这篇内容应该能帮你省下不少调试时间。我会从“技能到底是个什么东西”讲起,再讲接口设计的几条硬规则,然后带大家完整实现一个可以跑的 skill,最后聊多技能调度、测试和监控。全程都是我自己踩过的坑和验证过的做法,不绕弯子。

1. Skill 是什么:从一次“翻车”说起

1.1 一次印象深刻的翻车现场

之前帮某团队做一个内部智能体,里面有个盘点库存的技能。我当时图省事,在技能描述里只写了一句话:“查询库存数量”,参数也只给了一个product_name字段,类型是 string。结果模型在实际调用时表现非常离谱:用户问“仓库里还有没有这款鞋”,它把“仓库里还有没有这款鞋”一整句话当作商品名传进去;用户问“帮我看看明天会不会下雨”,它居然也去调用了库存查询技能。

这次翻车让我彻底明白了一个道理:在传统编程里,函数的调用者是自己或同事,大家有默契,知道参数该怎么填;但在 agent 体系里,调用者是大模型,它对“意图”的理解完全来自你写的描述和参数定义。模型不会像人一样“猜到你的意思”,你写得模糊,它就发挥得特别自由。所谓 agent skills,本质上就是一套专门为模型设计的“外挂能力层”,它不只是代码,更是代码和模型之间的一套沟通协议。

1.2 Skill 的基本构成

一个合格的 skill,在我看来至少由四部分组成:

  1. 技能元信息:给模型看的“说明书”,包含名称、职责描述、适用场景、不适用场景和注意事项。
  2. 入参 Schema:定义调用参数的类型、取值范围、默认值和示例,一般用 JSON Schema 表达。
  3. 执行逻辑:真正干活的代码,可以是本地函数、HTTP 调用、子进程甚至容器请求。
  4. 返回结果:执行完反馈给模型的观察结果,通常需要结构化、截断、带状态。

很多人做 skill 只写了第 1 句和第 3 步,忽略第 2 步和第 4 步,然后抱怨“模型怎么老调错”。实际上,模型看不到你的执行代码,它能看到的只有描述、参数定义和返回结果,这三样东西才是你和模型之间的全部沟通渠道。

1.3 技能、工具、插件的区别

这里顺带澄清一个容易混淆的概念:技能、工具、插件,在很多文档里是混着用的。我自己的理解是:

  • 工具(tool)偏向单函数、弱封装,偏向通用能力;
  • 技能(skill)是在工具之上的业务化封装,包含使用说明、边界约束,甚至可以组合多个工具;
  • 插件(plugin)往往是技能加上界面、配置和分发机制的打包产物,面向特定平台的集成形态。

这个划分的意义在于提醒自己:一个 skill 不能只是一段可执行代码,它必须是“一段能被模型正确理解并稳定调用的能力单元”。如果没有配套的模型友好接口定义,那它只能算一个工具,离合格的技能还有距离。

提示:判断一个 skill 合不合格,最简单的办法是把自己当成模型,只看描述和 Schema,不看实现代码,问自己一句:“我知道什么时候该调它、参数该怎么填、返回结果是什么意思吗?”如果答案模棱两可,模型大概率也会模棱两可。

2. 接口设计的三个关键:描述、参数 Schema、返回值

2.1 描述写得好不好,直接决定模型动不动手

skill 的描述至少要回答五个问题:这个技能做什么、什么时候用、什么时候不要用、有什么副作用、有没有前置条件。我们对比一下两种写法:

“查询库存数量:输入产品名称或SKU,返回当前可售库存。”

和:

“查询库存信息:调用库存中心的实时接口,获取指定商品的库存总量、在途量、可售量。适用于用户询问‘还有货吗’‘库存多少’‘什么时候补货’等场景。若用户问的是价格、物流时效、商品参数,禁止调用本技能。注意:库存数据存在约5分钟缓存延迟;商品必须已上架,未上架商品返回 error 状态。”

第一种写法为什么容易翻车?因为它没有告诉模型“什么时候不要用”,模型对无关话题也会尝试匹配;参数含义没写清楚,模型就会把整个用户问题塞进来。第二种写法把触发词、边界词、副作用都写明白了,模型就能更精准地做判断。别觉得“多写几句很啰嗦”,对模型来说,描述里的每一个边界词都可能在关键时刻拦住一次误调用。

2.2 参数 Schema:给模型一份“填空题”而不是“问答题”

参数 Schema 的设计原则可以总结成一句话:能枚举就别自由输入,能结构化就别拍平,能必填就标必填,能给默认值就给默认值,能放示例就放示例。下面是我常用的一个消息发送技能参数定义:

{ "type": "object", "properties": { "recipient_id": { "type": "string", "description": "接收人ID,对应员工通讯录中的 employee_id,格式如 E-0001", "examples": ["E-0001"] }, "channel": { "type": "string", "enum": ["email", "im", "sms"], "default": "im" }, "content": { "type": "string", "description": "消息正文,纯文本,http 链接请用 markdown 包裹", "maxLength": 2000 }, "priority": { "type": "string", "enum": ["normal", "urgent"], "default": "normal" } }, "required": ["recipient_id", "content"] }

这背后的逻辑是:模型在生成参数时其实是在做“有限选项的推断”。如果你给它一个无限宽泛的 string,它会把用户的原始语气词、标点、废话全带进来;如果给它 enum 和示例,它的选择空间被压缩到几个明确选项里,准确率会明显提高。default 值也很关键,模型拿不准某个可选字段时,倾向于少传参,你给了默认值就等于替它做了决定,避免后续代码收到缺失字段。

2.3 返回值设计:模型看到的“观察”比“结果”更重要

skill 执行完,返回值不是给人看的,是给模型看的。模型要靠这段返回值决定下一步:继续追问、调下一个技能、还是直接给用户答复。所以返回值必须遵循几个原则:

  • 只返回模型做后续决策所需的信息,别把完整日志、调试信息一股脑塞进去;
  • 大批量数据必须截断或分页,否则会挤占上下文窗口;
  • 用结构化的 JSON 键值对,而不是大段自然语言;
  • 明确标记执行状态:成功、失败、部分成功;
  • 失败时给出“可行动的错误说明”,不要把堆栈直接丢给模型。

举个例子,一个失败的技能返回Error: KeyError: 'data',模型根本不知道该怎么办;如果返回{"status": "failed", "reason": "上游接口返回空数据", "suggest": "请提醒用户稍后重试或检查账号权限"},模型就能把这句话转述给用户,甚至主动触发重试逻辑。返回值的质量,直接影响整个 agent 的交互体验。

3. 手写一个可用 Skill:以“沙箱执行 Python”为例

3.1 需求背景

智能体经常需要做点计算或数据处理,比如数值统计、JSON 解析、生成临时脚本。最直接的做法是在宿主进程里直接执行模型给的代码,这显然不安全——模型生成的代码可能是死循环,也可能做了危险操作。所以我们需要一个“沙箱执行 Python”技能:模型传入代码,返回执行后的输出。

3.2 接口定义与实现

技能注册用的元信息大概是这样的:

{ "name": "execute_python_code", "description": "在隔离的 Python 沙箱中运行代码,返回 stdout、stderr 和退出码。适用于数值计算、字符串处理、JSON 解析、数据分析辅助等场景。不适合需要访问网络、读取宿主文件系统的程序。", "parameters": { "code": { "type": "string", "description": "需要运行的 Python 源码", "maxLength": 8000 }, "timeout": { "type": "integer", "description": "超时秒数", "default": 5, "minimum": 1, "maximum": 30 } }, "required": ["code"] }

对应实现,我建议第一版用子进程加资源限制,简单可控:

import subprocess import tempfile import os import sys def execute_python_code(code: str, timeout: int = 5): if len(code) > 8000: return {"status": "error", "message": "代码长度超过上限"} with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: f.write(code) script_path = f.name try: result = subprocess.run( [sys.executable, script_path], capture_output=True, text=True, timeout=timeout, env={"PATH": "/usr/bin", "HOME": "/tmp"} ) return { "status": "success", "stdout": result.stdout[-2000:], "stderr": result.stderr[-1000:], "exit_code": result.returncode, } except subprocess.TimeoutExpired: return {"status": "error", "message": f"执行超时(>{timeout}s)", "stdout": "", "stderr": "TimeoutExpired"} finally: os.unlink(script_path)

3.3 几个容易被忽略的细节

这段代码有四个细节值得展开说说。第一,截断输出result.stdout[-2000:]非常关键,模型生成一次死循环或打印大量中间结果的代码是家常便饭,如果放任 10 万字符的输出塞进上下文,整个对话质量都会崩溃。第二,清空环境变量只保留最小 PATH,是为了避免子进程继承宿主的环境变量、不小心改到不该改的地方。第三,超时必须设置并透传,TimeoutExpired会被上层捕获并返回结构化错误,模型看到的是“超时”而不是一堆 traceback。第四,临时文件清理放在finally里,否则一次异常就会留下垃圾文件。

当然,生产级实现不能只用subprocess,至少要用 Docker 容器,配合只读文件系统、网络隔离、CPU 和内存配额。但无论容器化多完善,前面说的截断、状态标记、结构化返回这几条原则是不变的。

3.4 把 Skill 注册进 Agent 循环

技能写好后,还要把它挂到 agent 的主循环里。伪代码大概是这样的:

skills = load_skills(["execute_python_code", "query_order_status"]) def agent_loop(user_input, history): messages = history + [{"role": "user", "content": user_input}] response = model.chat_with_tools(messages=messages, tools=[s.manifest for s in skills]) while response.tool_calls: for call in response.tool_calls: skill = find_skill(call.name) observation = skill.run(**call.arguments) messages.append({ "role": "tool", "tool_call_id": call.id, "content": json.dumps(observation, ensure_ascii=False) }) response = model.chat_with_tools(messages=messages, tools=[s.manifest for s in skills]) return response.content

这里的while循环是为了支持“多轮工具调用”:模型先调技能 A,看到返回结果后又决定调技能 B,最后才给用户回答。我曾经见过有人只做一轮工具调用,模型第一轮返回了结果就没机会继续处理,体验差一大截。工具调用循环是整个 agent 运行时的核心骨架,值得好好打磨。

4. 多个 Skill 共存时的调度与冲突处理

4.1 模型到底是怎么“选”技能的

主流的模型接口会把所有技能描述一起塞进上下文,模型在解码时根据用户问题与描述之间的语义相似度来选择调用哪个技能。这带来一个直接影响:技能的名称和描述决定了选择的效果。命名上我习惯用“动词+宾语”的结构,比如query_order_status、cancel_order,而不是泛泛的order、status。描述里要埋触发词和边界词,就像前面说的“还有货吗”“库存多少”这种显式触发词,能显著提高模型的匹配精度。

当技能数量一多,把全部描述都塞进上下文不太划算。可以考虑加一层“技能路由”:先用一次轻量调用或向量检索,从几十个技能中选出最相关的三五个,再让主模型在这几个候选里做最终决策。这个优化不建议一上来就做,技能少于 10 个时,直接全量塞描述通常效果更好、调试也更简单。

4.2 技能重复与覆盖:两个都能做的事,模型必然选不准

我踩过一次印象很深的坑:系统里同时存在search_product和filter_inventory两个技能,都能返回商品列表。用户的意图如果不够清晰,模型就在两个技能之间随机横跳,同一句话这周走的是 A 技能,下周走的是 B 技能,下游的统计口径直接乱了。后来我把功能合并成search_product一个技能,内部用include_stock参数控制是否需要库存字段,问题立刻消失。

经验法则:功能重叠的技能必须合并,而不是共存。如果两个技能确实有细微差别,就在描述里用“适用范围”字段把边界写得非常清楚,比如一个只返回商品基础信息,一个只返回库存明细,彼此不交叉。还有一种常见坏习惯是在描述里写“本技能查不到就调用另一个技能”,这等于把决策难度踢回给模型,模型会把问题在两个技能之间踢来踢去,最后给你一个含糊的答案。

4.3 技能之间的级联调用:强耦合还是让模型自己串

有时候技能之间是有依赖的,比如“生成分析报告”这个技能,内部要先执行 Python 代码,再读取某个数据源。这里有两种串法:一种是技能 A 的执行代码里直接调用技能 B 的函数,强耦合,稳定可靠;另一种是让模型在对话里自己先调 B 再调 A,灵活但不可控。

我的建议是,稳定路径用强耦合,探索路径用模型自串。凡是用户高频、结果必须稳定的链路,都写成强耦合的复合技能;凡是开放的、创造力导向的任务,才让模型自己组合。因为强耦合的路径一旦测试通过就不会再变,而模型自串每一次都可能走出不同的路线,对线上稳定性是很大的考验。

4.4 版本与下线管理

技能上线久了必然要迭代,这里有两个容易被忽略的坑。第一个是技能版本化:在 manifest 里加version字段,调用日志里记录版本号,否则你很难判断某个线上问题到底是旧版本技能引起的还是新版本引起的。第二个是下线流程:旧技能不要直接删,先标记 deprecated,并在描述里写明“已废弃,请使用 xxx 技能”。直接删除会导致历史对话里已生成的 tool_call 在回放时找不到对应技能,线上会报错;而保留但标记废弃,能让模型在过渡期逐步切换过去。

5. 测试与监控:如何判断一个 Skill 真的好用

5.1 先搞清楚“能跑”和“会被正确调用”是两码事

很多人在 skill 开发阶段只做单元测试,验证输入输出符合预期,就觉得大功告成。但 agent 场景最大的风险根本不在执行层,而在“决策层”:模型该调用的时候没调用,不该调用的时候瞎调用,参数抽取得驴唇不对马嘴。这些错误单元测试全部测不出来,必须专门设计场景评测集。

5.2 构造三类评测样本

我会把评测集分成三类:正样本、负样本、参数样本。正样本是“应该调用该技能”的用户输入;负样本是“绝对不该调用”的输入;参数样本用来验证参数抽取准确性。下面是我常用的一个小表格:

类型示例输入期望行为
正样本“帮我查一下订单 ORD123 到哪了”调用 query_order_status,参数 order_id=ORD123
负样本“今天天气怎么样”不调用任何技能
参数样本“查订单 123 和 456 的状态”调用两次,或传数组,两个订单都不能漏

负样本尤其不能省。我见过太多项目只测正样本,结果模型学会了动不动就调技能,能答的问题也非要绕一圈工具调用,用户在那边等得急躁。

5.3 指标定义与复盘方法

我通常盯四个指标:调用准确率(该调时调了)、误召率(不该调时调了)、参数命中率(参数抽取完全正确的比例)、任务成功率(最终回答用户是否满意)。其中误召率最敏感,描述里多一句边界说明,它可能立刻降一半。

分享一次典型的复盘经历:有次我在query_order_status的描述里加了一句“订单已发货后也可以查询物流信息”,结果误召率从 6% 涨到了 13%。原因是模型遇到了所有跟物流沾边的查询都去调这个技能,但技能实现根本不知道物流详情。后来我把物流查询拆成独立技能,误召率又降了回去。这就是一个很典型的例子:描述里的每个措辞都在塑造模型的行为,改描述等于改产品逻辑,一定要谨慎。

5.4 日志与可观测性

线上一定要给每次工具调用记录 trace_id、模型输入、选择的技能、参数、结果、耗时和 token 消耗。我每晚会看三类指标:平均每轮工具调用次数,超过 3 次说明路由混乱或技能设计太碎;技能执行失败率,持续上涨说明依赖方出问题了;模型生成 tool_calls 前后的推理片段,这部分原始内容能帮你定位描述里的歧义点。没有这些日志,调 skill 就像闭着眼睛调参数,全靠猜。

6. 关于 Agent Skills 的几点个人经验与展望

6.1 不要一上来就堆几十个技能

我见过最激进的项目,第一版就规划了 40 多个技能,最后模型完全学不会路由,成功率惨不忍睹。技能越多,描述之间的语义重叠就越难避免,误召率呈指数级上升。我的建议是从 5 到 10 个核心技能起步,覆盖 80% 的用户需求,跑稳后再慢慢扩展。少几个技能,少很多麻烦。

6.2 换模型版本后一定要重跑评测集

不同版本的模型对描述的理解能力不一样,同一个描述在旧模型上表现优秀,换到新模型上误召率可能翻倍。这是我在升级底座模型时吃过的亏。所以每换一次模型,都要把评测集重新跑一遍,重点看误召率有没有异常波动。这个成本不高,但能帮你少被线上投诉折磨。

6.3 技能的迭代节奏应该像做产品

我会建议把 skill 当成一个持续迭代的产品而不是一次性开发任务。每周花一点时间翻日志里的失败案例,把高频误召和参数错误样例不断补充进评测集,参数 Schema 的修改要做到向后兼容,否则线上的调用会直接报错。技能描述不是写一版就完了,它是整个 agent 项目里最值得反复打磨的部分。

6.4 技能层的未来方向

我自己的判断是,agent skills 会朝着三个方向演进:一是可移植性,一个技能就是一个目录,包含 manifest 和实现代码,未来能像软件包一样在不同 agent 之间复用传播;二是自动生成,让模型从历史对话里总结高频行为,自动产出候选技能;三是反馈闭环,把每次调用的质量信号回灌给描述生成环节,形成自演化的技能体系。目前这些还在很早期的阶段,但方向很清晰。

对我来说,做 agent 项目最大的体会就是:聪明的大脑只是起点,真正决定产品好用不好用的是它能不能稳定地调用一堆设计良好的技能。从这个角度看,skill 层才是智能体真正的产品价值所在,也是值得投入最多心力的地方。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 9:17:01

图神经网络表情识别升温:三种构图路线与核心技术解析

这周把图神经网络相关的新论文过了一遍,第39周的存量不算少,但真正让我停下来反复看的,还是集中在几个方向上。一个是针对物理系统的等变图模型还在稳定出活,另一个是图神经网络表情识别这条线突然热闹了起来,连带着人…

作者头像 李华
网站建设 2026/10/14 9:16:43

SwiftUI 与 AI Agent 结合:用 JSON 技能驱动 UI 自动生成

SwiftUI 和 AI Agent 结合,听起来像是个很“新潮”的方向,但拆开来看,本质上是解决一个非常务实的问题:让 AI 不仅能“想”,还能“动手写界面”。这个实验框架的核心思路,是把 SwiftUI 的声明式描述能力&am…

作者头像 李华
网站建设 2026/10/14 9:16:07

中州韵小狼毫懒人包部署指南:从配置到避坑

简介:这份资源是面向 Rime 中州韵小狼毫(须鼠管)用户的终极懒人配置包,适合希望跳过繁琐配置、直接获得完整输入体验的中高级使用者。包内共 62 个文件,以 30 个 lua 脚本、13 个 txt 字典、12 个 yaml 配置为主&#…

作者头像 李华
网站建设 2026/10/14 9:15:49

模型融合实战指南:从误差分析到动态加权的工程化落地

1. 项目概述:为什么“模型融合”不是锦上添花,而是工程落地的必经门槛“Task5:模型融合”这个标题看似平淡,甚至有点像课程作业里的编号式命名——但它背后藏着一个残酷现实:单模型性能再强,也大概率跨不过真实场景的鲁…

作者头像 李华
网站建设 2026/10/14 9:15:36

Adversarial Autoencoder:隐空间分布对齐的生成模型跳板

1. 这不是“GAN的简化版”,而是理解生成模型本质的一把钥匙如果你刚学完VAE,正打算顺手点开一篇“GAN入门教程”,结果标题写着《Adversarial Autoencoders(小白学GAN系列 二)》,第一反应可能是:…

作者头像 李华
网站建设 2026/10/14 9:14:59

科研论文写作规范与IMRaD结构应用指南

1. 科研论文写作的核心价值与常见痛点第一次接触科研论文写作的研究生往往会陷入一种矛盾状态——既被导师要求尽快产出成果,又对学术写作的规范与技巧感到茫然。我在指导实验室新生时发现,90%的学术写作问题其实源于对科研论文本质认知的偏差。科研论文…

作者头像 李华