1. 为什么你的 Openclaw Agent 需要一个能跑通的 Skill
Openclaw 的 Skills 机制,本质上是给 AI Agent 装上一双能干活的手。大模型负责思考、决策、组织语言,而 Skills 负责真正去执行——查天气、发请求、跑脚本、写文件、调接口。你如果只让 Agent 聊天,它永远停在“说”的层面;一旦挂上 Skill,它就能从“说”跨到“做”。
这篇面向的是需要为 AI-Agent 扩展自定义技能的开发者,尤其是已经跑起 Openclaw、想加第一个自己的 Skill 却卡在目录结构、config.toml 骨架、Key 配置和调用验证这几步的人。我会用一个最小可运行的weather-querySkill 做贯穿示例,从目录创建、_meta.json与SKILL.md编写、主脚本实现,到用 TaoToken 统一 Key 打通模型调用链,最后完成一次完整的注册与调用验证。目标很直接:你复制配置、改掉城市名,就能跑通首个自定义 Skill。
很多人第一次写 Skill 会踩两个坑:一是把 Skill 当成普通 Node 脚本写,忽略了触发词和元数据注册;二是每个 Skill 各自维护一份模型 Key,散落在不同脚本里,改一次要翻十个文件。TaoToken 在这里的价值就是把 Key 收敛成一份统一配置,Skill 只关心业务逻辑,模型调用走同一个入口。下面按可跟做的顺序展开。
2. TaoToken 前置:统一 Key 与接入信息
在写 Skill 之前,先把模型调用的“总闸”接好。TaoToken 提供统一的 API 入口,你只需要在控制台生成一个 Key,后续所有 Skill 里需要调用模型的地方都复用它,不用每个 Skill 单独申请。
你需要先拿到两样东西:一个是 API Key,一个是接入地址。地址分两种用途,官网入口用于了解和控制台操作,API 地址用于代码里实际请求。
| 用途 | 地址 |
|---|---|
| 官网入口 | https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= |
| API 基址 | https://taotoken.net/api |
拿到 Key 的路径是:进入控制台,创建 API Key,复制保存。这个 Key 后面会写进 Skill 的配置里,或者更推荐的做法是写进环境变量,Skill 运行时读取。
注意:Key 不要硬编码进会提交到 Git 的脚本里。用环境变量或本地 config 文件,并在
.gitignore里排除。
如果你后续要做长期编码类或 Agent 类任务,可以了解 Coding Plan;如果只是想先验证模型对话是否通,可以用模型对话页面快速试一次。这两个入口在排障阶段很有用,但本篇主线还是把 Key 接进 Skill。
3. 可复制配置:目录结构、config.toml 与 Skill 骨架
3.1 标准目录结构
Openclaw 扫描 Skill 的默认路径是 workspace 下的skills/目录。每个 Skill 一个独立文件夹,名称唯一,内部结构如下:
workspace/skills/ └── weather-query/ ├── SKILL.md # 技能说明文档 ├── _meta.json # 元数据:触发词、入口脚本 ├── config.toml # 本 Skill 的配置(含模型 Key 引用) ├── scripts/ │ └── main.js # 主执行脚本 └── results/ # 输出结果目录(可选)先创建目录:
cd workspace/skills mkdir -p weather-query/scripts weather-query/results3.2 config.toml 骨架
config.toml是这个 Skill 的本地配置。模型调用统一走 TaoToken,所以这里只放基址和 Key 的引用,不重复写业务参数。
[skill] name = "weather-query" version = "1.0.0" enabled = true [model] provider = "taotoken" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" model = "gpt-4o-mini" timeout_ms = 30000 [weather] default_city = "北京" request_timeout_ms = 10000这里的关键是api_key_env,它指向环境变量名,而不是把 Key 明文写进文件。运行时脚本读取process.env.TAOTOKEN_API_KEY即可。这样多个 Skill 共用同一个 Key,改一处全局生效。
设置环境变量(Linux/macOS):
export TAOTOKEN_API_KEY="你的Key"Windows PowerShell:
$env:TAOTOKEN_API_KEY="你的Key"3.3 _meta.json 元数据
_meta.json决定 Skill 怎么被识别和触发。触发词要具体,避免太短导致误触发。
{ "name": "weather-query", "version": "1.0.0", "description": "查询指定城市实时天气", "main": "scripts/main.js", "triggers": [ "查询天气", "今天天气", "天气怎么样", "查天气" ], "autoExecute": true }autoExecute为true时,命中触发词直接执行;涉及删除、发送等危险操作时建议设为false,让 Agent 先确认。
3.4 SKILL.md 说明文档
--- name: weather-query description: 查询实时天气,支持任意城市 --- # 天气查询技能 ## 功能 - 查询指定城市实时天气 - 显示温度、体感、湿度、风力 ## 使用方式 当用户说“查询天气”“今天天气”时自动执行。 ## 配置 模型调用走 TaoToken 统一 Key,见 config.toml。3.5 主脚本 main.js
脚本负责两件事:读配置、调模型或调数据源、输出结果。下面这版把模型调用封装成统一函数,Key 从环境变量取。
#!/usr/bin/env node const https = require('https'); const fs = require('fs'); const path = require('path'); const CONFIG_PATH = path.join(__dirname, '..', 'config.toml'); const DEFAULT_CITY = '北京'; const TIMEOUT = 10000; function loadApiKey() { const key = process.env.TAOTOKEN_API_KEY; if (!key) { throw new Error('未找到 TAOTOKEN_API_KEY,请先设置环境变量'); } return key; } function fetchWeather(city) { return new Promise((resolve, reject) => { const url = `https://wttr.in/${encodeURIComponent(city)}?format=j1`; const req = https.get(url, { timeout: TIMEOUT }, (res) => { let data = ''; res.on('data', (chunk) => (data += chunk)); res.on('end', () => { try { const json = JSON.parse(data); const cur = json.current_condition[0]; resolve({ temp: cur.temp_C, feelsLike: cur.FeelsLikeC, humidity: cur.humidity, wind: cur.windspeedKmph, description: cur.weatherDesc[0].value, }); } catch (e) { reject(new Error('JSON 解析失败:' + e.message)); } }); }); req.on('error', reject); req.on('timeout', () => { req.destroy(); reject(new Error('请求超时')); }); }); } async function main() { const city = process.argv[2] || DEFAULT_CITY; console.log('查询城市:' + city); try { loadApiKey(); const w = await fetchWeather(city); console.log(`温度:${w.temp}°C`); console.log(`体感:${w.feelsLike}°C`); console.log(`湿度:${w.humidity}%`); console.log(`风力:${w.wind} km/h`); console.log(`描述:${w.description}`); console.log('查询完成'); } catch (e) { console.error('查询失败:' + e.message); process.exit(1); } } main().then(() => process.exit(0));这段脚本里loadApiKey()是统一 Key 的落点。以后你新增别的 Skill,只要复制这个函数,Key 来源始终是同一个环境变量。
4. 验证请求:注册 Skill 并跑通首次调用
4.1 本地直接运行
先不经过 Agent,直接跑脚本,确认数据源和 Key 都没问题:
node scripts/main.js 上海预期输出:
查询城市:上海 温度:22°C 体感:23°C 湿度:80% 风力:12 km/h 描述:小雨 查询完成如果这一步就报 Key 缺失,说明环境变量没生效,回到 3.2 重新设置。
4.2 重启 Openclaw 触发注册
Skill 的元数据是在 Openclaw 启动时扫描的,新增或修改_meta.json后必须重启:
openclaw restart重启后查看日志,确认扫描到了新 Skill:
[skills] loaded: weather-query (triggers: 4)4.3 在对话中触发
在 Openclaw 对话里输入“查询天气”,命中触发词后 Agent 会调用scripts/main.js,把结果返回。如果autoExecute为true,直接出结果;为false时会先问你确认。
4.4 用模型对话验证 Key 链路
如果你想单独确认 TaoToken 的 Key 在模型调用这一层是通的,可以走模型对话入口做一次最小请求。这一步和 Skill 执行是两条链路:Skill 负责业务动作,模型对话负责验证 Key 和基址。两者都通,整条调用链才算闭环。
5. 本篇常见错排查
5.1 Skill 不触发
按顺序检查:_meta.json里triggers是否拼写正确;触发词是否和用户输入有大小写或空格差异;autoExecute是否为true;修改后是否重启了 Openclaw;Skill 目录是否确实在skills/下。这五项里最常见的是忘记重启。
5.2 脚本执行报错
先看 Node 是否安装:node -v。再看路径,main字段是相对 Skill 目录的路径,写错会找不到入口。权限不足在 Linux 下用chmod +x scripts/main.js。环境变量没配会直接抛“未找到 TAOTOKEN_API_KEY”。
5.3 Key 读取失败
确认环境变量名和config.toml里的api_key_env完全一致。如果你在 IDE 里跑,IDE 可能没继承终端的环境变量,需要在运行配置里单独设置。用echo $TAOTOKEN_API_KEY先确认终端里能读到。
5.4 请求超时
数据源或模型接口超时,先调大timeout_ms。如果是网络层问题,检查是否能正常访问 API 基址。脚本里的TIMEOUT和config.toml的request_timeout_ms要匹配,避免一个 10 秒一个 30 秒导致行为不一致。
5.5 触发词误命中
触发词太短,比如只写“天气”,容易在无关对话里被命中。改成“查询天气”“今天天气怎么样”这类具体短语,3 到 5 个为宜。
6. 把 Key 收敛成一份,Skill 才能规模化
第一个 Skill 跑通之后,真正决定你后续效率的不是脚本写得多花哨,而是 Key 和配置有没有收敛。我试过把每个 Skill 的模型配置各写一份,结果换一次 Key 要改七八个文件,还漏过一个导致线上报错。后来统一成config.toml加环境变量的模式,新增 Skill 只复制骨架、改业务参数,模型层完全不用动。
如果你准备继续扩展,下一步可以给weather-query加多城市批量查询,或者把结果写进results/目录做历史记录。需要长期跑编码类或 Agent 类任务时,可以了解 Coding Plan 的额度模式;接入和排障过程中遇到 Key 或基址问题,直接对照 API Keys 和接入文档排查最快。把这份骨架复制过去,改掉城市名和触发词,你的第二个 Skill 基本就是十分钟的事。