81 个 SKILL.md 文件,分布在 15 个类别里。skills_hub.py 有 4,956 行。这一章讲怎么用纯文本教会智能体做一件它本来不会的事。
一个技能就是一个 Markdown 文件。没有代码,没有编译,没有注册。
---
name: deploy-to-staging
description: 把当前分支部署到预发布环境并验证健康检查
version: 1.2.0
author: platform-team
license: MIT
platforms: [slack, cli]
metadata:
hermes:
tags: [deploy, ci, infra]
related_skills: [rollback-deploy, check-service-health]
---
# 部署到预发布环境
## 前置检查
1. 确认当前分支的 CI 全绿:`gh pr checks`
2. 确认没有未提交的改动:`git status --porcelain`
## 部署步骤
1. 打 tag:`git tag staging-$(date +%Y%m%d-%H%M%S)`
2. 推送:`git push origin --tags`
3. 等待部署流水线:`gh run watch`
## 验证
- 访问 https://staging.example.com/health,应返回 200
- 如果 5 分钟内没有变绿,执行 rollback-deploy 技能
为什么这有用?
模型知道 git、知道 gh、知道怎么发 HTTP 请求。它不知道的是「在你们团队,部署是这么做的」 —— 用哪个命令、按什么顺序、验证什么、出错了找谁。
技能填补的正是这个缺口:不是教模型新能力,而是告诉它「在这个具体环境下,正确的做法是什么」。
文件开头 --- 之间的部分叫「前置元数据」(front matter),是 YAML 格式的结构化信息。
| 字段 | 作用 |
|---|---|
name | 技能的唯一标识。用户可以直接用它调用(/deploy-to-staging) |
description | 最重要的一个字段。它决定模型什么时候会想起来用这个技能。见下一节 |
version | 技能也会演进。团队的部署流程变了,技能要跟着改 |
author | 出问题时找谁 |
license | 技能可以被分享、被开源。需要明确许可 |
platforms | 限定在哪些平台可用。有的技能只在命令行里有意义(涉及本地文件),有的只在聊天工具里有意义(涉及发消息) |
metadata.hermes.tags | 标签,用于分类和检索 |
related_skills | 技能之间的关系。上面的例子里,部署技能指向了回滚技能 —— 出问题时模型知道下一步该看哪个 |
81 个技能全部展开是多少字?假设每个 3 KB,就是 243 KB ≈ 6 万 token。
解法:只常驻「目录」,内容按需加载。
这解释了为什么 description 是最重要的字段。
它是模型唯一能看到的、用来判断「这个技能跟当前任务有没有关系」的信息。
写得好:「把当前分支部署到预发布环境并验证健康检查」→ 用户说「发到 staging」时能匹配上。
写得差:「部署工具」→ 太模糊,模型不知道是部署到哪、部署什么。
写技能描述的原则:写「什么时候该用它」,而不是「它是什么」。
技能本身是纯文本,但围绕它有一整套工程设施:
skills_hub.py 4,956 行 技能的加载、检索、执行编排
skill_ledger.py ★ 技能台账
skill_provenance.py ★ 来源追溯
skill_usage.py ★ 使用统计
skill_linter.py ★ 格式检查
skills_guard.py ★ 安全守卫
skills_sync.py ★ 同步
skill_provenance.py:来源追溯「provenance」(来源、出处)在这里是一个安全概念。
一个技能文件里写的步骤,智能体会照着执行。所以:一个技能文件就是一段可执行的指令 —— 只不过它是用自然语言写的。
攻击场景:有人往你的技能目录里放了一个技能文件,描述写得很正常(「清理临时文件」),内容里却是把敏感数据发到外部。下次模型判断需要清理临时文件时,就会照做。
来源追溯就是回答:这个技能是谁写的?从哪来的?改过没有?
skills_guard.py:安全守卫在技能被加载或执行前做安全检查。可能包括:
skill_linter.py:格式检查「linter」是「代码风格检查器」的通称。技能的 linter 检查的是:
related_skills 里引用的技能是否存在description 是不是太短/太模糊为什么需要 linter:因为技能是给人写的,而人会写错。一个 description 拼错了字段名,这个技能就永远不会被匹配到 —— 而且不会报错,只是「莫名其妙不生效」。Linter 把这类静默失败变成明确的错误。
skill_usage.py:使用统计记录哪些技能被用了、用了多少次、成功率如何。用途:
skill_ledger.py 与 skills_sync.py台账记录技能的完整清单和状态(启用/禁用、版本、来源)。同步负责在多个地方之间保持技能一致 —— 比如从一个共享仓库拉取团队的技能库。
| 技能 | 工具 | 插件 | |
|---|---|---|---|
| 是什么 | Markdown 文本 | Python 函数 | Python 包 |
| 教会模型 | 怎么做(用已有能力) | 能做什么(新能力) | 能做什么 + 改变系统行为 |
| 写的人 | 任何人,包括非程序员 | 程序员 | 程序员 |
| 出错的后果 | 模型走了错路,通常能自己纠正 | 工具报错 | 可能影响整个系统 |
| 常驻成本 | 1 行描述 | 一份 JSON schema(几百 token) | 取决于它注册了什么 |
| 典型例子 | 「我们团队的部署流程」 | 「读文件」「跑命令」 | 「接入 Slack」「换个记忆后端」 |
关键区分:技能不给模型新能力,只给它「在这个环境下的正确做法」。
部署技能里的每一条命令(git tag、gh run watch)模型本来就会执行 —— 它有 terminal 工具。技能提供的是顺序、参数、验证方式、失败时的退路。
这就是为什么技能可以是纯文本:它编码的是知识,不是能力。
related_skills 字段和技能内容里的「执行 rollback-deploy 技能」这样的引用,构成了一张技能之间的关系网。
这和渐进式披露是同一个思想的延伸。
不是「一次性把所有相关知识都塞进上下文」,而是「在需要的那一刻,告诉它去哪里找下一块」。
上下文是有限的、昂贵的。一个好的知识组织方式,应该让智能体在任意时刻只持有它当前真正需要的那部分。
这个原则贯穿了整篇文章讲过的所有机制:技能目录、记忆预取、上下文引擎的选择、工具集的投放 —— 全都是同一件事的不同表现。
81 个技能分布在 15 个类别里。类别本身反映了智能体被期望承担的工作范围:
这个分布说明技能系统的实际用途:把一个通用智能体特化成「这个团队的工程助手」。模型本身是通用的,技能库是团队特有的。同样的模型 + 不同的技能库 = 完全不同的助手。
① 一切扩展点都是抽象基类。平台、记忆、上下文引擎、模型供应商、执行环境、定时提供者 —— 全部是「定义契约,实现可替换」。而且区分了「可叠加能力」和「必须单选的策略」。
② 安全是分层的、fail-closed 的。工具收窄 × 审批红线 × 执行隔离 × 注入检测。每一层都不可靠,叠起来才够用。不确定的时候一律选择「不执行」。
③ 上下文是最稀缺的资源,一切设计围绕它。渐进式披露、委派分区、记忆预取、压缩策略 —— 表面上是七八个不同的机制,本质上都在回答同一个问题:「怎么让模型在任意时刻只持有它真正需要的那部分信息」。