1. 从一次“重复解释”说起:Claude Code Skills 到底解决什么问题
如果你用 Claude Code 写过一段时间代码,大概率遇到过这种场景:每次让它按团队规范生成接口文档,都要重新贴一遍格式要求;每次让它做代码审查,都要重复强调“先看有没有空指针、再看日志埋点是否齐全”。这些知识你脑子里很清楚,但 Claude 每次开新会话都像失忆一样,得从头教。
Claude Code Skills 就是冲着这个痛点来的。简单说,它是一套让 Agent 按需加载“专业技能包”的机制——你把某类任务的流程、规范、脚本打包成一个文件夹,Claude 在遇到相关任务时自动识别并加载,不需要你每次手动喂上下文。适合谁?适合已经在用 Claude Code 做日常开发、想让 Agent 稳定复现某套工作流的开发者,尤其是团队里需要统一代码规范、文档模板、审查清单的场景。
核心概念有三个:SKILL.md 是技能的描述文件,用 YAML frontmatter 声明名称和用途;Progressive Disclosure(渐进式披露)是加载策略,分三层按需读取,避免一次性塞满上下文窗口;MCP 则是另一条线,负责连接外部系统,和 Skills 是互补关系而非替代关系。这篇会从目录结构讲到可复制的 SKILL.md 模板,再演示一次技能触发和验证,最后说清楚 Skills 和 MCP 的边界在哪。
我试过把一个“API 文档生成”技能包放进项目里,之后每次让 Claude 生成接口说明,它都会自动按我们团队的字段顺序和示例格式输出,省掉了反复贴模板的步骤。下面把整套流程拆开讲。
2. 前置准备:TaoToken 统一 Key 与 Claude Code 接入配置
在写 SKILL.md 之前,得先让 Claude Code 能正常跑起来。如果你已经在用官方通道,可以跳过这节;如果希望通过统一 Key/API 通道管理调用,TaoToken 是一个可选方案。它的作用是把模型调用收敛到一个 Base URL 和一把 Key 上,方便在 Claude Code、Cline、Codex 等工具之间切换时不用反复改配置。
先拿 Key。打开 https://taotoken.net/api-keys ,登录后创建一个 API Key,复制保存。注意这个 Key 只在创建时完整显示一次,丢了就得重新生成。
接下来配置 Claude Code 的接入信息。Claude Code 读取的是环境变量或 settings 文件,推荐用 settings 方式,路径在项目根目录的.claude/settings.json,或者用户级的~/.claude/settings.json。内容如下:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的TaoToken密钥" } }如果你用的是 Claude Code 的 CLI 启动方式,也可以直接在 shell 里导出:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的TaoToken密钥"这里有个容易踩的坑:Base URL 末尾不要带/v1,Claude Code 会自己拼接路径。如果你写成https://taotoken.net/api/v1,请求会变成/api/v1/v1/messages,直接 404。
配置完成后,验证一下通道是否通:
claude -p "回复一句:通道正常"如果返回了正常文本,说明 Key 和 Base URL 都生效了。如果报 401,先检查 Key 有没有复制完整;如果报连接超时,检查 Base URL 是否写错。这一步过了,再往下写 SKILL.md 才有意义,否则技能触发了也调不通模型。
关于模型 ID 的选择,Claude Code 默认会用一个通用模型名,你也可以在 settings 里显式指定:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的TaoToken密钥", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" } }Model ID 写错会报model not found,这个在排障章节会细说。配置好之后,Claude Code 的每次请求都会走你设置的通道,Skills 的加载和触发也在这个基础上进行。
3. 可复制配置:SKILL.md 模板与目录结构
Claude Code Skills 的载体是一个文件夹,文件夹里必须有一个SKILL.md。这个文件以 YAML frontmatter 开头,至少包含name和description两个字段。Claude 在启动时会扫描所有技能的元数据,只加载 name 和 description 到系统提示里,用来判断当前任务该不该激活某个技能。这就是 Progressive Disclosure 的第一层。
先看目录结构。假设你要做一个“API 文档生成”技能,放在项目根目录的.claude/skills/下:
.claude/ skills/ api-doc-generator/ SKILL.md templates/ endpoint-template.md scripts/ extract_routes.pySKILL.md是入口,templates/放模板文件,scripts/放可执行脚本。Claude 在需要时才会去读 templates 或执行 scripts,平时这些文件不占上下文。
下面是SKILL.md的完整模板,可以直接复制改:
--- name: api-doc-generator description: 根据代码中的路由定义生成符合团队规范的 API 文档。当用户要求生成接口文档、API 说明或 endpoint 列表时使用。 --- # API 文档生成技能 ## 使用场景 当用户要求为某个模块或文件生成 API 文档时,按以下步骤执行。 ## 执行步骤 1. 读取用户指定的源文件,识别路由定义(如 Flask 的 `@app.route`、FastAPI 的 `@router.get`)。 2. 对每个路由,提取 HTTP 方法、路径、请求参数、返回结构。 3. 按 `templates/endpoint-template.md` 的格式生成文档。 4. 如果路由数量超过 10 个,调用 `scripts/extract_routes.py` 批量提取,避免手动逐个解析。 ## 字段顺序规范 - 接口名称 - 请求方法 + 路径 - 请求参数(表格) - 返回示例(JSON 代码块) - 错误码说明 ## 注意事项 - 如果源文件里没有类型注解,在文档中标注“类型待确认”。 - 不要编造返回字段,只写代码里实际出现的。这个文件里,frontmatter 的description很关键。它决定了 Claude 什么时候激活这个技能。写得太泛(比如“生成文档”)会导致误触发,写得太窄又可能漏触发。建议把触发场景写具体,比如“当用户要求生成接口文档、API 说明或 endpoint 列表时使用”。
Progressive Disclosure 的第二层就是加载整个SKILL.md的内容。第三层是SKILL.md里引用的templates/endpoint-template.md和scripts/extract_routes.py,Claude 只在执行到对应步骤时才去读或执行。
templates/endpoint-template.md可以这样写:
## {{接口名称}} **{{请求方法}} {{路径}}** ### 请求参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | {{name}} | {{type}} | {{required}} | {{desc}} | ### 返回示例 ```json {{response_json}}错误码
| 错误码 | 说明 |
|---|---|
| {{code}} | {{message}} |
`scripts/extract_routes.py` 是一个简单的路由提取脚本: ```python import re import sys import json def extract_routes(filepath): with open(filepath, 'r', encoding='utf-8') as f: content = f.read() pattern = r'@\w+\.(get|post|put|delete)\(["\']([^"\']+)["\']\)' matches = re.findall(pattern, content) return [{"method": m[0].upper(), "path": m[1]} for m in matches] if __name__ == '__main__': routes = extract_routes(sys.argv[1]) print(json.dumps(routes, ensure_ascii=False, indent=2))这个脚本的作用是把路由提取这件事从“让模型读代码猜”变成“确定性执行”,减少幻觉。Claude 通过代码执行工具调用它,拿到 JSON 结果后再填模板。
目录放好后,Claude Code 启动时会自动扫描.claude/skills/下的所有文件夹。你不需要额外注册,只要SKILL.md的 frontmatter 格式正确,技能就会被识别。
4. 验证请求与成功结果:一次技能触发与验证动作
配置和文件都就位后,怎么确认技能真的被触发了?这里演示一次完整的验证流程。
先准备一个测试用的源文件,比如demo_routes.py:
from flask import Flask app = Flask(__name__) @app.route('/users', methods=['GET']) def list_users(): return [] @app.route('/users/<int:user_id>', methods=['GET']) def get_user(user_id): return {} @app.route('/orders', methods=['POST']) def create_order(): return {}然后在 Claude Code 里输入:
帮我为 demo_routes.py 生成 API 文档如果技能配置正确,Claude 会先扫描到api-doc-generator的 description,判断当前任务匹配,然后加载完整的SKILL.md,按里面的步骤执行。你会看到它先读取demo_routes.py,识别出三个路由,然后按templates/endpoint-template.md的格式输出文档。
成功的结果大概长这样:
## 用户列表 **GET /users** ### 请求参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | 无 | - | - | - | ### 返回示例 ```json []错误码
| 错误码 | 说明 |
|---|---|
| 500 | 服务器内部错误 |
如果你在输出里看到字段顺序和模板一致、返回示例是 JSON 代码块、错误码表格存在,说明技能生效了。如果 Claude 只是随便回了一段文字,没有按模板走,那可能是 description 没匹配上,或者 `SKILL.md` 的 frontmatter 格式有问题。 再验证一下第三层加载。当路由数量超过 10 个时,`SKILL.md` 里写了要调用 `scripts/extract_routes.py`。你可以造一个包含 12 个路由的文件,再让 Claude 生成文档,观察它是否执行了脚本。如果它直接开始手动解析而不是调脚本,说明脚本调用那一步的指令不够明确,可以在 `SKILL.md` 里把条件写得更硬:“路由数量超过 10 个时,必须调用 scripts/extract_routes.py,不要手动解析。” 验证通过后,你可以把这个技能包复制到其他项目里,只要目录结构一致,Claude Code 就能识别。这就是 Skills 的可移植性——一个文件夹带走一套工作流。 ## 5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth 技能跑不起来,很多时候不是 SKILL.md 写错了,而是接入层出了问题。下面按真实报错逐个排查。 **401 Unauthorized** 这是最常见的。报错原文一般是:API error: 401 Unauthorized - invalid api key
原因通常是 Key 没复制完整、Key 已失效、或者 Base URL 和 Key 不匹配。先检查 `ANTHROPIC_API_KEY` 是否以 `sk-` 开头且没有多余空格。如果用的是 settings.json,注意 JSON 里不能有注释,末尾不能有多余逗号。改完后重启 Claude Code 让配置生效。 **local proxy failed** 报错原文:Error: local proxy failed to start: listen tcp 127.0.0.1:xxxx: bind: address already in use
这是 Claude Code 的本地代理端口被占用了。常见原因是上一次会话没正常退出,进程还在后台。解决方式是找到占用端口的进程并结束,或者换个端口。在 settings 里可以指定: ```json { "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的TaoToken密钥" }, "proxyPort": 8899 }如果换端口后还是报错,检查系统代理设置有没有冲突。
reading choices 相关报错
报错原文类似:
Error: reading choices: unexpected end of JSON input这个通常出现在流式响应解析失败时。原因可能是 Base URL 写成了带/v1的路径,导致返回体格式不对。确认ANTHROPIC_BASE_URL是https://taotoken.net/api,不带/v1。另外检查网络是否稳定,流式响应中断也会导致 JSON 解析失败。
OAuth 相关报错
如果你之前用官方账号登录过 Claude Code,配置里可能残留 OAuth token,和 API Key 模式冲突。报错原文:
Error: OAuth token invalid or expired解决方式是清除本地 OAuth 缓存,强制走 API Key 模式。缓存文件通常在~/.claude/下,找到credentials.json或类似文件,备份后删除。然后在 settings 里确保只配置了ANTHROPIC_API_KEY,没有 OAuth 相关字段。
技能不触发
如果接入层没问题,但技能就是不激活,先检查SKILL.md的 frontmatter 是否合法。YAML 对缩进敏感,name和description必须顶格写,冒号后面要有空格。另外确认技能文件夹放在.claude/skills/下,而不是项目根目录或其他位置。Claude Code 只扫描特定路径。
脚本执行失败
如果SKILL.md里引用了scripts/extract_routes.py,但执行时报ModuleNotFoundError,检查脚本依赖是否安装。Claude Code 执行脚本时用的是当前环境的 Python,不会自动装依赖。可以在技能包里加一个requirements.txt,并在SKILL.md里写明“执行前先安装依赖”。
6. 语义一致 CTA:Skills 与 MCP 的边界及后续接入
把 Skills 和 MCP 放在一起看,两者的分工其实很清楚。MCP 解决的是“连通性”——让 Claude 能访问外部数据库、API、文件系统,相当于给 Agent 装了一双能伸出去的手。Skills 解决的是“程序性知识”——告诉 Agent 某类任务该按什么步骤、什么规范来做,相当于给 Agent 一本操作手册。
什么时候用 Skills?当你需要固化一套工作流,比如代码审查清单、文档模板、部署检查步骤,这些知识不依赖外部系统,只是“怎么做”的指令。什么时候用 MCP?当任务需要实时读取外部数据,比如查数据库、调内部 CRM、拉取监控指标,这些是 Skills 做不到的,因为 Skills 本身不直接连接外部服务。
两者可以协作。比如一个“发布检查”技能,SKILL.md里写清楚检查步骤,其中一步是“调用 MCP 工具查询当前服务健康状态”。这样 Skills 负责流程编排,MCP 负责数据获取,各司其职。
如果你还没配好接入通道,可以先从 API Keys 页面拿 Key,再对照接入文档把 Base URL 和 Model ID 填进 settings。想先验证模型对话是否正常,可以用模型对话页面发一条测试消息。如果打算长期用 Claude Code 做编码和 Agent 任务,Coding Plan 页面有更完整的配置说明。
技能包写好后,建议先在单个项目里跑通,确认触发和脚本执行都正常,再复制到其他项目。每次改SKILL.md的 description 后,重新启动 Claude Code 让元数据刷新。脚本里的路径尽量用相对路径,避免换项目后失效。