OpenClaw 插件系统实战:default-processor 实现工具拦截与技能路由

前言

在 OpenClaw 的架构中,插件系统是扩展功能的核心机制。本文将分享我开发的 default-processor 插件,它实现了工具调用的拦截与路由,让技能可以接管内置工具的执行。

问题背景

在开发过程中,我遇到了几个痛点:

  1. 内置工具不稳定memory_search 在国内经常 403,无法正常使用
  2. TTS 需要云端:内置 TTS 走云端服务,延迟高且有隐私顾虑
  3. 无法灵活替换:想用自己的本地服务替代内置工具,但没有标准机制

解决方案:default-processor 插件

核心思路

通过 OpenClaw 的 before_tool_call 钩子,在工具调用前进行拦截,根据配置将请求路由到对应的技能脚本。

插件架构

extensions/default-processor/
├── package.json          # 插件配置
├── tsconfig.json         # TypeScript 配置
├── src/
│   └── index.ts         # 插件源码
└── dist/
    └── index.js         # 编译输出

关键代码

// 技能执行映射
const SKILL_EXECUTORS = {
  // memory_search -> memory-v2 技能
  'memory_search': async (params) => {
    const query = params.query || params.text || '';
    const script = `${WORKSPACE}/skills/memory-v2/scripts/hybrid-search.mjs`;
    const { stdout } = await execAsync(`node "${script}" "${query}"`);
    return { ok: true, result: stdout };
  },
  
  // tts -> voice-skill 本地 TTS
  'tts': async (params) => {
    const text = params.text || '';
    const script = `${WORKSPACE}/skills/voice-skill/scripts/tts-voice.sh`;
    const { stdout } = await execAsync(`bash "${script}" "${text}"`);
    return stdout.trim();
  },
};

插件注册

function register(api) {
  // 注册 before_tool_call 钩子
  api.on('before_tool_call', async (event, ctx) => {
    const { toolName, params } = event;
    const skillName = overrides[toolName];
    
    if (!skillName) return; // 无覆盖配置,使用内置工具
    
    const executor = SKILL_EXECUTORS[toolName];
    const result = await executor(params);
    
    // 返回结果,阻塞原工具调用
    return {
      result,
      block: !result.ok,
      blockReason: result.ok ? undefined : result.error
    };
  }, { priority: 100 });
}

配置方式

openclaw.json 中配置插件:

{
  "plugins": {
    "entries": {
      "default-processor": {
        "enabled": true,
        "config": {
          "overrides": {
            "memory_search": "memory-v2",
            "tts": "voice-skill"
          }
        }
      }
    }
  }
}

开发踩坑记录

1. TypeScript 编译问题

现象:切换模型时报错 Cannot read properties of undefined

原因:OpenClaw 加载插件时找不到编译后的 .js 文件,直接加载 .ts 失败

解决

  • 添加 tsconfig.json 配置
  • 编译输出到 dist/index.js
  • 更新 package.jsonmainopenclaw.extensions 指向编译后的文件

2. 参数格式兼容

现象:插件加载成功但工具调用报错

原因:官方插件用 parameters + @sinclair/typebox,我的插件用 inputSchema,格式不兼容

解决:统一使用 parameters 格式:

parameters: Type.Object({
  query: Type.String(),
})

3. 双目录配置

注意workspace/skills/default-processor-pluginextensions/default-processor 两个目录的 package.json 都要更新,确保一致。

实际效果

配置完成后:

工具原来现在
memory_search云端 API,经常 403本地向量数据库,毫秒响应
tts云端 TTS本地 MeloTTS,Intel GPU 加速

日志验证

[default-processor] Plugin loaded
[default-processor] Intercepting memory_search -> memory-v2
[default-processor] Skill memory-v2 executed: success

总结

default-processor 插件实现了:

  1. ✅ 工具调用拦截(before_tool_call 钩子)
  2. ✅ 技能路由(配置化映射)
  3. ✅ 本地服务替代(避免云端依赖)
  4. ✅ 零侵入切换(随时回退到内置工具)

这套机制让 OpenClaw 的扩展性大大提升,任何内置工具都可以被技能无缝替换。

参考

Built with Hugo
Theme Stack designed by Jimmy