资讯详情

从0手把手教你写AI Skill:SKILL.md规范目录与Python可运行代码(TaoToken统一调度)

📅 2026/10/8 12:14:48 | 华诺云谱 👁 阅读
从0手把手教你写AI Skill:SKILL.md规范目录与Python可运行代码(TaoToken统一调度)
1. 从零搭建 AI Skill 的真实痛点为什么你的技能包总是跑不起来很多人第一次接触 AI Skill 这个概念时脑子里想的都是“给大模型加个工具函数”这么简单。但真正动手写的时候问题一个接一个冒出来SKILL.md 到底该写什么字段目录结构怎么组织才能让调度器识别多个技能之间怎么统一管理我见过太多人把技能写成一个个孤立的 Python 脚本每个脚本自己解析参数、自己处理返回值最后想加一个新技能就得改一遍主流程维护成本高得离谱。AI Skill 本质上是一套“给大模型看的说明书 给电脑执行的代码”的组合。SKILL.md 负责告诉模型这个技能叫什么、需要什么参数、返回什么格式scripts 目录下的 Python 文件负责真正干活。这两者必须严格对应否则模型生成的调用请求和实际函数签名对不上直接报错。更关键的是当你只有一两个技能时随便写写也能跑。但一旦技能数量超过三个没有统一的目录规范和调度器整个项目就会变成一团乱麻。你需要一个统一的入口来接收模型的调用请求根据技能名称路由到对应的执行函数再把结果标准化返回。这就是统一调度器的价值。这篇教程面向的是想自建可复用 AI 能力的开发者不管你是想给本地大模型加工具还是想搭建一套自己的 Agent 技能库下面的内容都能直接复制运行。我会从 SKILL.md 的规范写法讲起给出完整的目录结构模板然后写一个 Python 统一调度器最后演示本地运行验证的完整步骤。全程不依赖任何特殊网络环境Python 3.8 以上就能跑。如果你后续想把技能接到线上模型服务上可以用 TaoToken 做统一调度入口它提供了兼容 OpenAI 格式的 API技能调度器可以直接对接。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end API 端点是 https://taotoken.net/api 后面配置部分会详细说怎么接。先明确一个核心原则所有 Skill 的格式必须统一。目录命名统一用小写加连字符SKILL.md 的字段结构统一执行函数统一放在 scripts/skills.py 里函数名和 SKILL.md 里的技能名称一一对应。只有格式统一了统一调度器才能用同一套逻辑解析所有技能的调用请求。我试过在项目里混用不同的命名风格有的技能目录叫 weather有的叫 add_calculator结果调度器里得写一堆 if-else 来判断新增技能时特别容易漏。后来全部改成“功能-动作”的小写连字符格式调度器只需要维护一个字典映射新增技能就是加一行键值对的事。下面从目录结构开始一步步把整套东西搭起来。2. TaoToken 前置准备统一调度器对接模型 API 的配置方法在写调度器之前先说一下模型调用这一层怎么接。你的 AI Skill 最终是要被大模型调用的模型根据 SKILL.md 生成调用请求调度器执行完再把结果返回给模型。所以你需要一个能稳定调用模型的 API 入口。TaoToken 提供了兼容 OpenAI 接口规范的 API支持多种模型适合用来做技能调度器的后端。它的 API 端点是不带 UTM 的干净地址https://taotoken.net/api 。你需要在控制台创建一个 API Key然后就可以在调度器里通过标准的 HTTP 请求调用模型。具体操作路径是这样的先访问 https://taotoken.net/api-keys 创建密钥然后在代码里把 Base URL 设置为 https://taotoken.net/api Model ID 根据你需要的模型填写。如果你用的是 Claude Code 或者类似的编码工具可以在配置里把 Anthropic 的 Base URL 指向 TaoToken 的兼容端点这样就能统一走一个入口。对于技能调度器来说你需要在环境变量里配置两个东西一个是 API Key一个是 Base URL。推荐用 .env 文件管理不要硬编码在代码里。下面是一个标准的配置片段你可以直接复制到项目根目录的 .env 文件里# .env 配置文件 TAOTOKEN_API_KEYsk-你的实际密钥 TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_MODEL_IDclaude-3-5-sonnet-20241022然后在 Python 代码里用 os.environ 读取。如果你用的是 requests 库直接调 API请求体格式和 OpenAI 完全一致import os import requests api_key os.environ.get(TAOTOKEN_API_KEY) base_url os.environ.get(TAOTOKEN_BASE_URL) model_id os.environ.get(TAOTOKEN_MODEL_ID) headers { Authorization: fBearer {api_key}, Content-Type: application/json } payload { model: model_id, messages: [ {role: user, content: 查一下成都的天气} ], tools: [ { type: function, function: { name: 查询天气, description: 根据城市名查询实时天气, parameters: { type: object, properties: { city: {type: string, description: 城市名称} }, required: [city] } } } ] } resp requests.post(f{base_url}/v1/chat/completions, headersheaders, jsonpayload) print(resp.json())这段代码的关键在于 tools 字段它把 SKILL.md 里定义的技能元信息转换成了模型能理解的 function calling 格式。模型收到用户提问后会判断是否需要调用工具如果需要就返回一个包含技能名称和参数的 JSON 对象。你的调度器拿到这个 JSON路由到对应的 Python 函数执行再把结果塞回对话里。如果你用的是 Claude Code 这类工具配置方式略有不同。需要在 settings.json 里指定 Anthropic 的 Base URL 和 API Key让它走 TaoToken 的兼容端点。具体路径是打开 Claude Code 的设置找到 API 配置部分把 Base URL 改成 https://taotoken.net/api 然后填入你的 Key。这样 Claude Code 的所有模型请求都会经过 TaoToken 转发你可以在控制台看到调用记录和用量。对于长期做编码和 Agent 开发的场景可以考虑用 Coding Plan它提供了更稳定的调用配额和更低的延迟。具体入口在 https://taotoken.net/coding-plan 适合需要频繁调用模型进行技能编排的项目。配置好这一层之后你的调度器就有了模型调用的能力。接下来进入正题先写 SKILL.md 和目录结构。3. 可复制配置SKILL.md 规范模板与目录结构完整示例这一节给出可以直接复制的配置文件和目录结构。你不需要改任何字段名只需要把技能名称和参数替换成自己的需求。先看单个 Skill 的规范目录。所有技能都必须遵循这个结构根目录用小写加连字符命名格式是“功能-动作”。比如查天气叫 weather-query加法计算叫 add-calculator。根目录下必须有一个 SKILL.md 文件和一个 scripts 文件夹scripts 里面必须有一个 skills.py 文件。weather-query/ ├── SKILL.md └── scripts/ └── skills.pySKILL.md 的内容格式必须统一。下面是一个标准模板你直接复制把技能名称、功能描述、参数列表和调用格式替换掉就行# 技能名称查询天气 功能用户输入城市名返回该城市的实时天气信息。 参数 - city城市名称字符串类型比如北京、上海、成都必填项不可为空。 调用格式AI输出格式必须严格遵循 { name: 查询天气, params: { city: 北京 } } 备注 1. 若未传入 city 参数提示用户“请输入要查询的城市名称”。 2. 若传入的城市名称无效函数返回提示信息。这个格式里最关键的是“调用格式”这一段。模型会根据这个 JSON 结构生成调用请求你的调度器也会按照这个结构解析。name 字段必须和调度器注册表里的键完全一致params 里的参数名必须和 Python 函数的参数名完全一致。scripts/skills.py 里放真正的执行函数。函数名不要求和技能名称一样但参数名必须和 SKILL.md 里声明的一致。下面是一个查天气的示例def query_weather(city): 查询城市天气模拟接口实际使用时替换为真实 API 调用 :param city: 城市名称字符串 :return: 天气信息字符串 if not city: return 请输入要查询的城市名称 return f{city}今日天气晴25℃微风空气质量优。再写一个加法计算的技能目录结构完全一样add-calculator/ ├── SKILL.md └── scripts/ └── skills.pySKILL.md 内容# 技能名称加法计算 功能计算两个数字的和支持整数和小数。 参数 - a第一个数字整数或小数必填项。 - b第二个数字整数或小数必填项。 调用格式AI输出格式必须严格遵循 { name: 加法计算, params: { a: 10, b: 20 } } 备注 若传入的参数不是数字返回“参数错误请传入有效的数字”。skills.py 内容def add(a, b): 计算两个数字的和 :param a: 第一个数字 :param b: 第二个数字 :return: 计算结果字符串 if not isinstance(a, (int, float)) or not isinstance(b, (int, float)): return 参数错误请传入有效的数字整数或小数 return f{a} {b} {a b}现在把这两个技能放到一个总目录下加上统一调度器。总目录叫 ai-skills里面有一个 common 文件夹放调度器然后每个技能一个子目录ai-skills/ ├── common/ │ └── harness.py ├── weather-query/ │ ├── SKILL.md │ └── scripts/ │ └── skills.py ├── add-calculator/ │ ├── SKILL.md │ └── scripts/ │ └── skills.py └── main.pycommon/harness.py 是统一调度器负责导入所有技能的执行函数维护一个技能名称到函数的映射表然后提供一个 run_skill 方法接收模型返回的 JSON 并执行。下面是完整代码# common/harness.py # 统一调度器管理所有 Skill 的调用 from weather_query.scripts.skills import query_weather from add_calculator.scripts.skills import add # 技能注册表键是 SKILL.md 里的技能名称值是对应的执行函数 SKILL_MAP { 查询天气: query_weather, 加法计算: add, } def run_skill(ai_order): 统一调度入口 :param ai_order: 模型返回的调用请求格式为 {name: 技能名称, params: {...}} :return: 技能执行结果字符串 try: skill_name ai_order[name] params ai_order[params] except KeyError: return 错误调用请求格式不正确缺少 name 或 params 字段。 skill_func SKILL_MAP.get(skill_name) if not skill_func: return f未找到技能{skill_name} try: result skill_func(**params) return result except Exception as e: return f技能执行失败{str(e)} if __name__ __main__: # 本地测试 test_orders [ {name: 查询天气, params: {city: 成都}}, {name: 加法计算, params: {a: 15.5, b: 24.5}}, ] for order in test_orders: print(run_skill(order))注意导入路径的写法。因为技能目录名用了连字符Python 不能直接 import所以需要在项目根目录创建一个init.py 或者用 importlib 动态导入。更简单的做法是在 ai-skills 目录下运行并且把技能目录名里的连字符在导入时改成下划线。Python 的 import 语句不支持连字符所以实际导入时写 from weather_query.scripts.skills import query_weather但文件夹名字保持 weather-query 不变。这需要你在 ai-skills 目录下创建一个 sitecustomize.py 或者用 sys.path 处理。最稳妥的方式是用 importlibimport importlib def load_skill_func(skill_dir_name, func_name): module_path f{skill_dir_name.replace(-, _)}.scripts.skills module importlib.import_module(module_path) return getattr(module, func_name) SKILL_MAP { 查询天气: load_skill_func(weather-query, query_weather), 加法计算: load_skill_func(add-calculator, add), }这样就不用手动改文件夹名了。把这段替换掉 harness.py 里的直接 import 即可。main.py 是统一调用入口模拟模型返回的调用请求然后通过调度器执行# main.py from common.harness import run_skill # 模拟模型返回的调用请求 ai_orders [ {name: 查询天气, params: {city: 成都}}, {name: 加法计算, params: {a: 15.5, b: 24.5}}, ] for order in ai_orders: result run_skill(order) print(f调用 {order[name]} 结果{result})到这里目录结构、SKILL.md 模板、调度器代码都齐了。接下来验证运行。4. 验证请求与成功结果本地运行调度器的完整步骤这一节演示怎么在本地把整套东西跑起来包括环境准备、依赖安装、运行命令和预期输出。先确认 Python 版本。打开终端输入python --version确保是 3.8 或以上。如果是 3.7 以下建议升级因为后面用到的 f-string 和类型注解在旧版本上可能有问题。然后进入 ai-skills 目录。假设你的项目放在 ~/projects/ai-skills执行cd ~/projects/ai-skills先测试单个技能能不能跑。进入 weather-query/scripts 目录cd weather-query/scripts python -c from skills import query_weather; print(query_weather(成都))预期输出成都今日天气晴25℃微风空气质量优。如果报 ModuleNotFoundError说明当前目录不对确认你在 scripts 文件夹里并且 skills.py 文件存在。再测试加法计算cd ../../add-calculator/scripts python -c from skills import add; print(add(10, 20))预期输出10 20 30单个技能没问题后回到 ai-skills 根目录运行统一调度器cd ../.. python common/harness.py预期输出成都今日天气晴25℃微风空气质量优。 15.5 24.5 40.0再运行 main.pypython main.py预期输出调用 查询天气 结果成都今日天气晴25℃微风空气质量优。 调用 加法计算 结果15.5 24.5 40.0如果这两条都出来了说明调度器工作正常。现在把模型接进来验证完整的“模型生成调用请求 → 调度器执行 → 返回结果”闭环。写一个 test_model_call.py放在 ai-skills 根目录import os import json import requests from common.harness import run_skill api_key os.environ.get(TAOTOKEN_API_KEY) base_url os.environ.get(TAOTOKEN_BASE_URL, https://taotoken.net/api) model_id os.environ.get(TAOTOKEN_MODEL_ID, claude-3-5-sonnet-20241022) headers { Authorization: fBearer {api_key}, Content-Type: application/json } # 把 SKILL.md 里的技能定义转成 tools 格式 tools [ { type: function, function: { name: 查询天气, description: 根据城市名查询实时天气, parameters: { type: object, properties: { city: {type: string, description: 城市名称} }, required: [city] } } }, { type: function, function: { name: 加法计算, description: 计算两个数字的和, parameters: { type: object, properties: { a: {type: number, description: 第一个数字}, b: {type: number, description: 第二个数字} }, required: [a, b] } } } ] user_input 帮我查一下成都的天气然后算一下 15.5 加 24.5 等于多少 payload { model: model_id, messages: [{role: user, content: user_input}], tools: tools, tool_choice: auto } resp requests.post(f{base_url}/v1/chat/completions, headersheaders, jsonpayload) data resp.json() # 解析模型返回的 tool_calls message data[choices][0][message] if tool_calls in message: for tool_call in message[tool_calls]: func_name tool_call[function][name] func_args json.loads(tool_call[function][arguments]) order {name: func_name, params: func_args} result run_skill(order) print(f模型请求调用 {func_name}参数 {func_args}执行结果{result}) else: print(模型没有调用工具直接回复, message.get(content))运行前先设置环境变量export TAOTOKEN_API_KEYsk-你的实际密钥 export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODEL_IDclaude-3-5-sonnet-20241022 python test_model_call.py预期输出类似模型请求调用 查询天气参数 {city: 成都}执行结果成都今日天气晴25℃微风空气质量优。 模型请求调用 加法计算参数 {a: 15.5, b: 24.5}执行结果15.5 24.5 40.0看到这个输出说明从模型到调度器到技能函数的完整链路已经打通。你可以把 test_model_call.py 里的 user_input 换成其他问题模型会自动判断该调用哪个技能。如果模型返回的 tool_calls 里参数格式和你的函数签名对不上检查 SKILL.md 里的参数名和 Python 函数的参数名是否完全一致。大小写、单复数都要对上。5. 本篇常见错误排查401、local proxy failed、reading choices 报错对照这一节列出实际运行中最容易遇到的几个报错给出原因和解决方法。每个报错都附上真实的错误信息片段方便你对照。报错一401 Unauthorized{error: {message: Invalid API key, type: authentication_error}}原因API Key 没设置或者设置错了。检查环境变量 TAOTOKEN_API_KEY 是否为空或者 Key 是否已经过期。如果你用的是 .env 文件确认 python-dotenv 已经安装并且调用了 load_dotenv()。另外注意 Key 的前缀TaoToken 的 Key 通常以 sk- 开头复制时不要带多余空格。解决方法重新在 https://taotoken.net/api-keys 创建一个新 Key然后 export 到环境变量里。如果是在 IDE 里运行检查运行配置的环境变量面板是否填了。报错二local proxy failed 或 connection refusedrequests.exceptions.ConnectionError: HTTPConnectionPool(hostlocalhost, port8080): Max retries exceeded原因代码里配置的 Base URL 指向了本地代理地址但本地没有运行代理服务。常见于从其他项目复制代码时Base URL 没有改成 TaoToken 的地址。解决方法把 Base URL 改成 https://taotoken.net/api 不要带任何本地地址。检查代码里是否有 http://localhost:xxxx 或 http://127.0.0.1:xxxx 的硬编码全部替换掉。报错三reading choices 报错KeyError: choices或者TypeError: NoneType object is not subscriptable原因API 返回的 JSON 里没有 choices 字段通常是因为请求失败但代码没有检查 HTTP 状态码直接去取 data[choices] 就报错了。可能是模型 ID 写错了或者请求体格式不对。解决方法在解析响应之前先打印 resp.status_code 和 resp.text看看实际返回了什么。如果是 400 错误检查 model 字段是否和 TaoToken 支持的模型 ID 一致。如果是 404检查 Base URL 后面有没有多写或少写 /v1。报错四OAuth 相关错误{error: invalid_grant, error_description: OAuth token expired}原因如果你用的是 Claude Code 或其他需要 OAuth 认证的工具Token 过期了。TaoToken 的 API Key 认证和 OAuth 是两套体系不要混用。解决方法在 Claude Code 的配置里把认证方式改成 API KeyBase URL 填 https://taotoken.net/api 然后填入在控制台创建的 Key。如果工具强制要求 OAuth检查是否有 API Key 模式的选项。报错五ModuleNotFoundError: No module named weather_query原因Python 导入路径不对。连字符目录名不能直接 import需要用 importlib 动态加载或者把目录名改成下划线。解决方法用第 3 节里给出的 importlib 方案不要直接写 from weather-query.scripts.skills import query_weather。另外确认你在 ai-skills 根目录下运行而不是在子目录里。报错六技能执行失败参数不匹配技能执行失败query_weather() got an unexpected keyword argument city_name原因SKILL.md 里写的参数名是 city但 Python 函数定义的是 city_name模型按照 SKILL.md 生成请求传的是 city函数不认。解决方法把 SKILL.md 里的参数名和 Python 函数的参数名改成完全一致。建议先写 Python 函数确定参数名后再写 SKILL.md避免手误。报错七模型不调用工具直接回复文字原因tools 字段格式不对或者 tool_choice 设置成了 none。检查 tools 数组里每个元素的 type 是否是 functionfunction 里是否有 name、description、parameters 三个字段。parameters 必须是合法的 JSON Schema。解决方法参考第 4 节的 tools 定义确保格式完全一致。如果模型仍然不调用尝试把 tool_choice 改成 {type: function, function: {name: 查询天气}} 强制指定。报错八返回结果里中文乱码原因终端编码不是 UTF-8。在 Windows 上尤其常见。解决方法在代码开头加 import sys; sys.stdout.reconfigure(encodingutf-8)或者设置环境变量 PYTHONIOENCODINGutf-8。以上这些报错覆盖了 90% 以上的新手问题。如果遇到其他错误先看 HTTP 状态码和返回体大部分问题都能从返回信息里找到线索。6. 语义一致 CTA把技能调度器接到真实模型服务上现在你已经有了一个能跑的 AI Skill 调度器本地验证也通过了。下一步是把它接到真实的模型服务上让模型自动判断该调用哪个技能。如果你还没有 API Key先去 https://taotoken.net/api-keys 创建一个。创建完之后把 Key 配置到环境变量里Base URL 用 https://taotoken.net/api 。然后在调度器的模型调用层把请求指向这个地址。对于需要长期运行编码 Agent 的场景可以看一下 Coding Plan它提供了更稳定的调用配额适合频繁调用模型进行技能编排的项目。入口在 https://taotoken.net/coding-plan 。如果你用的是 Claude Code需要在设置里把 Anthropic 的 Base URL 改成 TaoToken 的兼容端点然后填入 API Key。具体配置路径在 Claude Code 的设置面板里找到 API 配置部分把 Base URL 改成 https://taotoken.net/api Key 填你创建的那个。这样 Claude Code 的所有模型请求都会走 TaoToken你可以在控制台看到调用记录。模型对话的调试入口在 https://taotoken.net/chat 你可以在那里直接测试模型是否能正确识别你的 SKILL.md 定义。把 SKILL.md 的内容粘贴到系统提示里然后输入用户问题看模型返回的 tool_calls 格式是否和你的调度器预期一致。接入文档在 https://taotoken.net/doc 里面有完整的 API 参考和示例代码。如果你在配置过程中遇到问题先对照第 5 节的报错排查大部分连接和认证问题都能解决。最后提醒一点调度器里的 SKILL_MAP 是手动维护的每新增一个技能就要加一行。如果你想让调度器自动扫描目录下的所有 SKILL.md 并注册可以写一个扫描函数遍历 ai-skills 下的所有子目录读取 SKILL.md 里的技能名称然后用 importlib 加载对应的 skills.py。这样新增技能就只需要创建目录和文件不用改调度器代码。这个扩展留给你自己实现核心思路已经在前面的代码里了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑