在上一篇里,我讲过把存量系统交给 Agent 时,API、Skill、MCP 三层各是什么角色:API 是系统的能力面,MCP 把能力面标准化给 Agent 调用,而 Skill 承载的是「怎么用」——人脑里那套使用方法。
这一篇钻进 Skill 这一层,讲一个我们踩了很久才想明白的问题:
Skill 的默认形态是一份快照,而快照会腐烂。
我们的解法是把 Skill 拆成三部分:一个只有 51 行的「薄壳启动包」、一个服务端能力目录、一份按会话动态下发的领域协议。业务流程一行都不写在 Skill 里。

Skill 的默认形态是快照
主流的 Agent Skill(Claude Code 的 SKILL.md、各家 Agent 平台的 Skill / Agent File)大概长这样:
---
name: daily-report
description: 当用户需要记录工作、生成日报时使用……
---
# 日报助手
## 流程
1. 先调用 xxx 工具查询今天的工作素材;
2. 按「背景-动作-结果」结构整理成草稿;
3. 调用 xxx 工具提交,状态为 PENDING 时轮询结果;
4. ……
## 注意事项
- 素材分「工作」和「计划」两类,混合内容要拆分;
- 删除素材前必须向用户确认;
- ……
触发词、流程步骤、工具用法、注意事项,全部打包在一个文件里,安装时落到用户机器上。这个形态对个人工具没问题——你自己写的 prompt,烂了你自己知道。
但当你开始做一个面向一堆用户的 Agent 平台,Skill 由平台维护、装在用户的 Agent 宿主(Claude Code、Codex、公司内部的 Agent 客户端)里运行时,快照形态的问题会一个一个冒出来。
快照腐烂的三种方式
1. 改一次流程,就要所有用户重装一次
日报的生成规则变了、工具的参数 schema 变了、加了一个新的确认步骤——对服务端来说就是改几行代码的事。但流程写在 Skill 里意味着这些知识不在服务端,而在几百个用户机器上的几百份副本里。
你要么发新版 Skill 让所有用户重装(运维成本高、总有人不更新),要么忍受新逻辑和旧副本并存。快照没有「热更新」这个选项。
2. LLM 会「凭记忆」执行旧流程
更隐蔽的问题是第二份副本:Agent 的记忆。
Agent 宿主普遍有长期记忆机制(MEMORY.md、CLAUDE.md、自动总结的会话记忆)。用户用了一次日报功能,流程很容易被总结进记忆。下次用户再说「帮我记一下今天的工作」,Agent 大概率不再读 Skill 全文,而是直接按记忆里的流程走——
而记忆里的流程是旧的。你上周改了提交步骤,Agent 记忆里还是上周的。
这不是理论风险。我们在实践里反复观察到:只要协议全文在客户端出现过一次,它就会在你看不见的地方被缓存、被摘要、被复用。LLM 没有「这段指令过期了」的概念。
3. 旧副本等于绕过新权限模型
第三种腐烂最致命。假设你后来收紧了权限:某个写操作现在需要用户确认、某个工具从「所有用户可用」改成了「按角色可见」。服务端工具层的校验当然是新的,但旧协议副本里白纸黑字写着「直接调用 xxx 工具提交」——
Agent 会拿着过期的说明书,去敲新版的门。多数时候会被服务端拦下来(这没问题),但更糟的情况是:旧流程里描述的交互方式(不确认、不询问)和新权限模型对用户的承诺不一致。用户以为删除前会弹确认,Agent 按旧协议直接删了。
把这三条放在一起看,结论其实只有一个:
凡是会被 LLM 当作指令来读的东西,都必须有服务端的单一事实源。
客户端可以有副本,但副本必须短命到不值得缓存——这是我们整个设计的第一性原则。
拆成三层
顺着这个原则,我们把原来「一个大 Skill」拆成了三层:
┌─────────────────────────────────────────────┐
│ 薄壳启动包(SKILL.md,51 行) │
│ 只做能力路由:怎么找能力、怎么拉协议、失败怎么办 │
│ —— 不含任何业务流程 │
└───────────────┬─────────────────────────────┘
│ yi_list_capabilities
▼
┌─────────────────────────────────────────────┐
│ 服务端能力目录(Capability Catalog) │
│ 能力元数据 + 按当前身份过滤后的可见能力列表 │
│ 「你是谁」由 MCP 凭据决定,客户端不用声明 │
└───────────────┬─────────────────────────────┘
│ yi_get_capability_protocol
▼
┌─────────────────────────────────────────────┐
│ 领域协议(服务端 Markdown / 结构化渲染) │
│ 工作汇报怎么做、自动化怎么配——完整流程 │
│ 每个会话动态拉取,用完即弃 │
└─────────────────────────────────────────────┘
一次完整的会话流程是:
- 用户说「帮我记一下今天的工作」,宿主按触发词激活薄壳 Skill;
- Skill 指示 Agent 调用
yi_list_capabilities,拿到当前身份有权使用的能力目录; - Agent 按目录条目的
whenToUse选中「工作汇报」能力,调用yi_get_capability_protocol拉取完整协议; - Agent 严格按协议执行,协议不落盘、不进记忆;
- 会话结束,客户端什么都不保留。下次从第 2 步重新开始。
注意这个拆分带来的职责变化:
- 流程内容(第 3 层):改它不需要任何客户端更新,因为客户端根本没有它的副本;
- 能力有哪些、谁能用(第 2 层):完全是服务端推导,权限变了目录自动变;
- 薄壳本身(第 1 层):只剩「会话规则 + 路由方法」,一年改不了几次。
快照依然存在(薄壳也是快照),但我们把变化最快的那部分从快照里抽走了。
薄壳里装了什么
脱敏后的启动包如下(去掉了内部产品信息,比实际的 51 行略短):
---
name: yi
description: >
工作助手。当用户需要记录或总结工作、设置提醒和自动化、管理个人工作信息,
或不确定该使用哪项工作能力时使用。本启动包通过 MCP 能力目录按需加载领域协议。
触发词:工作助手、工作记录、工作日志、日报、周报、月报、工作总结、
记录一下、今天做了什么、后续计划、提醒、定时、自动化、定时任务、Cron……
version: 3.0.1
author: assistant-platform
tools:
- mcp
---
# 小yi(启动包)
你是用户的工作伙伴,是用户工作辅助的唯一入口。本启动包不承担具体业务流程:
它从 MCP 能力目录选择 capability 并动态拉取领域协议,不内置工作日志、自动化等
领域流程副本,也不安装或激活其它 Skill。
## 会话规则
1. 需要日期、时间等基础信息时,直接使用基础 MCP 工具。
2. 调用 `yi_list_capabilities` 获取当前身份实际可用的能力目录;同一会话中
目录未变化时可以复用结果。
3. 从 `whenToUse` 选择最匹配的 capability;调用
`yi_get_capability_protocol({ capabilityId })` 获取完整协议。
4. 获取完整协议后,立即严格按该协议完成任务,不再激活或安装另一个 Skill,
也不凭记忆执行。
5. 不要把任何领域协议写入记忆、本地文件或调用日志;下次需要时重新拉取。
## 能力路由
- 能力 ID、适用场景和协议入口全部以服务端能力目录为准,不在本启动包中硬编码。
- 用户意图仍无法归类时,简要展示能力目录并询问目标,不要擅自执行写操作。
## 权限与降级
- 能力目录只返回当前身份有权使用的能力;目录中不存在的能力不得尝试调用。
- capability 协议或业务工具缺失时,说明权限不足或服务暂不可用;不要绕过 MCP scope。
- 「能加载协议」不代表可以执行所有写操作,仍须遵守对应工具的 scope、确认和审计规则。
## 失败处理
- 能力目录或协议工具不可用时,告知用户「暂时不可达,请稍后再试」,
不要猜测流程或工具。
51 行里没有一行业务流程,全部是「元规则」。值得逐条说的是三条核心会话规则,它们分别堵住了前面三种腐烂。
规则 4:「严格按协议执行,不凭记忆」
这条直接对应腐烂方式 2。LLM 拿到一份完整流程文档后,天然的倾向是「我懂了」,然后按自己的理解(混着预训练知识和上次会话的残留)自由发挥。规则 4 把「懂了」和「照做」分开:协议是唯一依据,理解力只用于执行协议内的判断,不用于脑补协议外的步骤。
规则 5:「协议不写入记忆、本地文件、调用日志」
这条对应腐烂方式 2 和 3 的根因,也是整个设计里我最想强调的一行。
只要允许协议进记忆,前面说的「记忆里的旧流程」就会重新出现——你把副本从 Skill 里删了,它又从记忆里长出来。所以动态下发必须配一条禁令:协议的生命周期等于会话的生命周期。
我们甚至在服务端做了配套:MCP 调用日志对这两个工具显式设置 skipResultData: true,协议全文不进调用日志。否则日志就成了第二个缓存——既是泄密面,也是旧版本的长存地。
规则 2 的后半句:「同一会话中可以复用」
注意我们没有走极端。同一会话内目录不会变(权限变更粒度是分钟级的,会话是分钟级的),重复拉取纯属浪费。这条半句划出了缓存的合法边界:会话内可复用,跨会话必须重新拉。即使目录真的因权限变更而过期,风险也只停在目录层——协议工具在每次调用时都会重新鉴权(下一篇展开),旧目录换不来新协议。工程上的正确答案往往不是「绝不缓存」,而是「给缓存找一个够短的生命周期」。
服务端的镜像:双保险
还有一个现实问题:用户没装 Skill、或装了旧版 Skill 怎么办?
我们的做法是在 MCP 服务端的 instructions 字段里写同样的会话规则。MCP 协议在初始化握手时会把 instructions 交给客户端,等于服务端每次连接都重申一遍「先拉目录、再拉协议、严格照做」。
这样规则存在两份镜像:Skill 里一份(覆盖宿主 Agent 的激活与路由),服务端 instructions 一份(覆盖所有已连接客户端)。旧版 Skill 的用户至少会被服务端 instructions 纠正行为;没装 Skill 的客户端,其 Agent 也能按 instructions 摸到能力目录。
代价是两处规则要同步维护——我们把它记进了平台的工程约定:改会话规则时,SKILL.md 和 instructions 必须一起改,并同步升版本号。
这套设计的代价
诚实起见,也说说不好的地方:
- 每个会话多两次工具调用。拉目录 + 拉协议,一次几 KB。对内部平台这个开销可以忽略,但对延迟极敏感的场景要想清楚。
- 强依赖服务端在线。服务端挂了,Agent 连流程都不知道,只能按失败规则说「暂时不可达」。快照形态至少还能离线跑旧流程——但如前所述,「离线跑旧流程」本身就是我们想消灭的东西。
- 触发词仍是快照。description 里的触发词装在用户机器上,加新触发词依然要用户更新 Skill(我们就发过一次 3.0.1 只为加触发词)。这是接受的残余:触发词的变化频率远低于流程。
小结
把这篇的核心论点压缩成三句话:
- Skill 是快照,快照会在用户机器、Agent 记忆、调用日志三个地方腐烂,其中记忆最难防;
- 解法是把变化最快的「业务流程」从 Skill 里抽走,做成服务端单一事实源 + 按会话下发;
- 动态下发要配套「不缓存」的禁令,否则旧副本会换个地方长回来。
到这里,薄壳这一层讲完了。但还有两个关键问题没回答:能力目录到底返回什么结构、身份过滤怎么做?协议本体为什么选 Markdown?user: 前缀的用户自定义能力又是什么?下一篇讲能力目录与协议下发的接口设计。
本系列下一篇:《能力目录与协议下发:把接口设计和控制流一起交给 Agent》