第 7 章 · Skills 技能包
本章目标:掌握 SKILL.md 的结构与规范,理解渐进式披露的成本模型,学会把领域知识组织成可跨项目复用的技能资产。
7.1 技能解决什么问题
指令写进 agent 函数有个问题:每轮都要付 token 成本。而大部分专业知识(退款流程、代码审查清单、部署手册)只在特定时刻才需要。
技能(Skill)把这类知识做成按需加载的包:
| 组成 | 可见性 | 作用 |
|---|---|---|
| name + description | 始终可见(目录里一行) | 模型据此判断是否激活 |
| instructions(正文) | 激活后才加载 | 完整操作流程 |
| 支撑文件 | 显式读取才加载 | 清单、模板、参考文档 |
这就是渐进式披露(progressive disclosure):挂载几十个技能,平时只花几十行目录的 token;任务匹配时模型才激活对应技能读取全文。
7.2 编写一个技能目录
技能在磁盘上就是一个含 SKILL.md 的目录:
text
src/skills/refunds/
├─ SKILL.md # frontmatter + 操作流程
└─ POLICY.md # 支撑文件:仅在模型主动读取时加载markdown
<!-- src/skills/refunds/SKILL.md -->
---
name: refunds
description: 端到端处理客户退款请求。当客户要求退款或对扣款提出争议时使用。
---
每次处理退款请求都遵循以下流程:
1. 确认订单号与退款原因。
2. 读取 `POLICY.md`,核对该原因是否满足退款条件。
3. 符合条件:用 `issue_refund` 工具执行退款,并向客户确认金额。
4. 不符合:说明触发的具体规则条款,并提供政策中的替代方案。frontmatter 规范(Flue 按 Agent Skills 开放标准校验):
name必填:小写字母/数字/连字符,≤64 字符,必须与目录名一致;description必填:非空、≤1024 字符——它承载全部路由决策;license/compatibility/metadata可选,仅信息性;- 未知字段被忽略(兼容其他宿主的技能),但打包时
.env、私钥等机密文件是硬错误。
7.3 导入与挂载
技能像普通模块一样静态导入——构建时 Flue 识别导入、校验 frontmatter、把整个目录打进产物:
typescript
// src/agents/support-agent.ts
'use agent';
import { useModel, useSkill } from '@flue/runtime';
import refunds from '../skills/refunds/SKILL.md'; // 直接 import markdown 文件
export function SupportAgent() {
useModel('anthropic/claude-haiku-4-5');
useSkill(refunds); // 一行挂载
return '清晰准确地回答客户支持问题,涉及退款时遵循退款流程。';
}规则与技巧:
typescript
// ✅ 静态导入——构建期解析,dev 下编辑技能目录即时生效
import refunds from '../skills/refunds/SKILL.md';
// ❌ 动态导入直接报构建错误
// const bad = await import('../skills/refunds/SKILL.md');
// ✅ 从 npm 包导入技能(包需发布 SKILL.md 及其子路径导出)
import review from '@acme/review-skills/review/SKILL.md';
// ⚠️ 同名技能一次渲染只能挂一次,重复挂载抛错
useSkill(refunds);
// useSkill(refunds2); // 若 refunds2.name 也是 'refunds' → throw7.4 条件挂载:会话中途解锁技能
所有资源类 Hook 都支持条件声明——配合持久状态可以实现"阶段解锁":
typescript
'use agent';
import { useModel, usePersistentState, useSkill, useAgentStart } from '@flue/runtime';
import basicSupport from '../skills/basic-support/SKILL.md';
import escalationPlaybook from '../skills/escalation/SKILL.md';
export function SupportAgent() {
useModel('anthropic/claude-haiku-4-5');
const [vip] = usePersistentState('vip', false);
// 基础技能始终可用
useSkill(basicSupport);
// 升级手册只在标记翻转后进入目录,
// 运行时会向模型播报目录变化,且不击穿已缓存的 prompt
if (vip) {
useSkill(escalationPlaybook);
}
// 会话启动时异步检查用户等级并解锁
useAgentStart(async () => {
// ...查询 CRM 后通过 setPersistentState 翻转 vip 标志
});
return '处理客户咨询,必要时使用可用的专业技能。';
}7.5 设计高质量技能
markdown
---
name: review-pr
description: 对 Pull Request 做结构化审查。在用户请求审查代码变更或合并前检查时使用。
---
审查流程:
1. 通读 PR 描述与变更文件列表,理解改动意图。
2. 阅读 `CHECKLIST.md` 中的审查清单逐项核对。
3. 每个问题按 blocker / suggestion / nit 分级。
4. 输出汇总报告,blocker 问题必须给出修复建议。四条经验:
typescript
// 技能引用的类型:导入 SKILL.md 得到的是强类型 SkillReference,
// 而非裸字符串——挂载时传错类型会直接编译报错
import type { SkillReference } from '@flue/runtime';
import refunds from '../skills/refunds/SKILL.md';
// 类型上它就是这样:品牌化的引用类型
const ref: SkillReference = refunds; // ✅
// const bad: SkillReference = 'refunds'; // ❌ 编译错误:字符串不是技能引用- description 是路由的全部依据:写清"做什么 + 什么时候用"("当…时使用"句式);
- SKILL.md 只放流程:大块参考资料挪到支撑文件,正文里用相对路径指向它们;
- 技能即资产:开放格式意味着同一份技能可以给 Claude Code 等其他 harness 使用,也可以从第三方原样引入;
- 短内容用
defineSkill内联定义,不必为三行说明建目录。
本章小结
- 技能 = name + description(常驻目录)+ instructions(激活才读)+ 支撑文件(显式才读);
- 渐进式披露让多技能不增加日常 token 负担;
- SKILL.md 是 Agent Skills 开放标准:name 与目录同名、description 承载路由;
- 静态 import 即声明,动态 import 是构建错误;同名只能挂一次;
- 条件挂载 + 持久状态可实现会话中途解锁能力。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 技能的 description 在什么时候会被模型看到?
2. 关于 SKILL.md 的 frontmatter,下列哪项是强制的?
3. 下列哪种技能导入方式会导致构建错误?
4. 支撑文件(如 POLICY.md)何时进入模型上下文?
🛠️ 动手实践
- 把你团队的一份 SOP 写成技能目录(SKILL.md + 一个支撑文件),挂到测试 agent 上验证激活行为。
- 故意违反规范(name 与目录名不一致、超长 description),观察 Flue 的校验报错信息。
- 用持久状态实现"付费用户才能解锁的高级技能",测试免费账号下模型确实看不到它。
知识可以按需加载了。下一章接入更大的世界:MCP 工具生态。