OpenClaw技能安装加载流程详解
*** 构建完整的工具定义(基础工具 + 技能工具)*/// 1. 基础工具// [// ]// 2. 技能工具(从技能的 parameters 字段生成)return {required,},},});// 3. 合并所有工具// 基础工具(7 个)// 技能工具(根据安装的技能数量)...// 总计:7 + N 个工具。
📋 目录
1️⃣ OpenClaw 技能加载流程
1.1 技能目录结构
C:\Users\83760\.openclaw\workspace\skills\
├── weather/
│ ├── SKILL.md # 技能定义文件(必需)
│ ├── _meta.json # 元数据(可选)
│ ├── scripts/ # 脚本目录(可选)
│ │ └── search.py
│ └── references/ # 参考文档(可选)
├── baidu-search/
│ └── SKILL.md
└── ...
1.2 SKILL.md 文件格式
---
name: weather
description: Get current weather and forecasts (no API key required).
homepage: https://wttr.in/:help
metadata:
clawdbot:
requires:
bins: ["curl"]
parameters:
city:
type: string
description: 城市名称(英文,如 "Shanghai", "Beijing")
required: true
commands:
- template: "curl -s \"wttr.in/{city}?format=3\""
platform: auto
timeout: 10
examples:
- city: "Beijing"
- city: "Shanghai"
---
# Weather
Two free services, no API keys needed.
## wttr.in (primary)
Quick one-liner:
```bash
curl -s "wttr.in/London?format=3"
…(详细说明)
### 1.3 OpenClaw 加载流程
┌─────────────────────────────────────────────────┐
│ 1. OpenClaw 启动 │
│ - 读取配置文件 │
│ - 确定技能目录路径 │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 2. 扫描技能目录 │
│ - 遍历 skills/ 目录 │
│ - 查找包含 SKILL.md 的子目录 │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 3. 解析 SKILL.md │
│ - 读取 frontmatter(YAML) │
│ - 提取技能信息(name, description 等) │
│ - 解析 commands 数组 │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 4. 注册技能到工具系统 │
│ - 将技能转换为工具定义 │
│ - 添加到工具列表 │
│ - 大模型可以看到并使用 │
└─────────────────────────────────────────────────┘
### 1.4 核心代码(OpenClaw)
```javascript
// OpenClaw 核心代码(简化版)
async function loadSkills(skillsDir) {
const skills = [];
// 1. 扫描目录
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const skillDir = path.join(skillsDir, entry.name);
const skillMdPath = path.join(skillDir, 'SKILL.md');
// 2. 检查 SKILL.md 是否存在
if (!await fs.exists(skillMdPath)) continue;
// 3. 解析 SKILL.md
const skillContent = await fs.readFile(skillMdPath, 'utf-8');
const skill = parseSkillMd(skillContent);
skills.push(skill);
}
// 4. 注册到工具系统
registerSkillsAsTools(skills);
return skills;
}
2️⃣ 简化版 AI 技能加载流程
2.1 技能目录配置
// server.js
const WORKSPACE = path.join(__dirname, '..', '..');
// = C:\Users\83760\.openclaw\workspace
// assistant-v7.js 构造函数
this.skillsDir = path.join(workspace, '..', '..', 'skills');
// = C:\Users\83760\.openclaw\workspace\skills
2.2 简化版 AI 加载流程
/**
* 加载技能(动态加载 SKILL.md)
*/
async loadSkills() {
log(`\n 正在加载技能...`, 'info');
try {
// 1. 执行 skill_loader.py 扫描技能目录
const pythonPath = process.platform === 'win32'
? '"C:\\Python314\\python.exe"'
: 'python3';
const command = `${pythonPath} "${path.join(__dirname, 'core', 'skill_loader.py')}" "${this.skillsDir}"`;
const { stdout, stderr } = await execAsync(command, {
cwd: this.skillsDir,
encoding: 'utf-8',
maxBuffer: 10 * 1024 * 1024
});
// 2. 解析 Python 脚本返回的 JSON
const jsonMatch = stdout.match(/\{[\s\S]*"skills"[\s\S]*\}/);
if (jsonMatch) {
const data = JSON.parse(jsonMatch[0]);
// 3. 保存技能信息
for (const skill of data.skills || []) {
this.skills.push({
name: skill.name,
description: skill.description,
parameters: skill.parameters || {},
commands: skill.commands || [],
examples: skill.examples || [],
output_format: skill.output_format || '',
body: skill.body || '',
dir: skill.dir || '',
});
log(` 加载技能:${skill.name}`, 'info');
}
log(` 加载到 ${this.skills.length} 个技能`, 'info');
}
// 4. 更新工具定义(基础工具 + 技能工具)
this.toolDefinitions = this.buildToolDefinitions();
log(` 加载完成:${this.skills.length} 个技能`, 'info');
log(` 当前工具数量:${this.toolDefinitions.length}`, 'info');
} catch (error) {
log(` 技能加载失败:${error.message}`, 'error');
}
}
2.3 skill_loader.py 核心代码
# core/skill_loader.py
class SkillLoader:
def __init__(self, skills_dir: str):
self.skills_dir = skills_dir
self.skills = []
def scan_skills(self) -> List[Dict]:
"""扫描所有技能"""
self.skills = []
if not os.path.exists(self.skills_dir):
return self.skills
for item in os.listdir(self.skills_dir):
item_path = os.path.join(self.skills_dir, item)
# 跳过文件和隐藏目录
if not os.path.isdir(item_path) or item.startswith('.'):
continue
# 查找 SKILL.md
skill_md_path = os.path.join(item_path, 'SKILL.md')
if os.path.exists(skill_md_path):
skill = self._parse_skill_md(skill_md_path)
if skill:
self.skills.append(skill)
return self.skills
def _parse_skill_md(self, path: str) -> Optional[Dict]:
"""解析 SKILL.md 文件"""
with open(path, 'r', encoding='utf-8-sig') as f:
content = f.read()
# 提取 frontmatter(--- 之间的 YAML)
if content.startswith('---'):
parts = content.split('---', 2)
if len(parts) >= 3:
frontmatter_str = parts[1].strip()
frontmatter = yaml.safe_load(frontmatter_str) or {}
body = parts[2].strip() if len(parts) > 2 else ''
return {
'name': frontmatter.get('name', ''),
'description': frontmatter.get('description', ''),
'parameters': frontmatter.get('parameters', {}),
'commands': frontmatter.get('commands', []),
'examples': frontmatter.get('examples', []),
'body': body,
'dir': os.path.dirname(path),
}
return None
def build_tools_definition(self) -> List[Dict]:
"""构建工具定义(用于大模型工具调用)"""
tools = []
for skill in self.skills:
# 从 parameters 生成 JSON Schema
properties = {}
required = []
for param_name, param_def in skill.get('parameters', {}).items():
properties[param_name] = {
'type': param_def.get('type', 'string'),
'description': param_def.get('description', ''),
}
if param_def.get('required'):
required.append(param_name)
tools.append({
'type': 'function',
'function': {
'name': skill['name'],
'description': skill['description'],
'parameters': {
'type': 'object',
'properties': properties,
'required': required,
},
},
})
return tools
# 主程序
if __name__ == '__main__':
import sys
import json
skills_dir = sys.argv[1]
loader = SkillLoader(skills_dir)
skills = loader.scan_skills()
# 输出 JSON 格式
output = {
'skills': skills,
'tools': loader.build_tools_definition(),
}
print(json.dumps(output, indent=2, ensure_ascii=False))
2.4 工具定义构建
/**
* 构建完整的工具定义(基础工具 + 技能工具)
*/
buildToolDefinitions() {
// 1. 基础工具
const baseTools = this.getBaseToolDefinitions();
// [
// { name: "read_file", ... },
// { name: "write_file", ... },
// { name: "run_command", ... },
// { name: "list_dir", ... },
// { name: "read_excel", ... },
// { name: "web_search", ... },
// { name: "web_fetch", ... }
// ]
// 2. 技能工具(从技能的 parameters 字段生成)
const skillTools = this.skills.map(skill => {
const properties = {};
const required = [];
for (const [paramName, paramDef] of Object.entries(skill.parameters || {})) {
properties[paramName] = {
type: paramDef.type || 'string',
description: paramDef.description || '',
};
if (paramDef.required) {
required.push(paramName);
}
}
return {
type: 'function',
function: {
name: skill.name,
description: skill.description,
parameters: {
type: 'object',
properties,
required,
},
},
};
});
// 3. 合并所有工具
return baseTools.concat(skillTools);
}
2.5 技能执行流程
/**
* 通用技能执行器
*/
async executeSkill(skill, args) {
log(` 执行技能:${skill.name}`, 'tool');
// 1. 动态读取 SKILL.md 获取完整指令
const skillMdPath = path.join(skill.dir, 'SKILL.md');
const skillContent = await fs.readFile(skillMdPath, 'utf-8');
// 2. 解析命令模板(优先级:新格式 > 正文 template > 官方格式)
let commandTemplate = skill.commands?.[0]?.template;
if (!commandTemplate && skillContent) {
// 尝试从正文解析 template: 字段
const templateMatch = skillContent.match(/template:\s*["']([^"']+)["']/);
if (templateMatch) {
commandTemplate = templateMatch[1];
}
}
if (!commandTemplate && skillContent && skill.commands?.length > 0) {
// 官方格式:从 ```bash 代码块提取
const bashMatch = skillContent.match(/```bash\s*([\s\S]*?)```/);
if (bashMatch) {
const bashBlock = bashMatch[1].trim();
const lines = bashBlock.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#')) {
commandTemplate = trimmed.split('#')[0].trim();
break;
}
}
}
}
if (!commandTemplate) {
return `技能 ${skill.name} 没有定义可执行的命令模板`;
}
// 3. 替换模板参数
let commandStr = commandTemplate;
for (const [key, value] of Object.entries(args)) {
commandStr = commandStr.replace(new RegExp(`{${key}}`, 'g'), value);
// 处理 {key_str} 格式(用于 JSON 拼接)
if (value) {
commandStr = commandStr.replace(
new RegExp(`{${key}_str}`, 'g'),
`, "${key}": ${typeof value === 'string' ? `"${value}"` : value}`
);
} else {
commandStr = commandStr.replace(new RegExp(`{${key}_str}`, 'g'), '');
}
}
// 4. 执行命令
const result = await execAsync(commandStr, {
encoding: 'utf-8',
timeout: timeout * 1000,
maxBuffer: 10 * 1024 * 1024
});
return `技能 ${skill.name} 执行结果:\n\n${result.stdout || result.stderr}`;
}
3️⃣ 兼容性分析
3.1 OpenClaw 官方技能格式
---
name: stock-analysis
description: Analyze stocks and cryptocurrencies...
commands:
- /stock - Analyze a stock or crypto
- /stock_compare - Compare multiple tickers
- /stock_dividend - Analyze dividend metrics
---
# Stock Analysis
## Quick Commands
```bash
uv run {baseDir}/scripts/analyze_stock.py AAPL
### 3.2 简化版 AI 支持的格式
| 格式类型 | OpenClaw | 简化版 AI | 兼容性 |
|---------|---------|---------|--------|
| **commands 数组** | 字符串数组 | 支持 | 兼容 |
| **commands 对象** | 不支持 | 支持 | 扩展 |
| **parameters** | 支持 | 支持 | 兼容 |
| **正文 bash 代码块** | 支持 | 支持 | 兼容 |
| **正文 template 字段** | 不支持 | 支持 | 扩展 |
### 3.3 兼容性结论
**简化版 AI 完全兼容 OpenClaw 官方技能!**
**原因**:
1. 支持 OpenClaw 标准格式(commands 字符串数组 + bash 代码块)
2. 支持扩展格式(commands 对象 + template 字段)
3. 支持 parameters 定义
4. 使用相同的 SKILL.md 文件结构
---
## 技能安装到加载完整流程
### 4.1 完整流程图
┌─────────────────────────────────────────────────┐
│ 阶段 1:技能安装 │
│ (通过 skillhub 或桌面客户端) │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 1. 用户点击"安装"按钮 │
│ - 桌面客户端或 skillhub CLI │
│ - 命令:skillhub install │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 2. skillhub 下载技能 │
│ - 从技能仓库下载 │
│ - 解压到 skills/ 目录 │
│ - 路径:C:\Users\83760.openclaw\workspace\skills<skill>/ │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 3. 技能文件结构 │
│ - SKILL.md(必需) │
│ - scripts/(可选) │
│ - references/(可选) │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 阶段 2:简化版 AI 启动 │
│ (node src/server.js) │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 4. 初始化 AI 助手 │
│ - new SimpleAIAssistant(API_KEY, WORKSPACE) │
│ - 设置 skillsDir │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 5. 调用 init() 方法 │
│ - await this.loadSkills() │
│ - await this.loadMemoryToCache() │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 6. 执行 skill_loader.py │
│ - Python 脚本扫描 skills/ 目录 │
│ - 解析每个 SKILL.md 文件 │
│ - 返回 JSON 格式的技能列表 │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 7. 解析 Python 返回的 JSON │
│ - 提取 skills 数组 │
│ - 保存到 this.skills │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 8. 构建工具定义 │
│ - buildToolDefinitions() │
│ - 基础工具(7 个)+ 技能工具(N 个) │
│ - 保存到 this.toolDefinitions │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 9. 构建系统提示词 │
│ - buildSystemPrompt() │
│ - 包含工具列表 │
│ - 大模型可以看到所有可用工具 │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 阶段 3:用户使用 │
│ (通过 Web 界面或飞书) │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 10. 用户提问 │
│ - “深圳今天天气怎么样” │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 11. 大模型分析意图 │
│ - 识别需要调用 weather 技能 │
│ - 生成工具调用: │
│ { “name”: “weather”, “arguments”: { │
│ “city”: “Shenzhen” │
│ } } │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 12. 执行技能 │
│ - executeSkill(skill, args) │
│ - 解析 SKILL.md 中的命令模板 │
│ - 替换参数:{city} → Shenzhen │
│ - 执行:curl -s “wttr.in/Shenzhen?format=3” │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 13. 返回结果 │
│ - 技能执行结果 │
│ - 大模型生成自然语言回复 │
│ - 用户看到答案 │
└─────────────────────────────────────────────────┘
### 4.2 详细步骤说明
#### 步骤 1-3:技能安装
```bash
# 方式 1:使用 skillhub CLI
skillhub install weather --dir C:\Users\83760\.openclaw\workspace\skills
# 方式 2:使用桌面客户端
# 点击"安装"按钮 → 后台执行 skillhub install
# 安装后的目录结构
C:\Users\83760\.openclaw\workspace\skills\weather\
├── SKILL.md
├── _meta.json
└── scripts/
└── search.py
步骤 4-5:AI 初始化
// server.js
const WORKSPACE = path.join(__dirname, '..', '..');
// = C:\Users\83760\.openclaw\workspace
const assistant = new SimpleAIAssistant(API_KEY, WORKSPACE);
await assistant.init();
步骤 6-7:加载技能
# 执行的 Python 命令
C:\Python314\python.exe "C:\Users\83760\.openclaw\workspace\simple-ai\src\core\skill_loader.py" "C:\Users\83760\.openclaw\workspace\skills"
# Python 输出(JSON)
{
"skills": [
{
"name": "weather",
"description": "Get current weather and forecasts...",
"parameters": {
"city": {
"type": "string",
"description": "城市名称(英文)",
"required": true
}
},
"commands": [
{
"template": "curl -s \"wttr.in/{city}?format=3\"",
"platform": "auto",
"timeout": 10
}
],
"dir": "C:\\Users\\83760\\.openclaw\\workspace\\skills\\weather"
}
],
"tools": [...]
}
步骤 8:构建工具定义
// 基础工具(7 个)
[
{ name: "read_file", ... },
{ name: "write_file", ... },
{ name: "run_command", ... },
{ name: "list_dir", ... },
{ name: "read_excel", ... },
{ name: "web_search", ... },
{ name: "web_fetch", ... }
]
// 技能工具(根据安装的技能数量)
[
{ name: "weather", ... },
{ name: "baidu-search", ... },
{ name: "stock-analysis", ... },
...
]
// 总计:7 + N 个工具
步骤 9:构建系统提示词
## 你有以下工具
- read_file: 读取文件内容
- write_file: 创建或覆盖文件
- run_command: 执行 shell 命令
- list_dir: 列出目录内容
- read_excel: 读取 Excel 文件
- web_search: 使用 DuckDuckGo 搜索网络
- web_fetch: 抓取网页内容
- weather: Get current weather and forecasts(参数:city(必填 - 城市名称))
- baidu-search: Search the web using Baidu AI Search Engine(参数:query(必填 - 搜索关键词))
- stock-analysis: Analyze stocks and cryptocurrencies...(无参数)
...
步骤 10-13:用户使用
用户:深圳今天天气怎么样
↓
大模型:分析意图 → 需要调用 weather 技能
↓
工具调用:
{
"name": "weather",
"arguments": { "city": "Shenzhen" }
}
↓
执行技能:
1. 读取 SKILL.md
2. 解析命令模板:curl -s "wttr.in/{city}?format=3"
3. 替换参数:curl -s "wttr.in/Shenzhen?format=3"
4. 执行命令(安全检查通过)
5. 返回结果:shenzhen: +25°C
↓
大模型:生成回复
"深圳今天天气晴朗,气温 25°C。"
↓
用户:看到答案
更多推荐



所有评论(0)