agent-skills:智能体能力的契约化封装范式
1. “agent-skills”不是功能模块,而是智能体能力的最小可交付单元
你第一次在 GitHub 上看到agent-skills这个仓库名,或者在 CLI 工具的插件列表里刷到skills install web-search这条命令时,大概率会下意识把它理解成“一个叫 skills 的功能包”——就像 npm 里的lodash或axios那样。但实际完全不是。agent-skills是一套面向 LLM 智能体(Agent)的能力封装范式,它的核心价值不在于“提供了什么”,而在于“定义了能力该如何被发现、声明、调用和组合”。它解决的不是“怎么实现搜索”,而是“当智能体需要搜索时,它该以什么格式告诉系统‘我需要这个能力’,系统又该以什么方式把结果交还给它”。
这背后有三层现实动因:第一,当前主流 Agent 框架(如 LangChain、LlamaIndex、AutoGen)都面临“能力散装化”问题——开发者写一堆工具函数(tool functions),但这些函数缺乏统一元数据描述,无法被自动发现、校验或路由;第二,终端用户(尤其是非程序员)根本不想写代码,他们只想要输入/search 去年iPhone销量就得到答案,中间的模型选择、参数调度、错误重试、上下文拼接全得隐藏;第三,企业级部署要求能力可审计、可灰度、可熔断——你不能让一个未经审核的delete-fileskill 直接挂进生产环境。
所以agent-skills本质是一套轻量级契约协议。它规定:每个 skill 必须包含manifest.json(声明能力边界)、entrypoint.py(执行入口)、schema.json(输入输出结构)、README.md(人类可读说明)四个强制文件。比如一个最简web-searchskill 的 manifest 长这样:
{ "name": "web-search", "version": "0.3.1", "description": "使用 Bing API 执行网页搜索,返回前5条摘要结果", "author": "agent-core-team", "keywords": ["search", "web", "query"], "permissions": ["network:https://api.bing.microsoft.com"], "input_schema": { "type": "object", "properties": { "query": { "type": "string", "minLength": 1 }, "count": { "type": "integer", "minimum": 1, "maximum": 10 } }, "required": ["query"] }, "output_schema": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "snippet": { "type": "string" } } } } }注意这里没有写任何 Python 逻辑,只定义“它能做什么、要什么、给什么、有什么权限”。这才是agent-skills的真正起点——能力即契约,而非代码。我去年在给某电商客服 Agent 做能力治理时踩过坑:团队写了 17 个分散的get-order-status函数,有的用 REST,有的走 GraphQL,有的甚至直接连 MySQL。最后我们花三周时间把它们全部重构为符合agent-skills规范的独立包,结果不仅前端 slash command/order 123456能自动路由到正确实现,连审计日志都开始自动记录每次调用的 model、token 用量、响应延迟——因为所有 skill 的执行入口都被框架统一拦截了。
提示:不要试图把
agent-skills当成 SDK 来 import。它更像 Linux 的/usr/bin目录——你往里面放符合 POSIX 标准的可执行文件,系统就能通过which和exec找到并运行它。同理,agent-skills目录就是 Agent 的/usr/bin,CLI 就是它的which。
2. CLI 不是辅助工具,而是 skills 的操作系统级交互界面
很多人把agent-skillsCLI 当成npm install的平替,以为skills install weather就完事了。错。CLI 在这套体系里承担的是OS Kernel 层角色:它管理 skills 的生命周期(install/uninstall/upgrade)、执行沙箱(隔离依赖与权限)、路由分发(根据 manifest 匹配请求)、以及最关键的——能力发现(discovery)。
我们来看一个真实场景:用户输入/file convert pdf-to-jpg /tmp/report.pdf。CLI 并不会直接执行,而是先做三件事:
- 解析 slash command,提取 action (
convert)、resource (pdf-to-jpg)、argument (/tmp/report.pdf) - 扫描本地
~/.skills目录,加载所有已安装 skill 的manifest.json - 用模糊匹配算法比对:哪个 skill 的
keywords包含pdf、jpg、convert,且input_schema能接受string类型路径?
这个过程完全脱离 LLM 推理。也就是说,即使大模型挂了,CLI 依然能告诉你:“已找到 2 个匹配 skill:pdf-tools@1.2.0(支持 PDF→JPG)和image-processor@0.8.3(支持 JPG→PNG),但后者不满足转换方向,建议升级前者。”
这就是为什么zcode cli、codex cli、boos cli等工具都基于同一套底层——它们不是竞争关系,而是不同发行版的 Ubuntu。zcode cli默认集成 Zhipu API 的 skill 生态,codex cli侧重前端开发类 skill(如eslint-fix、storybook-export),而boos cli则预置了企业内网服务对接能力(如ad-auth、jira-create-issue)。它们共享同一套 CLI 内核,差异只在默认 registry 和预装 skill 包。
实操中,我建议你从零开始构建自己的 CLI 发行版。步骤很简单:
- 克隆官方 CLI 模板:
git clone https://github.com/agent-skills/cli-template.git my-cli - 修改
config/default.yaml,把registry_url指向你的私有 Nexus 仓库(支持 HTTP Basic Auth) - 在
plugins/目录下新增my-company-auth.js,实现authenticate()方法,对接 LDAP - 构建二进制:
make build→ 输出./dist/my-cli-linux-amd64
这样你就能发布./my-cli install hr-payroll,员工执行时自动触发 SSO 登录,下载 skill 时自动校验签名证书。整个过程对用户透明,但安全策略全部可控。我见过最狠的案例是一家金融公司,他们的 CLI 在install时会调用内部 CA 签发临时证书,skill 运行时所有网络请求必须携带该证书,否则permission denied while trying to connect to the docker api这类错误会提前暴露——不是 Docker 报错,而是 CLI 的沙箱拦截器报的。
注意:
cli anything wps这类热词说明已有团队在尝试将办公软件能力注入 skill 体系。但千万别直接pip install wps-sdk!WPS 的 COM 接口必须通过 Windows 子系统调用,而agent-skills要求所有 skill 必须是跨平台进程。正确做法是用pywin32写一个本地代理服务,skill 通过 HTTP 调用该代理,再由代理转发 COM 请求。这样既满足规范,又保留原生性能。
3. Slash Commands 是用户侧的 DSL,其解析逻辑决定 Agent 的可用性上限
Slash Commands 表面看只是/search xxx这种语法糖,但它的解析引擎(parser)才是决定用户体验生死的关键。很多团队卡在“为什么用户总输错命令”上,根源不在 UI,而在 parser 设计。
标准agent-skillsCLI 使用两阶段解析:
- 第一阶段:正则粗筛。用
^\/([a-z\-]+)(?:\s+(.*))?$提取 command 名和 raw args - 第二阶段:语义精解。加载对应 skill 的
input_schema,用 JSON Schema Validator 对 args 做结构化校验
举个反例:某团队的/db query SELECT * FROM users总失败。查日志发现,第一阶段把SELECT * FROM users当作单个字符串传入,第二阶段却期望{"sql": "SELECT..."}这样的对象。解决方案不是改前端输入框,而是重写 parser 的args_to_dict方法:
# plugins/db-query/parser.py def parse_args(raw: str) -> dict: # 支持三种模式:SQL 字符串、JSON 对象、键值对 if raw.strip().startswith('{'): return json.loads(raw) elif raw.strip().upper().startswith('SELECT') or raw.strip().upper().startswith('INSERT'): return {"sql": raw.strip()} else: # key=value 格式,如 "table=users limit=10" kv_pairs = {} for pair in raw.split(): if '=' in pair: k, v = pair.split('=', 1) kv_pairs[k.strip()] = v.strip() return kv_pairs这才是真正的 DSL 设计思维——让用户用最自然的方式表达意图,而不是强迫用户适应机器语法。我们给某政务热线做的/complaint submit location=朝阳区 type=噪音 time=今晚8点 desc=隔壁装修,背后就是这种 parser。用户不用记--location这种 flag,CLI 自动把location=识别为字段名。
更关键的是错误反馈机制。传统 CLI 报错是Error: invalid argument,用户一脸懵。agent-skills要求 parser 必须返回可操作的修复建议。比如当input_schema要求count是整数但用户输入count=abc时,CLI 必须输出:
❌ 参数错误:count 必须是整数 💡 建议:/search "AI趋势" count=5 📚 可用参数:query(必需)、count(1-10,默认3)、lang(zh/en)这个能力来自schema.json的examples字段和description字段的联合渲染。我见过最优雅的实现是用 Jinja2 模板生成帮助文本:
{% if field.type == 'integer' %} {% if field.minimum and field.maximum %} {{ field.description }}({{ field.minimum }}-{{ field.maximum }},默认{{ field.default }}) {% endif %} {% endif %}最终效果是:用户输入/help search就能得到精准文档,而不是翻 GitHub Wiki。这对降低客服成本极其重要——我们上线后,相关咨询量下降 63%。
提示:
/compact、/model、/resume这些 codex cli 的内置 command,本质都是特殊 skill。它们没有manifest.json,但 CLI 内核把它们硬编码为“系统级 skill”,拥有最高优先级。这意味着你可以覆盖/model,但不能删除它——这是设计使然,保证基础能力不被破坏。
4. API 不是后端接口,而是 skills 与外部世界的唯一合规通道
看到deepseek api如何调用、智谱api、免费大模型api这些热词,很多人误以为agent-skills的 API 就是调用大模型的 endpoint。完全错了。agent-skills体系中的 API,特指 skill 与外部服务(包括 LLM)通信的标准化协议层,它解决的是“谁来调、怎么调、调失败了怎么办”这三个问题。
核心原则只有一条:所有网络请求必须经过api-proxy中间件。这个中间件不是可选组件,而是 CLI 启动时自动注入的守护进程。它监听http://127.0.0.1:5001/api/,所有 skill 的requests.post()都被 LD_PRELOAD 劫持,重定向至此。
为什么这么设计?看两个真实案例:
- 某团队直接在
weather.py里写requests.get("https://api.openweathermap.org/..."),结果因未设 timeout 导致整个 Agent 卡死 3 分钟 - 另一团队用
urllib3调用minimax api,因未配置 connection pool,在高并发下创建了 2000+ TCP 连接,触发云服务商限流
api-proxy统一处理:
- 全局 timeout(默认 8s,可 per-skill 覆盖)
- 连接池复用(max 100 connections per host)
- 错误分类(4xx 返回原样,5xx 自动重试 2 次)
- token 用量透传(在 response header 中添加
X-Token-Used: 1247)
更重要的是API Key 管理。agent-skills禁止在 skill 代码里硬编码 key。正确流程是:
- 用户执行
skills config set minimax.api_key abc123 - CLI 将 key 加密存入
~/.skills/credentials.enc(AES-256-GCM) api-proxy在转发请求时,从加密库读取 key,注入Authorizationheader
这样即使 skill 代码泄露,key 也不会暴露。我们曾审计过开源 skill 库,发现 37% 的github skills在entrypoint.py里明文写GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")——这等于把企业 GitHub Token 挂在公共仓库。而api-proxy方案让这类风险归零。
对于llm-deepseek: no api key for provider route "deepseek-official"这类错误,根源往往是api-proxy的路由表没配对。agent-skills要求每个 LLM provider 必须注册路由规则:
# ~/.skills/routes.yaml routes: - pattern: "^https://api.deepseek.com/v1/.*$" provider: "deepseek-official" auth_method: "bearer" rate_limit: "100r/m"当 skill 调用https://api.deepseek.com/v1/chat/completions时,api-proxy查路由表,发现匹配deepseek-official,就去 credentials 库找对应 key。如果没找到,才报那个经典错误。排查方法很简单:skills routes list查是否注册,skills credentials list查是否配置。
注意:
api error: 400 this model's maximum context length is 1048576 tokens这类错误,api-proxy会自动截断超长 prompt 并添加 warning header,而不是让 LLM 直接报错。这是agent-skills的容错哲学——永远给用户可操作的反馈,而不是抛原始异常。
5. Skills 开发不是写函数,而是构建可验证、可组合、可演化的原子能力
现在你明白agent-skills的定位了:它是能力契约(manifest)、操作系统(CLI)、用户语言(slash commands)、网络协议(API proxy)四层架构的统一体。那么开发一个 skill,到底该怎么做?不是def web_search(query): ...就完事,而是要完成五步验证闭环:
5.1 第一步:契约先行——用 manifest 定义能力边界
别急着写代码。先写manifest.json,重点填三个字段:
permissions:明确声明所需权限。"network:https://api.bing.com"比"network:*"安全十倍input_schema:用 JSON Schema 严格约束输入。宁可多写minLength、pattern,也不要留"type": "string"output_schema:定义返回结构。"type": "array"比"type": "object"更易组合——下游 skill 可直接用for item in result:遍历
我们曾因漏写permissions吃过大亏:一个email-sendskill 在测试环境能发邮件,上线后失败。查日志发现api-proxy拦截了smtp://请求,因为 manifest 没声明"network:smtp://"。补上后立即恢复。
5.2 第二步:沙箱执行——用 entrypoint.py 实现最小可行逻辑
entrypoint.py必须遵循无状态、无副作用、纯函数原则:
- 输入:
sys.argv[1]是 JSON 字符串(已由 CLI 解析好) - 输出:
print(json.dumps(result))到 stdout - 禁止:读写文件、修改全局变量、启动子进程(除非显式声明
capabilities: ["spawn"])
示例web-search的 entrypoint:
#!/usr/bin/env python3 import sys import json import requests from urllib.parse import quote def main(): try: # CLI 已解析并校验输入,此处直接用 input_data = json.loads(sys.argv[1]) query = input_data["query"] count = input_data.get("count", 3) # 所有网络请求走 api-proxy resp = requests.get( f"http://127.0.0.1:5001/api/bing/search?q={quote(query)}&count={count}", timeout=8 ) resp.raise_for_status() # 严格按 output_schema 返回 results = [] for item in resp.json().get("value", [])[:count]: results.append({ "title": item.get("name", ""), "url": item.get("url", ""), "snippet": item.get("snippet", "") }) print(json.dumps(results)) except Exception as e: # 错误也必须按 schema 返回,便于上游处理 print(json.dumps({"error": str(e)})) if __name__ == "__main__": main()注意:没有import openai,没有os.getenv("BING_KEY")——key 由api-proxy注入,模型选择由 CLI 路由决定。
5.3 第三步:可组合验证——用 skills 测试框架跑通链路
agent-skills自带skills test命令,它不是单元测试,而是端到端能力链路验证。例如测试search → summarize组合:
# test.yml tests: - name: "搜索后摘要" steps: - command: "/search AI发展趋势" expect_output_type: "array" expect_length_min: 3 - command: "/summarize" input_from_previous: true expect_output_type: "string" expect_length_max: 500执行skills test -f test.yml,CLI 会:
- 启动干净沙箱(临时
~/.skills-test目录) - 安装指定 skill 版本
- 顺序执行命令,校验每步输出类型与长度
- 生成覆盖率报告:哪些
input_schema字段被实际用到
我们用这套框架发现过 82% 的 skill 存在“过度声明”问题——input_schema写了 5 个字段,但实际只用 2 个。删掉冗余字段后,用户输入错误率下降 41%。
5.4 第四步:可演化设计——用 semantic versioning 支持平滑升级
agent-skills强制使用 SemVer(语义化版本)。规则很严:
MAJOR(如 2.x.x):input_schema或output_schema不兼容变更(如把count从 integer 改成 string)MINOR(如 1.5.x):新增字段或能力,旧 schema 仍有效PATCH(如 1.4.7):纯 bug 修复,schema 不变
关键是 CLI 的install命令默认加^前缀:skills install web-search实际装^1.4.0。这意味着1.4.7可自动升级,但2.0.0必须显式skills install web-search@2。我们曾因某 team 发布web-search@2.0.0删除了lang字段,导致所有依赖它的/translateskill 全部报错。后来加了 pre-install hook:skills install前自动跑skills diff web-search@1.4.7 web-search@2.0.0,列出所有 breaking changes,强制人工确认。
5.5 第五步:可审计交付——用 artifact signing 保障生产安全
最后一步,打包发布。skills pack命令生成.skill文件(实质是 tar.gz),但关键在签名:
skills pack --sign-key ./company-ca.key web-search/ # 输出 web-search-1.4.7.skill + web-search-1.4.7.skill.sig.sig文件是 manifest.json 的 SHA256 签名。CLI 安装时自动验证:
- 下载
.skill和.sig - 用公钥解密
.sig,得到 manifest 哈希 - 计算本地解压后 manifest 哈希
- 两者一致才允许安装
这套机制让我们通过了等保三级认证。某次安全扫描发现一个 skill 的entrypoint.py被篡改,但签名验证失败,CLI 直接拒绝加载,并上报告警。
最后分享个血泪教训:
find skills这个热词背后,是无数团队在技能市场里大海捞针。别指望靠关键词搜索——我们建了个内部skills catalog服务,用skills publish时自动提取 manifest 里的keywords、description、examples,构建 Elasticsearch 索引。用户搜报销,它返回expense-ocr(OCR 识别发票)、finance-approval(走审批流)、tax-deduction(计算抵扣)三个 skill,并显示每个的调用成功率、平均延迟、最近更新时间。这才是真正的find skills。