前阵子我在本地的Claude测试工程里放了一个SKILL.md然后问模型会不会执行某个技能它倒是能准确说出技能的名字但真正让它跑起来的时候却要我再追问一轮才肯进入状态。一开始我以为是自己写错了格式后来翻完官方文档又做了几组对照实验才反应过来这根本不是“加载失败”而是Claude Skills这套机制压根就是按需唤醒的。它的核心并不是把技能塞进每一轮对话而是通过SKILL.md、available_skills、渐进式加载这套组合在上下文、成本和可发现性之间找到平衡点。这篇文章我想把Claude Skills的完整工作机制拆开聊一遍重点放在三层上下文架构和“最少惊讶原则”上。如果你正准备给Claude写技能、或者想搞清楚它和普通System Prompt、Function Calling有什么本质区别这篇文章应该能帮你省不少排查时间。1. Skills的加载策略为什么不能把所有能力一次性灌进上下文很多第一次接触Claude Skills的人都会惯性思维技能越多模型就越聪明把全部技能定义都塞进System Prompt里不就好了实际跑一轮你就知道这种思路在Claude这里很难成立。倒不是模型不能读长文本而是链路开销和干扰问题比想象中严重。每个技能如果都包含完整的指令、触发词、示例输入、参数说明、输出格式十几个技能加起来就是上万token。这些token会挤占真正的业务对话窗口模型在处理普通问题时还要时刻分心去“确认”当前场景是否匹配哪个技能那种微妙的响应变慢和答非所问就是这么来的。Claude Skills的做法更像我们平时写代码时的“懒加载”。默认情况下系统只向模型暴露一份轻量的技能清单让模型知道有哪些能力、每个能力叫什么、是干什么的只有当对话内容命中某个技能的触发条件时才把那份技能对应的SKILL.md正文加载进来。也就是说技能的“存在”和技能的“细节”被拆成了两层中间隔着一个按需读取的动作。我自己的理解是这套设计把模型从“背所有技能书”改成“查目录再翻书”。目录永远摆在手边开销很小书只有确定需要时才翻开成本可控。这个差异看起来简单但直接影响了Claude Skills在使用体感上和传统Prompt模板的本质区别。为了控制这个“查目录再翻书”的成本Claude Skills背后其实藏着一条很朴素的规则——技能描述必须足够紧凑紧凑到模型能在几百token内决定是否要加载它。这一点后面聊available_skills的时候还会反复出现。2. SKILL.md到底该写什么一份能跑起来的技能定义文件先说结论SKILL.md不是文档它是一个触发元数据 执行指令的混合体。它不需要面面俱到地解释背景但必须在被加载后让模型“拿起来就能干活”。我之前踩过一个坑把SKILL.md写成了长篇教程里面有背景介绍、设计哲学、常见问解答洋洋洒洒一千多行。结果就是模型虽然加载了文件但仍然抓不住重点回答质量反而不如不加载。后来我总结出一套最小结构核心就三个区域技能标识、触发条件、执行协议。以下是一个我目前用着很顺手的模板可以直接抄--- name: generate_monthly_report description: 当用户要求生成月度运营报表、汇总当月数据或分析月度趋势时使用。 version: 1.0.0 --- ## 触发条件 - 用户明确提到月度报表、月报、本月数据汇总等短语 - 用户请求对某一时间段内的数据做环比或同比分析 ## 执行流程 1. 确认数据源范围和时间区间 2. 调用本地CSV读取脚本获取原始数据 3. 计算关键指标总营收、订单量、客单价、退款率 4. 生成Markdown格式的报表并附上环比变化说明 ## 注意事项 - 如果数据缺失超过20%先向用户说明不要直接用残缺数据生成结论 - 月份统一使用自然月不要使用滚动30天注意看frontmatter里的description字段这是整个文件里最重要的部分。因为模型在做技能匹配时通常只看这个描述不会把整个SKILL.md都读进来判断“要不要用”。描述写得越精确、越简短匹配准确度越高。反过来如果你在描述里堆形容词比如“这是一个功能强大的报表工具”模型很容易在用户聊到“工具”两个字时误触发后面就是一连串的无效加载。我还建议在SKILL.md正文里只写“怎么执行”不写“为什么这么执行”。模型做决策需要的理由可以在触发条件和注意事项里隐含但不要在正文里展开长篇论证。执行指令越像一份操作SOP模型的方向感越强。另外一个容易被忽略的细节是SKILL.md的文件名本身也会被模型读取。所以文件名要能直观对应能力比如generate_monthly_report.md不要用skill001.md这种编号命名。模型在匹配技能时文件名和description是并列线索如果你两个线索都指向同一个动作命中率会明显提升。3. available_skills模型是怎么知道“自己会什么”的如果说SKILL.md是单本技能书那么available_skills就是摆在桌面上的“技能总目录”。它通常是一份结构化清单列出当前环境里所有可用技能的名称、简要描述和入口。Claude在对话过程中会频繁参考这份清单判断用户当前的需求是否能匹配某个能力。对我这种喜欢在多个项目里复用同一套技能的开发者来说available_skills这个概念特别关键。它的存在意味着你不需要手工把每个技能塞进System Prompt只需要保证技能文件放在正确的目录里系统会自动生成这份清单。这个过程有点像注册中心技能自己向系统“报个名”模型在需要时按名查找。这份清单在上下文中的形态一般很紧凑它不会展示技能的完整执行流程只包含以下信息字段作用示例技能名称模型调用时的唯一标识生成月度报表一句话描述模型进行匹配判断的核心依据根据CSV数据生成月度运营报表加载入口触发后读取的SKILL.md路径./skills/generate_monthly_report/skill.md使用限制可选字段用于防止误用仅当月度数据存在时使用这张表的每一行都承担着“可发现性”这个职能。我查过不少讨论帖很多人把available_skills直接理解成“技能列表页”这没错但更准确地讲它是模型做“要不要深入加载”这个决策时的第一依据。Claude先看available_skills觉得需要细看才会去读取对应的SKILL.md。在使用中有个常见的挫败场景你明明放好了技能文件问Claude它会什么它也能说出来但当你说“现在帮我处理一下”时它却开始泛泛而谈完全没有调用技能的样子。这种情况大概率不是技能没生效而是description写得让模型觉得“当前场景没有强匹配”它就没有走加载分支。反过来只要你描述里明确了触发词和行为动词日常对话中往往一句话就能把技能拉起来。我自己习惯的做法是每次增加新技能后先不急着用真实业务数据测试而是直接在对话里问“你能处理XX吗”看模型能不能从available_skills里准确定位到目标技能。如果连定位都做不到那参数配置再正确也没用——模型根本不知道有这个东西可以加载。顺着这个思路往下走就是标题里提到的“最少惊讶原则”。它和available_skills的关系在于模型不应该在没有任何铺垫的情况下突然从一个普通对话模式切换进技能执行模式。每一次能力调用都应该让用户感觉到“这件事本来就在预期之内”。换句话说技能给模型赋予的不只是扩展能力还是一种“响应行为”的约定。4. 渐进式加载的三层上下文架构从总览到明细的逐级穿透聊到这里就能把Claude Skills背后真正的骨架拖出来了——三层上下文架构。我之前在网上看到过不少零散分析但真正自己动手做对照测试之后才理清了每一层到底在上下文中扮演什么角色。4.1 第一层全局总览层这一层是系统最快能拿到、也最常驻上下文的部分。它包含所有可用技能的摘要信息对应我们刚才说的available_skills。在这一层里模型获得的只是“能力地图”有哪些技能、每个技能一句话说明、入口在哪。这一层的token开销最小几百到一千token左右就能覆盖几十个技能。正因为它轻它才会在每一轮对话里都被加载。如果没有这一层模型就得把所有技能完整读一遍才能知道“自己会什么”那跟全量加载没有区别。4.2 第二层技能索引层当模型根据总览层判断“当前用户需求可能匹配技能A”时它会打开该技能对应的入口索引。这个索引是SKILL.md的frontmatter部分以及正文中的“触发条件”和“执行流程”的摘要。索引层的目的是做二次确认。总览层看到的是一句话描述索引层则是让模型检查更详细的触发条件和关键细节决定“我是否真的要用这个技能”。这一层出现的时候技能的执行指令还没有完整进入上下文模型只是在“翻书页”的阶段。索引层的加载时机一般是对话中间某个转折点。比如用户先说“帮我看看这个月的销售数字”模型拿不到汇总结果此时它会翻技能索引确认“生成月度报表”是否适用。这一过程用户无感知但上下文的组成已经悄悄发生了变化。4.3 第三层执行明细层一旦模型确认技能可用它才会读取SKILL.md的完整内容包含具体的参数、脚本路径、步骤顺序、异常处理规则。此时技能才真正从“可发现”变成“可执行”。第三层是三层里信息量最大的一层也是唯一会把完整文件读进来的层。它不会常驻只有在命中触发条件时才瞬时进入上下文。执行完任务后即便上下文里还残留着那部分内容系统也不会把它当作战术性数据重复加载。我做了个简单对照很直观层级加载时机上下文内容费用特征全局总览层每轮对话常驻技能名称、一句话描述、入口极低可忽略技能索引层当总览层匹配候选目标后触发条件、执行流程摘要中低按需出现执行明细层索引层确认命中后SKILL.md完整指令与步骤较高瞬时占用这个设计的精妙点在于它在不牺牲“技能发现”的前提下大幅压缩了常驻token。模型既能意识到所有技能的存在又不用把技能的细节都在每一轮里背一遍。它对那种“技能很多但单次会话只会用到一两个”的场景特别友好。我来打个比方这就像一家大型超市门外只有一块写着“生鲜区在二楼、家电区在三楼”的指示牌。你走进超市只有走到生鲜区门口才需要看到那块写着“今日鲜鱼到货”的小黑板只有走到水产冰柜前才需要打开玻璃柜翻看价签。指示牌、小黑板、价签分别对应三层上下文没有人会把整个超市的商品目录贴在门上。明白了这三层结构再看“渐进式加载”这个词就清楚多了——它不是把技能从硬盘“流式读入”内存那种工程意义上的流式而是一种逻辑上的“慢启动”让模型在每一层都保留一次“反悔”的机会。如果第一层判断不匹配后面两层永远不会被加载。这个决策链条同时也在帮模型节约“思考预算”每轮对话都保持轻装上阵明显比拖着几个重型技能到处走要灵活。5. 最少惊讶原则为什么技能触发得太“突然”不是好事以及实践中的边界选择“最少惊讶原则”这个词最早来自设计领域意思是任何操作的结果都不应该让用户觉得意外。Claude Skills把这条原则用在了触发体验上当一个技能被调用时用户应该能预感到“模型接下来可能会使用某个专用能力”而不是在回复中段突然跳进一个完全不同的执行模式。我自己在实测中体会最深的表现是如果技能描述和用户当前话题衔接得好技能触发后模型的回答语气和结构不会出现明显的“断裂感”。它只是变得更专注更像一个专家开始组织自己的表达而不是从通用聊天突然切到一个机械化的工具流程。想做到这点除了上文中说的“描述要写清楚触发条件”之外还有三个实操层面可以修正的方向。第一技能名称要动词化让用户和模型都能字面理解“它要干什么”。一个叫check_disk_space的技能比叫storage_manager的技能清晰得多。模型在匹配时看到的语义路径更直接触发行为的可预测性就更高。第二SKILL.md的前置条件和边界限制要明确写入描述。不能只写“这个技能能做什么”还要写“在什么情况下不要用”。比如我处理数据时有个技能是专门做异常值清洗的我在描述里加了一句“仅当数据分布呈现明显离群点时才使用”这之后误触发的频率就显著下降了。第三尽量让输出格式和执行流程风格保持统一。用户一旦用过一次技能他会形成预期。如果第二次调用同一个技能时输出风格变了哪怕结果是对的体验也会打折扣。所以我通常会在SKILL.md里固定输出模板的骨架只留业务字段按需填充。还有一层边界问题经常被人忽略Skills、Tools和MCP到底怎么分我自己跑了一段时间后总结出很粗但好用的判断标准。如果你的动作是“需要模型进行推理、判断、组织内容”比如生成报表、写分析摘要、规划任务拆解那更适合做成Skill。因为Skills的描述和SKILL.md能让模型像套公式一样进入角色。如果你的动作是“调用外部系统执行一个明确且无歧义的函数”比如发送请求、查询数据库、操作系统级动作那更适合做成Tool。如果你的动作是“连接一个完整的外部服务暴露一组操作”那通常就是MCP的领域。不过实际项目里三者边界常有重叠。我现在的策略是以“上下文敏感度”为依据模型需要判断要不要用、怎么用就归Skills模型只需要闭眼调用、返回结果就行就归Tools。这个标准帮我在好几个项目里都做了比较干净的分层。最后分享一个能直接验证三层架构的本地实验。准备一个干净的项目目录结构大致是这样. └── skills/ └── generate_monthly_report/ └── SKILL.md然后准备好一份CSV数据和一个简单的实现脚本。这个事例我建议手动做不用任何Agent框架纯靠Claude目录识别的机制。你在对话里先问“你会做什么”它会列出技能名称这是第一层再问“generate_monthly_report怎么触发”它能说出触发条件这是第二层最后把数据文件路径丢给它让它真正生成报表这是第三层。三层跑通之后你可以再做一组反向测试把SKILL.md里description写得含糊一些比如“一个用于数据处理的助手”再重复上面的问题序列。你会发现模型在第一层就能报出技能名称但到第三层时往往不会主动加载完整指令而是尝试用通用能力硬解。这个现象就是“索引层判断失败”最直观的体现。我实际用下来的体会是Claude Skills处理“如何选择技能”这件事靠的不是穷举所有能力细节而是让模型在关键决策点用最少的信息做判断。这也解释了为什么那些被公认为“好用”的技能几乎都是description精炼到不能再精炼的。给技能写描述时你真正需要回答的问题是这句话能不能让模型在20个候选里一眼选中你。能这个技能就成功了一半剩下的执行细节反而都是流水账。