Skip to content

第 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' → throw

7.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'; // ❌ 编译错误:字符串不是技能引用
  1. description 是路由的全部依据:写清"做什么 + 什么时候用"("当…时使用"句式);
  2. SKILL.md 只放流程:大块参考资料挪到支撑文件,正文里用相对路径指向它们;
  3. 技能即资产:开放格式意味着同一份技能可以给 Claude Code 等其他 harness 使用,也可以从第三方原样引入;
  4. 短内容用 defineSkill 内联定义,不必为三行说明建目录。

本章小结

  • 技能 = name + description(常驻目录)+ instructions(激活才读)+ 支撑文件(显式才读);
  • 渐进式披露让多技能不增加日常 token 负担;
  • SKILL.md 是 Agent Skills 开放标准:name 与目录同名、description 承载路由;
  • 静态 import 即声明,动态 import 是构建错误;同名只能挂一次;
  • 条件挂载 + 持久状态可实现会话中途解锁能力。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 技能的 description 在什么时候会被模型看到?

2. 关于 SKILL.md 的 frontmatter,下列哪项是强制的?

3. 下列哪种技能导入方式会导致构建错误?

4. 支撑文件(如 POLICY.md)何时进入模型上下文?

🛠️ 动手实践

  1. 把你团队的一份 SOP 写成技能目录(SKILL.md + 一个支撑文件),挂到测试 agent 上验证激活行为。
  2. 故意违反规范(name 与目录名不一致、超长 description),观察 Flue 的校验报错信息。
  3. 用持久状态实现"付费用户才能解锁的高级技能",测试免费账号下模型确实看不到它。

知识可以按需加载了。下一章接入更大的世界:MCP 工具生态