OpenClaw 插件开发避坑指南

Hi,大家好 👋 今天来聊聊 OpenClaw 插件开发中那些让人头秃的坑 😅

作为过来人,我把踩过的坑整理成这份避坑指南,建议先收藏再看,防止以后用到时找不着 📌


1. outbound.sendText vs deliver 回调:别再傻傻分不清 🔀

😵 问题现象

这两个货太容易搞混了!用 sendText 回复用户消息、用 deliver 推送系统通知......结果消息跑错路,业务逻辑全乱套,调试调到头秃。

🔍 问题根源

它俩是两条完全不同的消息发送通道,井水不犯河水:

特性 deliver 回调 outbound.sendText 方法
触发时机 用户主动发消息 → 我来响应 系统主动推消息 → 不用等用户操作
典型场景 回复用户提问、反馈操作结果 系统通知、告警提示、定时推送
messageId 直接用用户原消息 ID,别自己造 手动生成,建议 Date.now().toString(36)
能力 支持工具调用、流式输出、多轮对话 只能发一次性文本,不支持流式

✅ 解决方案

一句话记住:用户主动找你的 → deliver;系统自己要推的 → sendText。

typescript 复制代码
// 🚀 sendText:主动推送(系统通知、告警等)
outbound: {
    sendText: async ({ to, text, accountId }) => {
         // 手动生成唯一ID
        const messageId = Date.now().toString(36);

        // 写到文件日志,方便排查
        const fs = require('fs');
        const logPath = path.join(__dirname, 'outbound.log');
        fs.appendFileSync(logPath,
            `[${new Date().toLocaleString()}] messageId: ${messageId} | 发给: ${to}\n`);

        await wsClient.send({ type: 'push', payload: { text, messageId, to } });
        return { channel: 'yeizi', ok: true, messageId };
    }
}
typescript 复制代码
// 🚀 deliver:被动响应(回复用户消息)
if (message.type === 'message' && message.text) {
    // 复用用户的ID,别自己造!
    const originalMessageId = message.messageId; 

    await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
        ctx: finalized,
        cfg,
        dispatcherOptions: {
            deliver: async (payload, info) => {
                await wsClient.send({
                    type: 'response',
                    payload: { content: payload.content, messageId: originalMessageId }
                });
            }
        }
    });
}

2. sendText 日志「消失」怎么办 📝

😵 问题现象

在 sendText 里写了 console.log,控制台毛都没有......函数到底有没有跑?参数对不对?完全不知道,排查问题全靠猜。

🔍 问题根源

sendText 是出站子系统异步调用的,跟主进程控制台不连通,console.log 相当于「对牛弹琴」。

✅ 解决方案

放弃 console.log,写文件日志才是正道:

typescript 复制代码
const fs = require('fs');
const logPath = path.join(__dirname, 'sendText-debug.log');

const log = (msg) => {
    fs.appendFileSync(logPath,
        `[${new Date().toISOString()}] ${msg}\n`, { encoding: 'utf8' });
};

log(`发送成功 | messageId: ${messageId} | 内容: ${text}`);

想看日志?type sendText-debug.log 或者直接打开文件,一目了然 👀


3. deliver 回调里抓取 AI 干活的全过程 🤖

😵 问题现象

想知道 AI 到底调用了哪个工具、返回了什么结果,可 deliver 回调触发好几次(工具调用、流式文本、最终回复全混在一起),根本分不清谁是谁。

🔍 问题根源

  1. 没开详细日志,OpenClaw 默认不输出工具调用信息
  2. deliver 回调返回的消息分三种类型,得靠 info.kind 来区分

✅ 解决方案

第一步:开启完整日志(在 openclaw.json 里)

json 复制代码
{
    "agents": {
        // 开启后能看到 AI 调工具的全过程
        "verboseDefault": "full" 
    }
}

第二步:靠 info.kind 区分消息类型

info.kind 意思是 能拿到的信息
tool AI 调用工具了 payload.content(工具返回结果)或 payload.error(报错信息)
block AI 正在「打字」中 payload.content(这波输出的文本片段)
final AI 终于说完了 payload.content(完整回复)

代码示例:

typescript 复制代码
deliver: async (payload, info) => {
    if (!info) return;

    switch (info.kind) {
        case 'tool':
            console.log('🛠️ 工具调用/返回:', payload.content || payload.error);
            break;
        case 'block':
            console.log('💬 AI 正在打字:', payload.content);
            break;
        case 'final':
            console.log('✅ AI 最终回复:', payload.content);
            break;
    }
}

💡 小技巧 :日志会同时写入 /var/log/openclaw/runtime.log,两个地方都可以看。


4. 多账户配置:鉴权鉴到怀疑人生怎么破 🔐

😵 问题现象

配置了 N 个账户,结果每个账户都独立鉴权、WebSocket 连接也各自建一个......服务器压力山大,连接还可能不稳定,鉴权失败的错误弹到麻木。

🔍 问题根源

没搞复用!每个账户都「自立门户」,鉴权、连接全重做了一遍。

✅ 解决方案

方案一:单账户走天下(推荐)

如果业务不需要严格区分用户身份,一个账户就能服务所有人,省心又省力。

方案二:共享连接池(多账户必用)

typescript 复制代码
// 全局就一个 WebSocket 连接,大家共用
let sharedWsConnection = null;

const getSharedConnection = async () => {
    if (!sharedWsConnection) {
        // 只有第一次需要鉴权创建连接
        sharedWsConnection = await createAndAuthenticateWs();
    }
    return sharedWsConnection;
};

gateway.startAccount = async (accountId) => {
    // 复用!不再重复鉴权
    const ws = await getSharedConnection();
    // 业务逻辑继续...
};

5. 插件依赖:别再重复安装内置包啦 📦

😵 问题现象

插件启动失败、版本冲突、包体积膨胀......一查,哦豁,dependencies 里写了 openclawtypescript,好家伙,这两个 OpenClaw 运行环境里已经有了!

🔍 问题根源

OpenClaw 内置的包(openclaw、typescript 等),别再 npm install 了,否则版本冲突没商量。

✅ 解决方案

❌ 错误示范(别学):

json 复制代码
{
    "dependencies": {
        // 千万别装!
        "openclaw": ">=2026.3.12",   
         // 千万别装!
        "typescript": "^5.0.0"       
    }
}

✅ 正确姿势:

json 复制代码
{
    "name": "@myorg/openclaw-yeizi",
    "version": "1.0.0",
    "type": "module",
    "peerDependencies": {
        // 声明版本即可,不实际安装
        "openclaw": ">=2026.3.13"    
    },
    "dependencies": {
        // 只装业务真正需要的
        "ws": "^8.16.0"              
    },
    "devDependencies": {
        // 开发用类型定义,可选
        "@types/ws": "^8.5.10"       
    }
}

验证方法:

  • node_modules 里没有 openclaw、typescript 文件夹 ✓
  • npm run start 能正常启动插件 ✓

🎉 祝大家插件开发顺利,少掉头发,多掉 bug(然后快速修好)💪

如果觉得有帮助,点个赞 👍 支持一下,也欢迎在评论区分享你的踩坑经历!

相关推荐
mldong5 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排5 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60616 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角7 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!7 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰7 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi9 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen9 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马10 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒10 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端