技能设计:把能力写成按需加载的单元
技能(skill)是智能体在需要时才加载的一小块能力,形态通常是一个带元信息的文件加若干参考文件与脚本。它和提示词最大的区别是:提示词是这次说的话,技能是长期维护的资产。这篇讲我在科研工作流套件里踩出来的写法。
先说结论
- 技能好不好用,主要不取决于正文写得多全,而取决于触发条件写得够不够窄;
- 反直觉的一点:正文越长,被正确调用的概率越低——一次加载就灌满上下文,技能反而成了负担,所以要渐进披露;
- 边界(不许做什么)和触发条件同等重要,缺了它,技能会改掉不该改的数据;
- 技能必须可验收,而验收主要靠负例:说"这种情况不该调用它"时,它有没有真的不调用。
一、从一段长提示词到 13 个模块
2026 年 7–8 月,第二个课题又要把"选题—训练—验证—写作—画图"流程重搭一遍。第一版做法很直接:把全流程写成一段长提示词,四千多字,从数据划分写到论文插图。
现象很快就有了:主流程要么整段加载后仍然漏步骤(比如忘了在实验前写判定标准),要么干脆不用它、自己临时发挥;出错时也无法定位——四千字是一个整体,说不清哪一步没执行。
排查方式是把长提示词按阶段切开,逐块看有没有被调用、哪一步被跳过。原因不在内容少,而在没有触发条件:模块不知道"现在轮到我了"。切开之后的形态是 13 个技能模块,加上约 20 个确定性脚本和 90 余项测试;再配上总入口负责判断阶段、按需路由。第二个反转在脚本上:确定性步骤抽出来单独测试之后,测试才有意义——90 余项测试覆盖脚本,不覆盖判断。
二、技能与提示词的区别
| 维度 | 提示词 | 技能 |
|---|---|---|
| 生命周期 | 这次会话 | 长期,可复用,随项目版本管理 |
| 加载时机 | 每次都占用上下文 | 命中触发条件才加载 |
| 可测试性 | 只能看结果 | 触发用例 + 脚本测试,可回归 |
| 演进方式 | 随手改 | 走提交记录,可回滚、可对比 |
三、触发条件与边界
触发条件要写两边:什么时候该调用、什么时候不该。只写正面条件,技能会到处抢活:
name: figure-export
triggers:
- 把实验结果导出成论文用图
- 同一批图要重复生成、格式一致
do_not_use:
- 一次性示意图(直接画更快)
- 数据还没定稿(图会重画)
requires:
- results/metrics.json 已存在边界写"不许做什么",和触发条件一样具体:
- 原始实验记录只读
- 不改数据划分与随机种子
- 不在画图脚本里重算指标(只读 metrics.json)边界不是形式主义:硬件设计流水线的 9 个技能模块按阶段划分(拓扑讨论 → 原理图 → 布局 → Gerber),每个模块只碰本阶段产物,上一阶段产物不齐不进入下一阶段。它挡住的是"边改电路边改布局"这类互相干扰。
四、渐进披露:正文只放最关键的
技能正文只保留四样:触发条件、步骤骨架、边界、产物路径。细节放被链接的参考文件,正文写明"什么时候去读哪个文件":
skills/
├─ research-workflow/SKILL.md # 总入口:判断阶段,路由到子模块
├─ data-split/SKILL.md # 触发 / 骨架 / 边界 / 产物
│ └─ references/split-protocol.md # 细节:分层与种子约定
└─ scripts/
├─ split_dataset.py # 确定性:同输入同输出
└─ tests/ # 90 余项测试挂在这里原因是一次加载的成本:正文里的细节,每个命中它的任务都要付一遍,而多数任务只需要其中一小部分。
性价比最高的一步
给正文设一个行数上限(我定的是 120 行),超了就把细节挪进 references。这个约束逼着你判断"什么必须每次都知道"。
五、验收:怎么知道技能被正确调用
验收的对象是"调用行为",不是输出好不好看。每个模块准备一组触发用例,正例负例都要:
正例:导出这次实验的结果图 → 应调用 figure-export
正例:按上次格式重画第 3 组图 → 应调用 figure-export
负例:画一张流程示意图 → 不应调用
负例:数据还在跑,先看图 → 不应调用(前置产物缺失)跑一遍用例,把"该调的没调""不该调的调了"分开计数,两类都为零,触发条件才算合格。再叠一层脚本测试:约 20 个确定性脚本由 90 余项测试覆盖,保证同样输入同样输出。
容易踩的坑
只测正例会让技能越来越"积极":任何沾边的任务都想加载,抢掉本该由主流程判断的事。负例才是真正的约束力。
六、脚本与判断的分界线
分界线只有一条:同样的输入是否必须得到同样的输出。 是,做成脚本;不是,留在技能里由模型判断。
| 判据 | 归脚本 | 留在技能(判断) |
|---|---|---|
| 输入输出 | 同样的输入必须同样输出 | 依上下文取舍 |
| 出错的代价 | 静默错误,必须靠测试兜住 | 可复核,人能兜底 |
| 例子 | 数据划分、指标计算、图表生成、记录模板填充 | 这个方法是否值得试、结论能不能写、是否进入下一阶段 |
把确定性部分抽干净,剩下的判断才会被认真对待;反之,让脚本"顺手"决定划分比例,测试就失去意义。
七、可复用的清单
- 一件事会在下个项目重现,就写成技能;不会,就留在提示词里;
- 触发条件写两栏:该调用、不该调用;
- 边界写"不许做什么",具体到文件与字段;
- 正文不超过 120 行,细节放 references 并写明何时去读;
- 每个技能配正例与负例触发用例,两类错误分别计数;
- 同样输入必须同样输出的工作抽成脚本,用测试覆盖;