📋 目录

  1. OpenClaw 技能加载流程
  2. 简化版 AI 技能加载流程
  3. 兼容性分析
  4. 技能安装到加载完整流程
  5. 常见问题与解决方案

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。"
  ↓
用户:看到答案
Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐