ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

claude code 消息系统 Skill 调用(五):SKILL.md 渐进式加载的配置与验证

claude code 消息系统 Skill 调用(五):SKILL.md 渐进式加载的配置与验证 1. 从一次客服回复请求看 Skill 渐进式加载到底解决了什么问题先还原一个真实场景。用户丢过来一句话“帮我查订单 ORD20260529001 的物流到哪了然后按公司客服话术规范整理一段回复给客户。”这句话里其实藏着两类完全不同的能力需求一类是数据查询需要调用get_order_logistics这样的业务工具拿到事实另一类是话术规范需要一套公司内部的客服回复标准包括语气、模板、升级规则。如果你把所有规范一次性塞进 system prompt会发生什么客服话术规范里通常包含语气指南、模板库、升级规则、格式化脚本加起来轻松上万 token。而用户这次只是问物流投诉模板和赔偿升级规则根本用不上。全量注入的代价是每一轮对话都在为用不到的内容付费而且上下文越长模型注意力越容易被无关信息稀释。渐进式加载Progressive Disclosure就是冲着这个矛盾来的。它把 Skill 拆成三层L0 元数据层只放 name 和 description会话启动时就位约 50 到 150 token 一个 SkillL1 指令层是 SKILL.md 全文只有 Skill 被真正调用时才加载约 500 到 2000 tokenL2 资源层是 references 和 scripts由 SKILL.md 指引按需读取可能上万 token 但只在相关场景才进 context。这套机制适合谁适合所有在 Claude Code 里维护多个 Skill、又担心上下文爆炸的开发者。尤其是那种一个 Skill 目录下挂着十几个参考文件的团队渐进式加载能让你在保持规范完整性的同时把单次请求的 token 消耗压到原来的三成左右。下面我会从 SKILL.md 的编写规范讲起给出可复制的配置再演示怎么通过日志验证加载时机最后把常见的加载失败问题一个个排掉。2. TaoToken 前置准备把 Claude Code 的消息链路接起来在动手写 SKILL.md 之前得先保证 Claude Code 的消息系统能正常跑通。我这边用的是 TaoToken 作为模型接入层它的 API 地址是https://taotoken.net/api官网在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。你需要在控制台创建一个 API Key然后把它配置到 Claude Code 的环境变量里。具体操作路径是这样的先打开控制台页面https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite在 API Keys 菜单里新建一个 Key复制出来。然后设置环境变量Linux 或 macOS 下可以写进~/.zshrc或~/.bashrcexport ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的KeyWindows 用户可以在系统环境变量里加这两条或者用 PowerShell 临时设置$env:ANTHROPIC_BASE_URLhttps://taotoken.net/api $env:ANTHROPIC_API_KEYsk-你的Key这里有个容易踩的坑Base URL 末尾不要带/v1Claude Code 的 SDK 会自己拼接路径。如果你写成https://taotoken.net/api/v1请求会变成/api/v1/v1/messages直接 404。我试过在配置文件里多写一层排查了半天才发现是路径重复。配置完之后你可以先用一个最小请求验证链路是否通。Claude Code 本身有交互界面但为了看清楚消息体建议先用 curl 打一发curl https://taotoken.net/api/v1/messages \ -H x-api-key: $ANTHROPIC_API_KEY \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-opus-4-7, max_tokens: 64, messages: [{role: user, content: 回复 ok}] }如果返回里能看到content数组和正常的文本说明 Key 和 Base URL 都没问题。这一步很关键因为后面 Skill 加载失败时你得先排除是模型链路本身不通还是 Skill 配置有问题。模型对话的调试入口在https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite你可以在那里先确认目标模型 ID 是否可用。另外如果你打算长期跑编码类或 Agent 类任务Coding Plan 的入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite它更适合高频调用场景。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite遇到参数问题可以对照查。3. SKILL.md 编写规范与可复制配置三层加载的落地写法现在进入正题。Skill 在磁盘上的目录结构决定了渐进式加载能不能成立。以客服回复 Skill 为例标准结构是这样的~/.claude/skills/customer-service-reply/ ├── SKILL.md ├── references/ │ ├── tone-guide.md │ ├── templates.md │ └── escalation-rules.md └── scripts/ └── format_logistics.pySKILL.md 是 L1 的核心它的写法直接决定模型会不会在正确时机去读 L2 资源。关键原则是SKILL.md 本身不承载全部知识而是当“目录式指令”告诉模型“什么场景该读哪个文件”。下面是一份可以直接复制的 SKILL.md--- name: customer-service-reply description: 公司客服回复规范。提供标准话术、语气指南、升级流程和场景模板。当用户需要整理对外回复、处理投诉、撰写道歉信、向客户解释物流或退款问题时使用。 --- # 客服回复规范 ## 工作流程 1. 先用 get_order_logistics 等业务工具拿到事实数据。 2. 根据回复场景从 references/ 中读取对应资源 - 物流类问题 → references/tone-guide.md - 投诉或道歉 → references/templates.md 的 apology 段 - 需要升级 → references/escalation-rules.md 3. 调用 scripts/format_logistics.py 把原始物流数据转成话术片段。 4. 拼成最终回复确保 - 首句称呼客户 - 中段陈述事实 - 末句给下一步预期 ## 关键约束 - 禁止承诺具体送达时间只能说“预计”。 - 涉及赔偿必须先调 escalation-rules。注意 frontmatter 里的description是 L0 层唯一进入 system prompt 的内容。它写得好不好直接决定模型在用户提问时能不能判断出该调用这个 Skill。description 要包含触发场景关键词比如“物流”“投诉”“道歉”“退款”而不是泛泛写“客服相关”。L0 层在会话启动时由 harness 扫描 Skill 目录生成拼进 system prompt 的形态大致是这样{ type: text, text: system-reminder\nThe following skills are available for use with the Skill tool:\n\n- customer-service-reply: 公司客服回复规范。提供标准话术、语气指南、升级流程和场景模板。当用户需要整理对外回复、处理投诉、撰写道歉信、向客户解释物流或退款问题时使用。\n- order-refund: 订单退款流程...\n/system-reminder, cache_control: { type: ephemeral } }这里cache_control是重点。L0 的内容在整个会话里不变所以它被 prompt cache 命中后成本几乎摊到零。而 SKILL.md 进入 context 是在 messages 里作为 tool_result 回灌的不会破坏 system 段的缓存只是后续 messages 重新计费。L2 的文件路径必须在 SKILL.md 里明示不能让模型去猜。上面工作流程第 2 步里写死了references/tone-guide.md这样的相对路径模型读到后就会用 Read 工具去取。如果你只写“读取相关参考文件”模型可能猜错路径白白浪费一轮请求。scripts 目录下的脚本同理SKILL.md 里要写清楚调用时机。比如format_logistics.py的职责是把物流 JSON 转成话术片段那就在流程第 3 步点名。脚本本身不自动执行模型会通过 Bash 或相应工具去跑。如果你用的是 Cline MCP 或 Codex 这类工具配置里同样要写全三件套Base URL、Key、Model ID。以 Codex 的auth.json为例{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: claude-opus-4-7 }Cline 的 MCP 配置里也是类似结构Base URL 填https://taotoken.net/apiModel ID 填你在模型对话页确认过的可用 ID。三件套缺一个请求就会在鉴权或路由阶段失败。4. 验证请求与成功结果用日志确认 Skill 按需加载配置写完之后最要紧的是验证加载时机对不对。渐进式加载的价值全在“按需”两个字上如果模型每次都把 references 全读一遍那这套机制就白设计了。验证方法分两步。第一步看消息序列第二步看 token 消耗。先构造一个物流查询请求观察 messages 数组的增长过程。会话启动时messages 是空的system 里只有 L0 的 Skill 描述列表。用户提问后第一轮 assistant 响应应该是决定调用 Skill{ role: assistant, stop_reason: tool_use, content: [ { type: text, text: 好的我先加载公司客服回复规范再查物流。 }, { type: tool_use, id: toolu_skill_001, name: Skill, input: { skill: customer-service-reply } } ] }harness 收到这个 tool_use 后去读~/.claude/skills/customer-service-reply/SKILL.md把全文作为 tool_result 回灌。此时 L1 进入 context但 references 下的文件一个都没加载。第二轮模型读了 SKILL.md识别出这是物流类问题按工作流程第 1 步先调业务工具{ type: tool_use, id: toolu_logistics_002, name: get_order_logistics, input: { order_id: ORD20260529001, include_history: true } }物流数据回灌后第三轮模型按 SKILL.md 第 2 步指引只读references/tone-guide.md不碰 templates 和 escalation{ type: tool_use, id: toolu_read_003, name: Read, input: { file_path: /Users/you/.claude/skills/customer-service-reply/references/tone-guide.md } }这就是渐进式加载的精髓。三个 references 文件加起来约 10000 token但本轮只为 tone-guide 的 3000 token 付费。templates 的 5000 token 和 escalation 的 2000 token 根本没进 context。最终 messages 数组的长度是 9 条序列如下[0] user.text → 帮我查订单... [1] assistant.text tool_use(Skill: customer-service-reply) [2] user.tool_result → SKILL.md 全文L1 加载 [3] assistant.text tool_use(get_order_logistics) [4] user.tool_result → 物流 JSON [5] assistant.text tool_use(Read: tone-guide.md) [6] user.tool_result → tone-guide.md 内容L2 按需加载 [7] assistant.text → 最终客服话术回复对比一下 token 账。渐进式实际消耗约 3800 token包括 SKILL.md 的 800 和 tone-guide 的 3000。如果全量加载所有 references 加 scripts 约 12300 token。节省约 70%而且这个节省是每一轮都成立的。怎么在日志里确认这些Claude Code 的调试输出里会打印每次请求的 messages 长度和 tool_use 名称。你可以 grep 日志里的tool_use字段看有没有出现Read且file_path指向 references 目录。如果一次物流查询里出现了templates.md或escalation-rules.md说明 SKILL.md 的指引写得太宽泛模型误判了场景。还有一个观察点L0 的 system 段在整个会话里应该保持不变。如果你发现每轮请求的 system 内容都在变说明 Skill 的 description 被动态修改了prompt cache 会失效成本会上去。正常情况下system 段只在会话启动时生成一次。5. 本篇常见错误排查401、local proxy failed 与加载失败配置和验证都跑通之后实际使用中还是会遇到各种报错。下面按真实错误信息逐个排。401 Unauthorized。这个最常见通常是 API Key 没设对或者没生效。先确认环境变量里ANTHROPIC_API_KEY的值是不是完整的sk-开头字符串有没有多余空格。然后确认ANTHROPIC_BASE_URL是不是https://taotoken.net/api末尾不要带斜杠。如果你在多个终端窗口操作注意环境变量只在当前 shell 生效新开的窗口要重新 source 配置文件。还有一种情况是 Key 被禁用或额度耗尽去控制台的 API Keys 页面确认状态。local proxy failed。这个报错说明 Claude Code 尝试走本地代理但连不上。检查你的系统代理设置或者环境变量里有没有HTTP_PROXY、HTTPS_PROXY这类配置指向了一个不存在的端口。如果你之前配过代理工具把相关环境变量清掉再试。另外确认ANTHROPIC_BASE_URL没有被代理规则拦截直接访问https://taotoken.net/api应该是通的。reading choices 报错。这个通常出现在响应解析阶段说明返回的 JSON 结构不符合预期。可能是 Base URL 写成了 OpenAI 兼容格式的路径但 Claude Code 期望的是 Anthropic 原生格式。确认你用的是https://taotoken.net/api/v1/messages这个端点请求头里带anthropic-version: 2023-06-01。如果混用了 OpenAI 的chat/completions路径就会解析失败。OAuth 相关报错。Claude Code 某些版本会尝试 OAuth 流程如果你用的是 API Key 模式需要在配置里明确禁用 OAuth。检查~/.claude/config.json或环境变量里有没有CLAUDE_CODE_USE_OAUTH之类的开关把它关掉。OAuth 和 API Key 是两条不同的鉴权路径混用会互相干扰。Skill 加载失败但模型链路正常。这种情况表现为模型决定调用 Skill但 tool_result 返回错误。先检查 SKILL.md 的 frontmatter 格式name和description必须存在且---分隔符完整。然后确认文件路径大小写Linux 下SKILL.md和skill.md是两个文件。如果 SKILL.md 里引用的 references 路径写错了模型 Read 时会报文件不存在这时候回去核对工作流程里的相对路径。Skill 被调用但 references 没加载。说明 SKILL.md 的指引不够明确。模型读了 SKILL.md 但没触发 Read可能是工作流程里只写了“读取相关资源”而没有具体路径。把路径写死比如references/tone-guide.md模型才会去读。token 消耗异常高。如果一次简单查询消耗了上万 token检查是不是所有 references 都被加载了。用日志确认 tool_use 里 Read 的次数正常物流场景应该只有一次 Read。如果出现多次说明 SKILL.md 的流程设计让模型把不相关的文件也读了需要收窄场景判断条件。排障时如果拿不准是链路问题还是配置问题可以先去接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite对照参数再去 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite确认 Key 状态。模型对话页https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite可以帮你确认 Model ID 是否拼写正确。6. 把渐进式加载用顺手的几个实操建议SKILL.md 的 description 值得反复打磨。它是 L0 层唯一进入 system 的内容也是模型判断“要不要调用这个 Skill”的唯一依据。写 description 的时候把用户可能说的原话关键词塞进去比如“物流”“退款”“投诉”“道歉信”而不是写“客服相关”。我见过太多 Skill 因为 description 太泛模型根本想不起来调它。references 的拆分粒度也有讲究。一个文件别超过 5000 token否则单次加载的成本就上去了。按场景拆物流一个、投诉一个、升级一个让 SKILL.md 里的分支判断能精确命中。scripts 尽量保持无状态输入输出都是纯数据这样模型调用时不用额外维护上下文。验证加载时机这件事建议养成看日志的习惯。每次新增或修改 Skill 后跑一个典型请求确认 messages 序列里 Read 的次数和路径符合预期。如果发现模型读了不该读的文件回去改 SKILL.md 的场景判断条件而不是改 references 的内容。最后prompt cache 的命中依赖 system 段稳定。L0 的 Skill 列表在会话启动后就不要动了中途增删 Skill 会导致缓存失效。如果你需要动态调整 Skill建议新开一个会话而不是在同一个会话里改配置。这套机制跑顺之后你会发现维护十几个 Skill 也不会把上下文撑爆每个 Skill 只在真正被需要时才付出 token 成本。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进