Claude Code 的 skills 源码解析(上):从 SKILL.md 到 Command 的加载链路与 TaoToken 配置

发布时间:2026/10/12 19:11:27
Claude Code 的 skills 源码解析(上):从 SKILL.md 到 Command 的加载链路与 TaoToken 配置
1. 从一次技能不生效说起Claude Code skills 加载链路到底怎么走如果你在 Claude Code 里写过自定义技能大概率遇到过这种场景.claude/skills/目录下明明放了SKILL.md输入/你的技能名却提示Unknown skill或者技能能被识别但模型完全没按你写的步骤执行。这类问题十有八九不是模型不听话而是技能压根没被加载进 Command 注册表或者加载了但触发条件没命中。Claude Code 的 skills 机制本质上是一套「磁盘 Markdown → 内存 Command 对象 → 注入对话上下文」的编译链路。理解这条链路你才能知道为什么有的技能启动就能用、有的要等文件被打开才激活、有的同名技能被静默覆盖。这篇聚焦加载阶段和斜杠命令触发阶段把SKILL.md的解析、Command 的注册、getPromptForCommand的延迟编译讲清楚同时给出把 endpoint 切到 TaoToken 后的完整验证动作确保你自定义的技能能被正确加载和调用。适合人群已经会用 Claude Code 但想搞懂 skills 内部机制的开发者、需要给团队沉淀可复用能力包的工程同学、以及想排查「技能不生效」这类玄学问题的同学。读完之后你应该能自己画出加载流程图并且能定位 90% 的技能加载失败原因。先说结论性的认知一个 skill 就是一个文件夹核心是SKILL.md它由 YAML frontmatter 加正文组成。frontmatter 决定「什么时候加载、谁能调用、用什么模型」正文决定「模型看到什么指令」。加载阶段只解析 frontmatter 并构建 Command 对象正文被闭包捕获直到你真正调用/skill-name时才编译成 prompt。这个「延迟加载」设计是理解整条链路的关键。2. TaoToken 前置准备让 Claude Code 的请求走对 endpoint在深入源码之前得先把运行环境搭好否则你改了技能也不知道请求发到哪去了。Claude Code 默认请求 Anthropic 官方 endpoint我们要做的是把 base URL 指向 TaoToken这样后续验证技能加载时请求链路是可控、可观测的。TaoToken 在这里扮演的是模型 API 接入层官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。你需要先去控制台创建一个 API Key控制台地址 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建完记得复制保存Key 只显示一次。Claude Code 读取配置的方式主要有两种环境变量和 settings 文件。环境变量方式最直接适合临时验证export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENsk-你的TaoToken密钥 export ANTHROPIC_MODELclaude-sonnet-4-6如果你希望长期生效写进~/.claude/settings.json更稳妥。注意这个文件是 Claude Code 的全局设置不是项目级的{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: sk-你的TaoToken密钥, ANTHROPIC_MODEL: claude-sonnet-4-6 } }这里有个容易踩的坑ANTHROPIC_AUTH_TOKEN和ANTHROPIC_API_KEY是两个不同的变量Claude Code 优先读ANTHROPIC_AUTH_TOKEN。如果你两个都设了可能出现 Key 冲突导致 401。建议只保留ANTHROPIC_AUTH_TOKEN。配置完成后用一条最简单的请求验证连通性。Claude Code 本身没有独立的 ping 命令但你可以直接跑一个非交互式请求claude -p 回复 ok 两个字母即可 --model claude-sonnet-4-6如果返回ok说明 endpoint 和 Key 都通了。如果报 401先检查 Key 是否复制完整、有没有多余空格如果报连接超时检查ANTHROPIC_BASE_URL是不是写成了带路径的https://taotoken.net/api/v1Claude Code 会自动拼接路径多写/v1反而会 404。模型 ID 这块TaoToken 支持 Claude 系列模型常用的有claude-sonnet-4-6、claude-opus-4-6等。你可以在模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 先手动试一下模型是否可用确认没问题再写进配置。这一步别省我见过太多人配置写错却以为是技能加载的问题。3. 可复制配置SKILL.md 目录结构与 frontmatter 全字段现在进入正题。Claude Code 加载技能时会按固定优先级扫描多个目录。理解目录结构是第一步因为放错位置技能就不会被加载。技能目录的搜索范围优先级从高到低是这样的/etc/claude/.claude/skills/ ← 企业策略管理managed ~/.claude/skills/ ← 用户全局技能user 项目/.claude/skills/ ← 项目级技能project沿目录树向上搜索 --add-dir 指定目录/.claude/skills/ ← 额外目录additional 项目/.claude/commands/ ← 旧格式命令legacy向后兼容注意一个细节项目级技能会沿目录树向上搜索。也就是说你在/home/user/project/src/components/下工作时Claude Code 会从当前目录一路往上找.claude/skills/直到项目根。这意味着你可以在子目录放技能但同名技能会被更靠近根目录的覆盖先注册者胜出。一个标准的技能目录长这样.claude/skills/ └── commit-helper/ ├── SKILL.md ← 必需指令文件 ├── scripts/ │ └── format.py ← 可选脚本 └── references/ └── style.md ← 可选参考资料关键约束只支持skill-name/SKILL.md这种两级格式。如果你直接把SKILL.md放在.claude/skills/根目录下不会被识别。加载逻辑里明确跳过了非目录项。接下来是SKILL.md的 frontmatter 全字段。这是加载阶段真正被解析的部分--- name: commit-helper description: 生成符合 Conventional Commits 规范的提交信息 argument-hint: 改动描述 arguments: [scope, message] model: claude-sonnet-4-6 effort: high context: inline allowed-tools: [Bash, Read, Write] user-invocable: true disable-model-invocation: false paths: src/**/*.ts, src/**/*.tsx hooks: PreToolUse: - command: eslint $FILE matcher: Write|Edit shell: bash version: 1.0 --- # Commit Helper 当用户需要生成提交信息时按以下步骤执行 1. 运行 git diff --staged 查看暂存区改动 2. 分析改动类型feat/fix/docs/refactor 3. 生成符合规范的提交信息 4. 使用 $ARGUMENTS 中的描述作为补充 注意事项不要自动执行 git commit只生成信息供用户确认。逐个字段说明。name是显示名称可选默认取目录名。description最重要它决定模型能否通过 SkillTool 语义匹配到这个技能写不好模型就找不到。argument-hint是参数提示用户输入/commit-helper时会显示。arguments定义参数列表。model和effort控制模型行为effort可选low/medium/high或整数。context有两个值inline在主线程执行fork在独立子进程执行有自己的上下文和 token 预算。allowed-tools声明技能执行时额外允许的工具这个会作为权限声明注入。user-invocable控制用户能否通过/name调用disable-model-invocation控制模型能否通过 SkillTool 调用。这两个开关组合出四种调用权限按需配置。paths是条件激活字段这是很多人忽略的机制。带paths的技能不会在启动时激活而是存进conditionalSkillsMap等用户操作的文件路径匹配上模式时才激活。匹配用的是 gitignore 风格底层是ignore库。比如paths: src/**/*.tsx意味着只有当你打开或编辑src下的 tsx 文件时这个技能才会被加入动态技能列表。hooks定义钩子PreToolUse可以在工具执行前跑命令matcher是工具名正则。shell指定 shell 环境version是版本号。这里要强调一个加载顺序的坑initBundledSkills()必须在getCommands()之前完成。源码里是这么写的// 同步注册必须在 getCommands 之前完成 if (process.env.CLAUDE_CODE_ENTRYPOINT ! local-agent) { initBuiltinPlugins(); initBundledSkills(); } // 并行启动 const setupPromise setup(preSetupCwd, ...); const commandsPromise worktreeEnabled ? null : getCommands(preSetupCwd);initBundledSkills()是纯内存的数组 push 操作耗时不到 1ms但它必须在getCommands()启动前完成否则getBundledSkills()返回空数组内嵌技能全部丢失。这个设计说明加载是有严格时序的不是随便并行。4. 验证请求从斜杠命令到模型响应的完整链路配置写好了怎么确认技能真的被加载并正确调用这一节给出可复现的验证步骤。第一步确认技能被注册。启动 Claude Code 后输入/会弹出命令列表你的技能名应该出现在里面。如果没出现说明加载阶段就失败了往下看第五节排查。第二步触发技能并观察请求。输入/commit-helper 修复登录超时Claude Code 会走这条链路REPL.onSubmit() → handlePromptSubmit() // 判断是否排队 → processUserInput() // 模式路由命中 slash 分支 → processSlashCommand() // 解析命令名查注册表 → getMessagesForPromptSlashCommand() // 加载技能内容 → onQuery() // 发送给模型在processSlashCommand这一层会先解析命令名const parsed parseSlashCommand(inputString) // 输入 /commit-helper 修复登录超时 解析为 // { commandName: commit-helper, args: 修复登录超时, isMcp: false }然后查注册表找不到就返回Unknown skill。找到后按 type 分发技能都是prompt类型进入getMessagesForPromptSlashCommand()。第三步观察技能内容编译。这一步调用command.getPromptForCommand(args, context)它做三件事变量替换$ARGUMENTS、${CLAUDE_SKILL_DIR}、${CLAUDE_SESSION_ID}、shell 注入执行!command格式、返回ContentBlockParam[]。最终构造出的消息列表长这样const messages [ createUserMessage({ content: metadata }), // 命令元数据 createUserMessage({ content: skillContent, isMeta: true }), // SKILL.md 内容 ...attachmentMessages, createAttachmentMessage({ type: command_permissions, allowedTools: additionalAllowedTools, }), ] return { messages, shouldQuery: true, allowedTools, model, effort, command }注意isMeta: true这表示 SKILL.md 内容对用户隐藏但会作为 user message 发给模型。shouldQuery: true是关键它告诉上层需要调用模型。第四步验证模型响应。如果一切正常模型会按 SKILL.md 里的步骤执行比如先跑git diff --staged再生成提交信息。你可以在 Claude Code 的输出里看到工具调用记录。如果你想更直接地验证 endpoint 是否走对了可以在请求发出后去 TaoToken 控制台的用量页面看调用记录。控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 能看到每次请求的模型、token 消耗、时间戳。如果技能触发了但控制台没有记录说明请求没走 TaoToken回去检查环境变量。一个实测技巧把effort设成high时模型对 SKILL.md 里多步骤指令的遵循度明显更好。如果你的技能步骤多、逻辑复杂别省这个配置。5. 本篇常见错排查401、Unknown skill 与技能不激活这一节对照真实报错给出排查路径。这些错误我基本都踩过按顺序排查能省很多时间。报错一401 UnauthorizedAPI Error: 401 {error:{type:authentication_error,message:invalid x-api-key}}原因通常是 Key 配错。检查三点ANTHROPIC_AUTH_TOKEN是否复制完整TaoToken 的 Key 以sk-开头、有没有多余空格或换行、是否同时设了ANTHROPIC_API_KEY导致冲突。解决方法是只保留ANTHROPIC_AUTH_TOKEN清掉其他 Key 变量。如果用的是 settings.json确认 JSON 格式合法可以用cat ~/.claude/settings.json | python -m json.tool验证。报错二Unknown skill: xxxUnknown skill: commit-helper这个报错来自processSlashCommand的注册表查找失败。可能原因技能目录结构不对必须是skill-name/SKILL.md两级、SKILL.md文件名大小写不对必须全大写、frontmatter 的 YAML 格式错误导致解析失败、技能被同名技能覆盖。排查方法是在 Claude Code 里输入/看命令列表如果技能不在列表里就是加载失败。检查SKILL.md的 frontmatter 是否以---开头和结尾中间不能有语法错误。报错三技能在列表里但调用无反应技能能被/列出但输入后模型没按指令执行。这通常是description写得太模糊或者disable-model-invocation设成了true导致模型无法通过 SkillTool 调用。另外检查context是不是设成了forkfork 模式在独立子进程执行输出可能不在主线程显示。报错四条件技能不激活带了paths字段的技能不会在启动时激活。如果你配了paths: src/**/*.tsx但一直在编辑.ts文件技能永远不会激活。解决方法是确认你操作的文件路径匹配paths模式或者干脆去掉paths让它启动即激活。报错五local proxy failed / connection refusedError: connect ECONNREFUSED 127.0.0.1:xxxx这说明请求被转发到了本地代理端口通常是环境里残留了HTTP_PROXY/HTTPS_PROXY变量。Claude Code 会读取这些变量。解决方法是清掉代理变量unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy然后重新验证claude -p ok。报错六reading choices of undefined这个报错通常出现在响应格式不符合预期时说明 endpoint 返回的不是标准 OpenAI/Anthropic 格式。检查ANTHROPIC_BASE_URL是否写成了https://taotoken.net/api不要带/v1或/chat/completions后缀。Claude Code 会自己拼接路径。排查完这些如果技能还是不生效用最笨但最有效的办法把SKILL.md的 frontmatter 精简到只剩description确认能加载后再逐个加字段。这样能快速定位是哪个字段导致的解析失败。6. 语义一致 CTA把技能链路跑通之后加载链路和触发链路都跑通之后你会发现 skills 的真正价值在于「知识可维护」——最佳实践集中在SKILL.md里更新文件就更新了能力不用改代码。这也是为什么值得花时间搞懂源码你知道技能在哪个环节被解析、在哪个环节被注入排查问题时就有据可依。如果你还没配好 endpoint先去 TaoToken 控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建 KeyAPI Key 管理页 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 可以随时查看和轮换。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各客户端的配置示例。想先手动验证模型是否可用去模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 试一条请求最快。如果你打算长期用 Claude Code 做编码和 Agent 任务Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 的额度模型更适合高频调用场景。下一篇会讲剩下 8 种调用入口SkillTool 的模型自主调用、MCP Skill、Cron 定时任务、Agent 预加载、Ultraplan 关键字、初始 prompt 注入。这些入口的触发条件和执行模式各不相同搞懂了就能把技能用在更复杂的自动化流程里。