1. 从一次 Agent 技能调用失败说起:SKILL.md 到底解决什么问题
如果你正在搭 AI Agent,大概率遇到过这种场景:模型明明知道该干什么,但一到具体任务就开始自由发挥——该查数据库的时候在编数据,该走审批流的时候直接给结论。你写了一大段 system prompt 约束它,结果换个对话轮次又失效了。这不是模型不行,而是你缺了一层结构化的能力描述。
Skill(技能)就是干这个的。它是 AI Agent 系统中用于封装特定能力、知识或行为的模块化组件,定义了 Agent 在特定场景下“知道什么”和“能做什么”。而 SKILL.md 是这套机制里最关键的落地文件——它把一段模糊的能力描述,变成 Agent 可解析、可路由、可执行的契约。
我试过用纯 prompt 让 Agent 处理“查订单并判断是否可退款”这类任务,前几轮还行,一旦用户追问细节就开始漂。后来把退款规则、订单查询接口、判断逻辑拆成一个独立 Skill,用 SKILL.md 声明触发条件和执行步骤,稳定性直接上了一个台阶。核心区别在于:prompt 是建议,SKILL.md 是契约。
这篇文章面向正在搭建 Agent 技能体系的开发者,会从 SKILL.md 的结构讲起,给出可直接复制的模板,然后结合 Kimi 的 Skill 目录形态和 Model Context Protocol(MCP)的调用场景,说明怎么把技能描述文件变成真正可执行的能力。最后用 TaoToken 统一 Key/API 通道完成接入验证,让你手里的 Agent 能稳定调用这些 Skill。
适合谁看:已经写过 function calling、正在纠结怎么管理多个工具、想让 Agent 从“能聊”变成“能干活”的开发者。不需要你精通 MCP 协议,但至少要跑通过一次大模型 API 调用。
2. TaoToken 前置准备:统一 Key 与 API 通道,让 Skill 调用不再散落各处
在讲 SKILL.md 模板之前,先把接入层的事情说清楚。因为 Skill 落地最大的坑不是描述文件写得好不好,而是每个 Skill 背后可能连着不同的模型、不同的 API 端点、不同的鉴权方式。Kimi 的 Skill 用一套,MCP 的工具用另一套,本地调试又换一套,Key 散落在四五个地方,排查问题时根本不知道是哪一层挂了。
TaoToken 在这里的角色是统一接入层。它提供一个兼容 OpenAI 风格的 API 通道,你可以用同一个 Base URL 和同一个 Key,去调用不同模型,包括 Kimi 系列和 Claude 系列。对于 Skill 体系来说,这意味着 SKILL.md 里声明的模型配置可以统一指向一个端点,Agent 编排层不需要为每个 Skill 维护独立的鉴权逻辑。
具体要准备三样东西:
第一,API Key。到 TaoToken 控制台的 API Keys 页面创建一个,格式通常是sk-开头的一串字符。这个 Key 会同时用于模型对话和后续的 Skill 调用验证。
第二,Base URL。统一使用https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 OpenAI SDK 的base_url传入即可。
第三,Model ID。这是最容易被忽略的一环。Skill 里如果写死了gpt-4之类的模型名,换到 TaoToken 通道后可能直接报模型不存在。你需要到模型对话页面确认当前可用的模型标识,比如 Kimi 系列对应的 ID 是什么,然后把它写进 SKILL.md 的模型配置段。
把这三件套记牢:Base URL + Key + Model ID。后面无论是写 SKILL.md、配 MCP server,还是调 Claude Code,都是围绕这三个值展开。如果你用的是 Cline 或 Claude Code 这类工具,它们的配置文件里也是填这三项,只是字段名不同。
有一点要提醒:不要把 Key 硬编码在 SKILL.md 里然后提交到 Git。SKILL.md 是描述文件,应该通过环境变量引用 Key,比如${TAOTOKEN_API_KEY},实际值放在.env或系统的环境变量里。这样 Skill 可以共享,Key 不会泄露。
3. 可复制配置:SKILL.md 模板与 Agent 调用参数
这一节是全文的核心,直接给你能用的东西。先看 SKILL.md 的完整模板,然后看 Agent 侧怎么读这个文件并发出请求。
SKILL.md 采用 YAML frontmatter + Markdown 正文的结构。frontmatter 放元数据和触发条件,正文放工作流和参考资源。下面是一个“订单退款判断”Skill 的模板,你可以直接复制修改:
--- name: order-refund-check description: 查询订单状态并判断是否符合退款条件,适用于电商客服场景 version: 1.0.0 keywords: - 退款 - 订单 - 退货 - 售后 triggers: - type: keyword values: ["退款", "退货", "能不能退"] - type: intent value: "refund_inquiry" model: provider: taotoken base_url: https://taotoken.net/api model_id: kimi-k2-0905-preview temperature: 0.2 max_tokens: 1024 parameters: type: object properties: order_id: type: string description: 订单编号,通常为 16 位数字 required: true reason: type: string description: 用户申请退款的原因 required: false required: - order_id output: format: json schema: refundable: type: boolean reason: type: string next_action: type: string --- # 订单退款判断 Skill ## Usage 当用户询问订单能否退款、退货流程、售后政策时触发本 Skill。 如果用户没有提供订单号,先追问订单号,不要自行编造。 ## Workflow 1. 从用户消息中提取 order_id,若缺失则追问。 2. 调用 `query_order_status` 工具获取订单状态,参数为 order_id。 3. 根据订单状态判断: - 状态为 `paid` 且未发货:可退款,next_action 为 "直接退款" - 状态为 `shipped`:可退款但需拦截物流,next_action 为 "联系物流拦截" - 状态为 `delivered` 且超过 7 天:不可退款,next_action 为 "转人工" - 状态为 `refunded`:已退款,next_action 为 "告知用户已处理" 4. 结合用户提供的 reason 字段,生成友好回复。 5. 输出必须符合 output.schema 定义的 JSON 结构。 ## References - references/refund-policy.md:退款政策细则 - references/order-status-codes.md:订单状态码对照表这个模板里有几个关键点值得展开。triggers段决定了 Skill 什么时候被激活,关键词触发和意图触发可以同时存在,Agent 编排层会做匹配。model段里的base_url和model_id就是上一节说的三件套中的两项,Key 不写在这里,通过环境变量注入。parameters用 JSON Schema 定义输入,Agent 在调用前会做参数校验,缺order_id就直接追问,不会带着空参数去执行。
Agent 侧读取这个文件后,实际发出的请求长这样。以 Python 为例:
import os import json from openai import OpenAI client = OpenAI( base_url="https://taotoken.net/api", api_key=os.environ["TAOTOKEN_API_KEY"], ) skill_meta = { "name": "order-refund-check", "model_id": "kimi-k2-0905-preview", "temperature": 0.2, } response = client.chat.completions.create( model=skill_meta["model_id"], temperature=skill_meta["temperature"], messages=[ { "role": "system", "content": "你正在执行 order-refund-check Skill,严格按照 SKILL.md 中的 Workflow 处理。", }, { "role": "user", "content": "订单 1234567890123456 能退款吗?我不想要了。", }, ], response_format={"type": "json_object"}, ) print(response.choices[0].message.content)注意response_format设成了json_object,这样模型输出会强制走 JSON,方便下游解析。如果你用的是 MCP 协议,SKILL.md 里的parameters段可以直接映射成 MCP tool 的 inputSchema,Workflow段则作为 tool 的 description 传给模型。MCP server 启动时读取 SKILL.md,注册成一个可调用的 tool,Agent 通过 MCP 客户端发现并调用它。
如果你用 Cline 或 Claude Code,配置方式略有不同。Cline 的 MCP 配置在cline_mcp_settings.json里,需要填 command、args 和环境变量。Claude Code 则用~/.claude/settings.json或项目级的.mcp.json。不管哪种,核心还是那三件套:Base URL 指向https://taotoken.net/api,Key 从环境变量读,Model ID 填你在模型对话页面确认的值。
4. 验证请求:从 SKILL.md 到成功返回的完整链路
配置写完了,怎么确认整条链路是通的?分三步验证,每一步都有明确的成功标志。
第一步,验证 API 通道本身。先用一个最简单的请求确认 Key 和 Base URL 没问题:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "kimi-k2-0905-preview", "messages": [{"role": "user", "content": "回复 OK"}], "max_tokens": 10 }'成功的话会返回一个 JSON,choices[0].message.content里是 “OK” 或类似内容。如果这一步就报 401,说明 Key 有问题,去控制台确认 Key 是否启用、是否复制完整。如果报模型不存在,说明 Model ID 写错了,去模型对话页面核对。
第二步,验证 SKILL.md 能被正确解析。写一个小的解析脚本,读 frontmatter 并打印关键字段:
import yaml with open("skills/order-refund-check/SKILL.md", "r", encoding="utf-8") as f: content = f.read() frontmatter = content.split("---")[1] meta = yaml.safe_load(frontmatter) assert meta["name"] == "order-refund-check" assert meta["model"]["base_url"] == "https://taotoken.net/api" assert "order_id" in meta["parameters"]["properties"] print("SKILL.md 解析通过") print("模型:", meta["model"]["model_id"]) print("触发词:", meta["triggers"][0]["values"])这一步能跑通,说明你的 SKILL.md 格式没问题,Agent 编排层可以正常读取。
第三步,端到端验证。把 SKILL.md 的内容作为 system prompt 的一部分,加上用户输入,发一次完整请求:
import os from openai import OpenAI client = OpenAI( base_url="https://taotoken.net/api", api_key=os.environ["TAOTOKEN_API_KEY"], ) with open("skills/order-refund-check/SKILL.md", "r", encoding="utf-8") as f: skill_content = f.read() response = client.chat.completions.create( model="kimi-k2-0905-preview", temperature=0.2, messages=[ {"role": "system", "content": skill_content}, {"role": "user", "content": "订单 1234567890123456 能退款吗?"}, ], response_format={"type": "json_object"}, ) result = response.choices[0].message.content print(result)成功返回应该是一个 JSON,包含refundable、reason、next_action三个字段。如果refundable是布尔值而不是字符串,说明模型正确遵循了 output schema。如果返回的是自然语言而不是 JSON,检查response_format是否设置正确,以及 SKILL.md 里 output 段是否写清楚了。
实测下来,最容易出问题的是模型没有严格按 Workflow 走。比如用户没给订单号,模型自己编了一个。解决办法是在 SKILL.md 的 Usage 段里明确写“若缺失则追问,不要编造”,并且在 system prompt 里再强调一次。约束要写两遍,一遍在文件里,一遍在调用时。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节按报错信息来,你遇到哪个查哪个。
401 Unauthorized。最常见的原因是 Key 没传对。检查三处:环境变量TAOTOKEN_API_KEY是否真的存在(echo $TAOTOKEN_API_KEY看一下),请求头里是不是Bearer加空格再加 Key,Key 本身有没有多余的空格或换行。如果 Key 是从控制台复制的,注意不要复制到首尾的空白字符。还有一种情况是 Key 被禁用或删除了,去 API Keys 页面确认状态。
local proxy failed / connection refused。这个报错通常出现在你本地起了代理或者 MCP server 没启动。如果你在用 Cline 的 MCP 功能,检查cline_mcp_settings.json里的 command 路径是否正确,args 是否指向了存在的脚本。如果 MCP server 是 Python 写的,确认依赖装在了正确的虚拟环境里。另外,Base URL 不要写成https://taotoken.net/api/带尾斜杠,有些 SDK 会拼出双斜杠导致路由失败。
reading 'choices' of undefined。这是典型的响应结构不符合预期。原因通常是请求根本没成功,返回的是一个错误对象,但代码直接去读response.choices[0]。加一层判断:
if "choices" not in response: print("请求异常:", response) else: print(response["choices"][0]["message"]["content"])如果用的是 OpenAI SDK,它会在非 200 时抛异常,所以更可能是你捕获了异常但没打印内容。把except块里的错误信息完整打出来,通常能看到具体原因,比如模型不存在或参数格式错误。
OAuth 相关报错。如果你在配 Claude Code 或某些需要 OAuth 的工具,注意 TaoToken 的 API 通道用的是 Bearer Key,不是 OAuth 流程。不要把 OAuth 的 client_id、client_secret 填到 API Key 的位置。Claude Code 的配置里,如果它要求填ANTHROPIC_API_KEY,你填 TaoToken 的 Key 即可,Base URL 指向https://taotoken.net/api。如果工具强制走 OAuth 且不让你改 Base URL,那它可能不支持自定义端点,换用支持 OpenAI 兼容接口的工具。
模型返回空内容或截断。检查max_tokens是否设得太小。SKILL.md 里如果 Workflow 步骤多,输出 JSON 又长,1024 可能不够。调到 2048 或 4096 试试。另外temperature设太高会导致输出不稳定,Skill 类任务建议 0.1 到 0.3 之间。
SKILL.md 解析失败。YAML frontmatter 对缩进敏感,不要用 Tab,全部用空格。---分隔符必须独占一行,前后不能有空格。如果parameters段嵌套层级深,建议用在线 YAML 校验工具先验一遍。
6. 语义一致 CTA:把 Skill 接入统一通道,从验证到长期运行
走到这里,你的 SKILL.md 已经能跑通了,Agent 也能正确调用。接下来要解决的是长期运行的问题:多个 Skill 怎么管理,Key 怎么轮换,模型怎么切换。
统一接入的价值在这里体现得最明显。所有 Skill 的base_url都指向同一个地址,Key 只有一份,换模型只需要改 SKILL.md 里的model_id,不用动鉴权逻辑。如果你有十个 Skill,分别连十个不同的端点,维护成本是指数级上升的。统一通道把它压成线性。
具体操作上,建议把 Key 和 Base URL 抽成环境变量或配置中心的值,SKILL.md 里只写引用。比如:
model: provider: taotoken base_url: ${TAOTOKEN_BASE_URL} model_id: ${TAOTOKEN_MODEL_ID}这样不同环境(开发、测试、生产)可以用不同的 Key,但 SKILL.md 本身不变。Agent 编排层在加载 Skill 时做变量替换。
如果你要验证某个 Skill 在特定模型上的表现,直接到模型对话页面切换模型试。那里可以快速对比 Kimi 和 Claude 在同一个 SKILL.md 下的输出差异,不用改代码。确认哪个模型更合适后,再把model_id写回 SKILL.md。
对于需要长期跑编码任务或 Agent 工作流的场景,Coding Plan 提供了更稳定的配额和优先级。它适合那种每天都要调用几十上百次 Skill 的情况,按量付费的 Key 在高峰期可能会有延迟波动。你可以先按量验证,确认 Skill 体系稳定后再切到 Coding Plan。
接入文档里有完整的参数说明和错误码对照,遇到本文没覆盖的报错可以去那里查。API Keys 页面管理你的 Key,支持创建多个 Key 做环境隔离。模型对话页面用来快速验证模型可用性和输出效果。
最后给一个实用建议:每个 Skill 上线前,用固定的测试用例跑一遍,把输入和期望输出存成 JSON 文件,每次改完 SKILL.md 就回归测试一次。Skill 是契约,契约变了就要验证,不然 Agent 的行为会在你不知情的情况下漂移。