OpenClaw技能开发入门:为Qwen3-4B-Thinking定制私人助手

1. 为什么需要定制OpenClaw技能

去年冬天,我发现自己每天早晨都要重复同样的动作:打开浏览器→搜索"北京天气"→截图发到家庭群。这种机械操作持续两周后,我决定用OpenClaw实现自动化。但现成技能库里没有天气查询模块,这促使我踏上了技能开发之路。

OpenClaw真正的魅力在于它的可扩展性。通过开发自定义技能,我们可以让AI助手真正理解个人工作流中的特殊需求。比如在我的案例中,不仅需要获取天气数据,还要能自动识别家庭成员所在城市,并按特定格式生成带表情的温馨提醒。

2. 开发环境准备

2.1 基础工具链配置

我的开发环境是macOS + VS Code,关键组件包括:

# 确认Node.js版本(要求18+)
node -v
# v20.3.1

# 全局安装ClawDev工具包
npm install -g clawdev@latest

建议在~/.openclaw/dev目录建立技能开发工作区,这个位置不会被OpenClaw的常规更新覆盖。我创建了专属的天气技能目录结构:

weather-skill/
├── package.json
├── src/
│   ├── index.js
│   └── weather.js
├── test/
│   └── weather.test.js
└── README.md

2.2 模型服务对接

由于要使用Qwen3-4B-Thinking模型处理自然语言请求,需要先在OpenClaw配置文件中添加模型端点。这是我的~/.openclaw/openclaw.json配置片段:

"models": {
  "providers": {
    "local-qwen": {
      "baseUrl": "http://localhost:8000/v1",
      "apiKey": "sk-no-key-required",
      "api": "openai-completions",
      "models": [
        {
          "id": "Qwen3-4B-Thinking",
          "name": "本地Qwen思考版",
          "contextWindow": 32768
        }
      ]
    }
  }
}

配置完成后,记得用openclaw gateway restart重启服务使配置生效。

3. 天气预报技能开发实战

3.1 需求拆解与技术选型

一个完整的天气查询技能需要处理以下环节:

  1. 地址识别(如"上海"或"朝阳区")
  2. 数据获取(选择免费天气API)
  3. 结果格式化(模型喜欢的结构化数据)
  4. 异常处理(网络错误、地址模糊等)

经过对比,我选择心知天气API作为数据源,它的免费版完全够用。更关键的是,它的返回格式简洁,适合直接喂给大模型处理。

3.2 核心代码实现

src/weather.js中,我实现了基础查询类:

const axios = require('axios');

class WeatherFetcher {
  constructor(apiKey) {
    this.apiKey = apiKey || process.env.XZ_WEATHER_KEY;
    this.baseUrl = 'https://api.seniverse.com/v3';
  }

  async getByLocation(location) {
    try {
      const response = await axios.get(`${this.baseUrl}/weather/now.json`, {
        params: {
          key: this.apiKey,
          location: location,
          language: 'zh-Hans',
          unit: 'c'
        }
      });
      return this._formatResponse(response.data);
    } catch (error) {
      console.error('天气查询失败:', error);
      return { error: this._parseError(error) };
    }
  }

  _formatResponse(data) {
    const result = data.results[0];
    return {
      location: result.location.name,
      temperature: result.now.temperature,
      condition: result.now.text,
      lastUpdate: result.last_update
    };
  }
}

module.exports = WeatherFetcher;

3.3 与模型协同工作

真正的魔法发生在index.js,这里实现模型与技能的对接:

const WeatherFetcher = require('./weather');
const { Tool } = require('clawdev');

module.exports = class WeatherSkill extends Tool {
  constructor() {
    super({
      name: 'weather',
      description: '获取指定城市的实时天气信息',
      parameters: {
        type: 'object',
        properties: {
          location: {
            type: 'string',
            description: '城市名称,如"北京"或"New York"'
          }
        },
        required: ['location']
      }
    });
    this.fetcher = new WeatherFetcher();
  }

  async execute(args) {
    const rawData = await this.fetcher.getByLocation(args.location);
    
    // 让模型加工原始数据
    const prompt = `将以下天气数据转换为自然语言描述:
    地点:${rawData.location}
    温度:${rawData.temperature}℃
    天气状况:${rawData.condition}
    最后更新时间:${rawData.lastUpdate}
    
    要求:用中文生成一段温馨的天气提示,包含适当的emoji(不要用表情符号文字)`;
    
    const modelResponse = await this.openclaw.models.chat({
      model: 'Qwen3-4B-Thinking',
      messages: [{ role: 'user', content: prompt }]
    });
    
    return modelResponse.choices[0].message.content;
  }
};

这种设计模式让模型专注于它擅长的自然语言生成,而技能处理确定性的数据获取和转换。

4. 测试与调试技巧

4.1 单元测试配置

我在test/weather.test.js中编写了基础测试用例:

const WeatherFetcher = require('../src/weather');
const mockAdapter = require('axios-mock-adapter');
const axios = require('axios');

describe('WeatherFetcher', () => {
  let mock;
  
  beforeAll(() => {
    mock = new mockAdapter(axios);
  });
  
  it('应正确格式化API响应', async () => {
    mock.onGet(/weather\/now.json/).reply(200, {
      results: [{
        location: { name: "北京" },
        now: { temperature: "22", text: "晴" },
        last_update: "2024-03-20T08:00:00+08:00"
      }]
    });
    
    const fetcher = new WeatherFetcher('test_key');
    const result = await fetcher.getByLocation('北京');
    
    expect(result).toEqual({
      location: '北京',
      temperature: '22',
      condition: '晴',
      lastUpdate: '2024-03-20T08:00:00+08:00'
    });
  });
});

使用jest运行测试时,记得设置环境变量:

XZ_WEATHER_KEY=mock_key jest weather.test.js

4.2 真实环境调试

开发过程中最实用的调试方法是使用OpenClaw的REPL模式:

openclaw repl --skill ./weather-skill

在REPL中可以直接调用技能方法,实时观察模型交互:

const weather = require('./index');
const instance = new weather();
await instance.execute({ location: "上海" });

5. 打包与发布

5.1 创建技能包

完整的package.json配置示例:

{
  "name": "@yourname/weather-skill",
  "version": "0.1.0",
  "description": "OpenClaw天气查询技能",
  "main": "src/index.js",
  "scripts": {
    "test": "jest"
  },
  "dependencies": {
    "axios": "^1.6.2"
  },
  "devDependencies": {
    "clawdev": "^0.8.0",
    "jest": "^29.7.0",
    "axios-mock-adapter": "^1.22.0"
  },
  "claw": {
    "type": "skill",
    "categories": ["productivity"],
    "compatibility": ["openclaw>=0.12.0"]
  }
}

使用npm pack命令生成.tgz发布包,这个包可以直接通过OpenClaw安装。

5.2 发布到ClawHub

如果想分享给社区,可以发布到ClawHub:

clawhub login
clawhub publish --category productivity

发布后其他用户可以通过简单命令安装你的技能:

clawhub install @yourname/weather-skill

6. 进阶开发建议

在完成基础版本后,我陆续为天气技能添加了这些增强功能:

  • 多日预报查询
  • 空气质量指数整合
  • 根据天气自动生成穿衣建议
  • 地理位置模糊匹配(如"我家附近")

一个实用的技巧是让技能支持渐进式信息获取。当用户只说"天气"时,技能会通过模型反问"您想查询哪个城市的天气?",这种交互模式比直接报错友好得多。

开发过程中最深的体会是:好的技能设计应该像乐高积木,每个功能块保持独立且可组合。我的天气技能现在可以单独使用,也能作为子模块被其他技能(如出行规划)调用。这种设计思路极大提高了代码的复用率。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐