Skill Engineering:让 AI 真正按预期工作的七条设计原则
Skill(或称 Agent 指令包、Prompt Pack)是给大模型的"职业说明书"。但在生产实践中,写得最详细的 Skill 往往最不被遵守。本文从一系列真实事故出发,提炼出七条具有普适性的设计原则——不论你用的是 Claude Code、Cursor、Cline,还是自研 Agent 框架,这些坑都长得一模一样。
写在前面:Skill 的本质矛盾
Skill 的核心机制是:用自然语言文档约束长上下文 LLM 的行为。
这里有个根本性的张力:
| 文档侧 | LLM 侧 | |
|---|---|---|
| 时态 | 静态、有序、确定性 | 动态、概率性、有遗忘 |
| 生命周期 | 写一次永远在那里 | 每次生成都重新"理解" |
| 规则形态 | 显式的 | 执行是隐式的 |
理解这个张力是写好 Skill 的起点。Skill 不是说明书,而是约束系统。
原则一:规则必须落到「检查门」上,而不是只写在文档里
失败模式
你在 style-guide.md 里写:
单个段落不超过 100 字,超出即拆分。
然后 LLM 写出一个 300 字的段落。你问它:"你看过 style-guide 吗?"它说:"看过。"问它:"那为什么超长?"它说:“抱歉,我下次注意。”
——下次依然超长。
根本原因
声明式规则(declarative rule)和执行式检查(imperative check)是两件事。文档里写"不超过 100 字"是声明,但如果检查清单里没有"统计每段字数"这一条,规则就不会被执行。
设计要点
每条硬性规则必须满足三件套:
规则定义(在哪声明)→ 检查清单条目(如何验证)→ 阻断条件(不过怎么办)
| 弱设计 ❌ | 强设计 ✅ |
|---|---|
| “段落不超过 100 字” | “统计最长段落字数 X,X > 100 必须拆分” |
| “避免重复用词” | “列出本文出现 ≥3 次的非功能词” |
| “保持风格一致” | “对照 3 条具体风格指标逐条勾选” |
核心:规则的执行力 = 检查门的强制力。任何无法被某个 checklist 项验证的规则,等同于没写。
原则二:在「加载时」解析资源路径,不在「运行时」让 AI 拼接
失败模式
Skill 里写:
配置文件位于项目根目录的 config/ 下,请读取后执行。
LLM 开始猜测项目根目录在哪,从 /home 开始 find,甚至凭空编造一个路径。
根本原因
LLM 不是 shell,它不会自动解析你的环境变量、模板占位符、相对路径。它看到什么字符串就理解成什么字符串——或者更糟,它会"合理推测"一个错误答案。
设计要点
凡是涉及绝对路径、API endpoint、版本号、运行时配置的内容,都应该在 Skill 装载阶段就替换为具体值,而不是依赖 LLM 在运行时拼接。
| 反模式 ❌ | 正确做法 ✅ |
|---|---|
| prompt 中放占位符 | 种子代码做模板替换 |
| “路径在项目的 config/ 下” | “路径在 /home/user/project/config/app.yaml” |
| LLM 运行时拼接 | 系统装载时硬编码进 prompt |
更广义地说:任何 LLM 不需要推理的工程细节,都不要让它推理。 它的注意力是稀缺资源。
原则三:用「主动指令」,不用「能力陈述」
失败模式
你可以使用 Bash 工具执行脚本来获取数据。
LLM 阅读后内心 OS:「好的,我了解我有这个能力了。」然后继续不调用 Bash,凭印象编造结果。
根本原因
LLM 对「能力描述」的默认反应是确认能力的存在,不是触发能力的使用。「能做」和「该做」在文本中是两个完全不同的语义场。
设计要点
| 能力陈述(弱)❌ | 主动指令(强)✅ |
|---|---|
| 可以使用 X 工具 | 必须调用 X 工具 |
| 建议查阅 Y 文档 | 加载 Y 文档,逐条对照 |
| 注意避免 Z | 出现 Z 时立即替换 |
| 你有 W 权限 | 现在执行 W,不要只描述 |
写 Skill 的时候默念一句话:
「我希望 AI 现在做什么动作?」——不是希望它具备什么能力,而是希望它产生什么具体动作。然后用动词写出来。
原则四:长上下文会稀释规则,必须设置中途检查点
失败模式
你在 Skill 开头加载了 5 条约束:
- 生成前 500 字时全部遵守 ✓
- 生成到 2000 字时违反 1 条 ⚠️
- 生成到 4000 字时违反 3 条 ✗
这不是 LLM 在偷懒,而是注意力机制的物理特性:随着新 token 涌入,早期约束在工作记忆中的相对权重持续衰减。
设计要点
对于长生成任务,单点约束(开头加载)不够,需要前置 + 中途 + 后置三层防御:
[前置:加载约束] → 生成中 → [中途自检 ×N] → 生成中 → [后置:终点强制门]
| 层级 | 触发方式 | 作用 |
|---|---|---|
| 前置 | 开篇加载 | 建立约束意识 |
| 中途 | 每 N 字 / 每 N 步 | 防止规则漂移 |
| 后置 | 终点强制清单 | 最后一道闸 |
关键:中途检查点用具体数字触发(“每 1000 字暂停自检”),而不是"适时检查"——后者等于没检查。
原则五:显式区分「内部思考」与「输出内容」
失败模式
LLM 输出里出现:
他走向门口——等等,应该说他冲向门口,因为情境更紧张。他冲向门口。
LLM 把内部修订过程当作正常输出写了出来。
根本原因
LLM 默认不区分「这是给用户看的」和「这是我的思考过程」。在它的视角里,所有 token 都是同等的输出。如果你不显式告诉它"思考停留在脑内",它会把推理痕迹直接写进交付物。
设计要点
凡是涉及生成产物(代码、文章、报告、JSON)的 Skill,都应该有一条明确的边界声明:
内部纠错、自我修订、规则核对——这些过程都发生在你的工作记忆中。
最终输出只包含修订后的终稿,不包含任何自我纠正痕迹。
禁止出现:"等等"、"不对"、"修改为"、"(删除)"、"重新写"等修订语。
同样适用于:
- 代码生成:不要把「我先尝试 A,再改成 B」写成注释
- JSON 输出:不要包含
// 这里我犹豫了一下 - 报告写作:不要出现「上面那段说得不对,应该是……」
原则六:Skill 中的引用文档要强制加载,而不是「可选参考」
失败模式
你写:
写复杂逻辑时可以参考
complex-patterns.md。
LLM 永远不会主动去读这个文件。除非你说"必须读",并且给出判断条件 + 加载动作 + 验收标准。
设计要点
引用文档分三种,处理方式不同:
| 类型 | 特征 | 处理方式 |
|---|---|---|
| 常用参考 | 高频、轻量 | 直接 inline 在 Skill 主体里 |
| 关键专题 | 触发条件明确 | 强制加载 + 检查清单 |
| 长尾参考 | 偶尔需要 | 列入索引,按需加载 |
对于「关键专题」,标准模板是:
**当 [明确触发条件] 时,必须执行:**
1. 加载 [文档名]
2. 对照其中的 [清单名] 逐条勾选
3. 全部通过后再继续,否则返回修改
三个要素缺一不可:触发条件、加载动作、验收门。少一个就会失效。
原则七:依赖外部世界状态时,强制实时验证
失败模式
用户问:「这个库的最新版本支持哪些功能?」LLM 凭训练数据回答——而训练截止已经是一年前。
或者:「市场上有没有类似产品?」LLM 自信地说"没有"——实际有 50 个。
根本原因
LLM 的世界知识有截止日期,且对自己的不确定性识别能力很差。它会用一个过时但流畅的答案掩盖一个真实但模糊的状态。
设计要点
凡是涉及以下类型的判断,Skill 中必须强制嵌入实时查询步骤:
- 版本/API 现状:库的最新版本、API 是否变更、deprecation 状态
- 市场/竞品状态:有没有类似产品、谁在做什么
- 政策/规范状态:法规、标准、平台规则
- 数据/价格状态:实时报价、库存、可用性
标准模板:
当任务涉及 [外部世界状态] 时:
1. 必须调用 [搜索/API 工具] 验证当前情况
2. 不得仅依据训练数据作答
3. 若工具不可用,明确告知用户「无法验证,以下基于历史数据」
最后一条很关键——让 LLM 学会暴露自己的不确定性,而不是用流畅性掩盖它。
设计模式总结
将七条原则反向归纳,得到五个可复用的设计模式:
模式 A:约束三件套
规则声明 + 检查清单条目 + 阻断条件
任何硬性规则必须三件齐全。
模式 B:装载时解析
工程细节 → 装载阶段消化 → LLM 看到的是终态
不让 LLM 处理它本不该处理的占位符、模板、配置。
模式 C:前中后三层防御
前置加载 → 中途检查点 → 后置强制门
对抗长生成中的规则漂移。
模式 D:动作语态
"必须做 X" > "应该做 X" > "可以做 X" > "你有做 X 的能力"
强度从左到右递减。生产 Skill 用左两个,避免右两个。
模式 E:思考与输出分离
所有自我修订发生在工作记忆中 → 最终输出只包含终稿
显式声明,不能默认 LLM 知道。
设计自检清单
每写完一份 Skill,过一遍:
- 每条硬性规则是否在某个 checklist 中有对应验证项?
- 是否有任何未解析的占位符/模板变量出现在 prompt 中?
- 关键动作的措辞是「必须」还是「可以」?
- 长生成任务是否有中途检查点(不只是终点检查)?
- 是否显式声明了「内部思考不写入输出」?
- 关键参考文档是否有「触发条件 + 加载动作 + 验收门」三要素?
- 涉及外部世界状态的判断是否强制实时验证?
- 检查清单的每一项是否都包含具体可观测的产物(数字、列表、勾选状态)?
结语:从「写文档」到「造约束系统」
很多人写 Skill 的方式像写 README——把功能、用法、注意事项都列清楚,期望 AI 看完就照做。但 LLM 不是工程师,它不会因为某条规则写得清楚就更愿意遵守。
真正有效的 Skill 不依赖 LLM 的善意,而是通过结构性的约束设计让违规成本高于遵守成本:
| 朴素思维 | 系统思维 |
|---|---|
| 告诉它怎么做 | 让它不得不那样做 |
| 罗列规则 | 设计检查门 |
| 期待理解 | 强制验证 |
| 写一遍生效 | 多层冗余 |
| 信任 LLM | 不信任 LLM,设计兜底 |
Skill Engineering 的本质,是用有限的、确定性的文档结构,为无限的、概率性的 LLM 输出搭建一套护栏。
护栏不在文字的优美,而在拐点的设置。
更多推荐



所有评论(0)