Skill 与渐进式披露
第 9 章的搜索助手打完了“模型 + 工具 + 循环”的组合拳;从这一章起进入第四章「怎么扩展它」——当能力越攒越多,怎么组织?第 7 章末尾说过:工具越多,Agent 越笨——schema 挤占窗口、选择困难。但有些能…
第 9 章的搜索助手打完了“模型 + 工具 + 循环”的组合拳;从这一章起进入第四章「怎么扩展它」——当能力越攒越多,怎么组织?第 7 章末尾说过:工具越多,Agent 越笨——schema 挤占窗口、选择困难。但有些能力的“说明书”天生就大(比如“如何做前端设计”要写好几百行规范)。Skill 机制的解法很优雅:平时只给一行索引,用到时才加载全文。这一章讲这个“渐进式披露”的设计哲学和实现。
1. 问题:说明书比工具还大
回顾第 7 章的困境,它有一个更极端的形态:
工具 schema(常驻上下文) ≈ 100~200 token / 个
一份前端设计规范(完整版) ≈ 2000+ token
十个领域的操作手册 ≈ 20000+ token ← 窗口直接爆炸
比如你想要一个“前端设计助手”能力:涉及设计原则、配色规范、组件用法、无障碍要求——写成完整说明书有几千 token。但用户十个任务里只有一个用得上它。常驻加载 = 为 10% 的需求支付 100% 的窗口税。
这就是一组经典矛盾:
| 方案 | 问题 |
|---|---|
| 全部塞进系统提示词 | 窗口爆炸、费用爆炸、指令淹没(第 5 章) |
| 拆成多组工具 schema | 第 7 章的选择困难 + 窗口税依旧 |
| 不提供 | 能力缺失 |
2. 解法:渐进式披露(Progressive Disclosure)
Skill 的思路来自一个朴素观察:人也不是把所有手册背下来再干活的——需要时翻手册就行。
三层结构:
第 0 层 索引:一行描述(常驻上下文,~10 token)
"skill: 前端设计规范——创建高质量网页时使用"
第 1 层 元数据:触发条件、适用范围(按需注入,~100 token)
第 2 层 正文:完整的操作手册(使用时才加载,2000+ token)
工作流变成:
- Agent 启动时只加载所有 skill 的索引(10 个 skill 也就 100 token)
- 模型遇到匹配的任务,调用
use_skill工具取出正文 - 正文作为工具结果进入上下文,模型按手册执行
代价是多一次工具调用的延迟,收益是窗口的大幅节约。当 skill 数量多、单个体积大时,这笔账非常划算。
3. 实现:给 Agent 装 Skill 系统
沿用第 7 章的封装风格。skill 本体就是一个带元数据的 markdown 文件:
# ---------- skill: 前端设计规范(存为 skills/frontend-design.md) ----------
FRONTEND_SKILL = """# 前端设计规范
## 适用场景
从零创建网页、重构页面视觉、优化 UI 细节时使用本规范。
## 设计原则
1. 布局:移动优先,栅格对齐,留白克制
2. 配色:主色一个、辅色一个、中性色阶,对比度 ≥ 4.5:1
3. 字体:系统字体栈,标题/正文层级不超过 4 级
4. 交互:所有可点击元素有 hover/active 态反馈
## 输出要求
- 交付单文件 HTML,内联 CSS
- 必须通过基本的可访问性检查
"""
然后是 use_skill 工具(伪装成普通工具的“文档加载器”):
from pydantic import BaseModel, Field
SKILLS: dict[str, dict] = {
"frontend-design": {
"index": "前端设计规范:创建或重构网页视觉时使用",
"content": FRONTEND_SKILL,
},
# ... 其他 skill
}
class UseSkillArgs(BaseModel):
name: str = Field(description="要使用的技能名,必须是索引中列出的名称")
def use_skill(name: str) -> str:
skill = SKILLS.get(name)
if not skill:
return f"技能不存在。可用技能:{list(SKILLS)}"
return skill["content"]
关键设计在系统提示词——只放索引,不放正文:
def build_system_with_skills() -> str:
lines = ["你是一个通用助手,具备以下技能:"]
for name, s in SKILLS.items():
lines.append(f"- {name}: {s['index']}")
lines.append("任务匹配某个技能时,必须先调用 use_skill 加载其完整规范再执行。")
return "\n".join(lines)
运行效果(第 8 章的 Agent 原样复用):
用户: 帮我做一个个人主页
⚙ use_skill({"name": "frontend-design"}) ← 模型主动加载规范
↳ # 前端设计规范 ...(2000 token 进入上下文)
⚙ write_file({"path": "index.html", ...}) ← 按规范干活
✔ 完成
窗口对比:常驻方案每次请求都带 2000+ token;skill 方案只在用到的会话里花这 2000。十个 skill 常驻 2 万 token 的场景,skill 机制把日常开销压到 100 token 以内。
4. 和普通工具的本质区别
值得停下来想清楚:use_skill 和普通工具差在哪?
| 维度 | 工具(Tool) | 技能(Skill) |
|---|---|---|
| 本质 | 一个可执行的函数 | 一段给模型看的文档 |
| 执行者 | 你的代码 | 模型自己(按文档行事) |
| 结果 | 外部世界的改变(文件/请求) | 上下文的扩充 |
| 类比 | 锤子 | 使用锤子的教程 |
一句话:工具给模型“手”,技能给模型“方法论”。工具改变世界,技能改变模型的行事方式。两者正交,可组合——skill 正文里可以指示模型“先调用 read_file 再动手”。
也正因如此,skill 有个天然限制:它约束的是统计行为(第 5 章的老话题)。规范写“对比度 ≥ 4.5:1”,模型大概率遵守但不保证——需要硬约束时得配合工具层校验(第 6 章)或代码检查。
5. Skill 的工程形态
真实的 skill 生态(如 Anthropic 的 Agent Skills、Trae 的技能系统)比上面的玩具版多几层工程:
skills/
├── frontend-design/
│ ├── SKILL.md # 正文:规范/方法论
│ ├── scripts/ # 可执行脚本(工具与技能的混合体)
│ └── assets/ # 模板、参考文件
└── code-review/
└── SKILL.md
三个进阶模式:
- 带资源的 skill:SKILL.md 引用目录里的脚本/模板。模型读文档后可以决定执行其中的脚本——此时 skill 成了“工具的打包发行版”
- lock 文件:
skills-lock.json记录每个 skill 的来源和版本,保证团队环境一致(和 pnpm-lock 同一思想) - 三级渐进:索引 → SKILL.md 的目录 → 具体小节。超大手册可以再拆,模型按需翻到“第 3 节”
6. 什么时候该做成 Skill
决策清单(对照第 7 章的工具设计):
做成 Skill,当内容是“知识/规范/流程”—— - ✅ 前端设计规范、代码评审标准、写作风格指南 - ✅ 某业务的操作 SOP(先查表、再核对、后回填) - ✅ 大而全但低频使用(10% 的任务才用)
做成 Tool,当内容是“动作”—— - ✅ 读文件、调 API、执行计算 - ✅ 需要确定性结果(校验、转换) - ✅ 高频核心路径
混合形态最常见:skill 文档里指导模型按什么顺序调用哪些工具。比如“发布技能” = 文档(发布流程)+ 对工具既有能力的编排。
本章小结
- 渐进式披露:索引常驻、元数据按需、正文用时加载——用一次调用延迟换大幅窗口节约
- use_skill 伪装成工具,本质是“文档加载器”:工具给模型手,技能给模型方法论
- skill 约束的是统计行为,硬约束仍需工具层校验兜底
- 工程形态:SKILL.md + 资源目录 + lock 文件 + 三级渐进
- 决策标准:知识/规范/低频 → Skill;动作/确定性/高频 → Tool
Skill 解决了“自家能力怎么组织”的问题。但生态里有大量别人做好的服务(天气、数据库、GitHub)——每个都手写工具太蠢。需要的是一个标准协议,让所有 Agent 都能即插即用地接入所有工具。这就是 MCP。
下一篇:11 · MCP 全景 —— Model Context Protocol:Anthropic 发起的开放标准,正在成为“Agent 界的 USB-C”。解剖它的通信角色、传输方式和 JSON-RPC 报文。