郑工长

用飞书做 OpenClaw 通道?有一个坑 98% 的人都没注意!

发布于 2026年2月23日 | 分类: AI随心分享

用飞书做 OpenClaw 通道?有一个坑 98% 的人都没注意!

你好,我是郑工长。

今天OpenClaw的飞书通道突然不能用了,主要是表现通过飞书与Agent沟通时没有反映!查看飞书开发平台的日志信息,报错"本月 API 配额已耗尽"。

其实,中午就收到了一条信息,但没有注意。

查了飞书开放平台的 API 调用日志,发现一个反直觉的现象:

真正消耗配额的,不是消息发送,而是每分钟一次的"健康检查"。

即使你一天只发几条消息,健康检查也会默默消耗掉 4.3 万次 API 调用/月(单账号),而飞书企业自建应用的免费配额只有 1 万次/月。

这意味着什么?意味着即使你什么都不做,只要 OpenClaw 在运行,你的 API 配额就在被疯狂消耗。

说穿了,这是一个典型的"后台任务无意识调用"问题。

我花了一个下午,在飞书插件中添加了缓存机制。效果很直接:

  • 优化前:43,200 次/月(单账号)
  • 优化后:720 次/月(单账号)
  • 减少比例:98.3%

下面是完整的排查过程和解决方案。如果你也在用飞书作为 OpenClaw 通道,建议立即检查。


现象

发现 OpenClaw 的飞书机器人 API 调用配额在每月还未过完就已经耗尽。错误信息如下:

99991403	This month's API call quota has been exceeded

飞书 API 配额限制

根据飞书开放平台文档,企业自建应用默认 API 配额为 10,000次/月,每月1日重置。

初步排查:误判为 ackReaction

最初怀疑是 OpenClaw 的 ackReaction 功能(消息确认表情)导致的 API 调用。检查配置发现:

"messages": {
  "ackReactionScope": "group-mentions"
}

将该配置改为 direct 后,API 调用确实减少,但问题依然存在。

真正的原因:健康检查

通过飞书开放平台后台的 API 调用日志分析,发现大量调用来自:

POST /open-apis/im/v1/messages/{id}/reactions  (已被排除)
GET  /open-apis/bot/v3/info                    ← 真正元凶

调用链路

OpenClaw Gateway
    ↓ 每分钟健康检查 (health check)
调用 plugins 的 status.probeAccount()
    ↓
probeFeishu() → 调用飞书 API /open-apis/bot/v3/info
    ↓
HTTP 请求到飞书服务器

配额计算

飞书账号数 调用频率 每月 API 调用次数
1个 每分钟1次 43,200 次/月
2个 每分钟1次 86,400 次/月

这就是问题所在:即使只有一个飞书通道,每月的健康检查调用也会消耗掉 4.3 万次 API 调用,远超 1 万次的免费配额!


解决方案

方案选择

方案 优点 缺点
申请更多配额 一劳永逸 需要审核,可能需要付费
等待每月重置 无需操作 治标不治本
添加缓存机制 免费、立即生效 每次升级需重新应用

最终选择方案三:在飞书插件中添加缓存机制,减少重复的 API 调用。

实现原理

probeFeishu() 函数中添加内存缓存:

  1. 首次调用:请求飞书 API,结果存入缓存
  2. 缓存有效期内:直接返回缓存结果,不调用 API
  3. 缓存过期:重新请求飞书 API,更新缓存

缓存策略

状态 缓存时间 说明
成功 60 分钟 大幅减少 API 调用
失败 5 分钟 避免立即重试失败请求

效果预估

飞书账号数 优化前 优化后 减少比例
1个 43,200 次/月 720 次/月 98.3%
2个 86,400 次/月 1,440 次/月 98.3%

操作指南

步骤一:定位 OpenClaw 安装路径

# 查看 openclaw 全局安装路径
npm list -g openclaw

输出示例:

/Users/username/.nvm/versions/node/v22.22.0/lib
└── openclaw@2026.2.25

记下路径,后面的操作会用到。

步骤二:备份原文件

在修改前,务必备份原始文件:

# 替换为你的实际路径
cp ~/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/extensions/feishu/src/probe.ts \
   ~/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/extensions/feishu/src/probe.ts.backup

步骤三:修改 probe.ts

找到飞书插件的 probe.ts 文件:

# 路径格式:<你的nvm路径>/lib/node_modules/openclaw/extensions/feishu/src/probe.ts
nano ~/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/extensions/feishu/src/probe.ts

将文件内容替换为以下代码:

import { createFeishuClient, type FeishuClientCredentials } from "./client.js";
import type { FeishuProbeResult } from "./types.js";

// 缓存配置:60分钟缓存,减少API调用
const DEFAULT_TTL = 60 * 60 * 1000; // 60分钟 = 1小时
const cache = new Map<string, { result: FeishuProbeResult; expires: number }>();

export async function probeFeishu(creds?: FeishuClientCredentials): Promise<FeishuProbeResult> {
  if (!creds?.appId || !creds?.appSecret) {
    return {
      ok: false,
      error: "missing credentials (appId, appSecret)",
    };
  }

  const cacheKey = creds.appId;

  // 检查缓存是否有效
  const cached = cache.get(cacheKey);
  if (cached && Date.now() < cached.expires) {
    return cached.result;
  }

  try {
    const client = createFeishuClient(creds);
    // Use bot/v3/info API to get bot information
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- SDK generic request method
    const response = await (client as any).request({
      method: "GET",
      url: "/open-apis/bot/v3/info",
      data: {},
    });

    let result: FeishuProbeResult;

    if (response.code !== 0) {
      result = {
        ok: false,
        appId: creds.appId,
        error: `API error: ${response.msg || `code ${response.code}`}`,
      };
    } else {
      const bot = response.bot || response.data?.bot;
      result = {
        ok: true,
        appId: creds.appId,
        botName: bot?.bot_name,
        botOpenId: bot?.open_id,
      };
    }

    // 将结果存入缓存(无论成功或失败都缓存,失败时可以减少重复请求)
    cache.set(cacheKey, {
      result,
      expires: Date.now() + DEFAULT_TTL,
    });

    return result;
  } catch (err) {
    const result: FeishuProbeResult = {
      ok: false,
      appId: creds.appId,
      error: err instanceof Error ? err.message : String(err),
    };

    // 失败时也缓存结果(短期缓存5分钟),避免立即重试
    cache.set(cacheKey, {
      result,
      expires: Date.now() + (5 * 60 * 1000), // 失败缓存5分钟
    });

    return result;
  }
}

步骤四:重启 OpenClaw

修改完成后,重启 Gateway 使修改生效:

openclaw gateway restart

步骤五:验证

检查 Gateway 状态:

openclaw status

确认运行正常后,可以去飞书开放平台后台观察 API 调用次数,应该会大幅下降。

由于现在已经超出额度,根据调整,现在API请求失败后是5分钟再请求1次,是正常的。等到月初额度恢复后,成功请求就会变成60分钟1次


配置说明

缓存时间调整

如果需要调整缓存时间,修改 DEFAULT_TTL 值:

// 30分钟缓存
const DEFAULT_TTL = 30 * 60 * 1000;

// 60分钟缓存(默认)
const DEFAULT_TTL = 60 * 60 * 1000;

// 2小时缓存
const DEFAULT_TTL = 2 * 60 * 60 * 1000;

缓存时间建议

使用场景 推荐缓存时间 每月 API 调用(2个账号)
个人使用,能接受1小时延迟 60分钟 1,440 次
对实时性要求较高 30分钟 2,880 次
极致节省 120分钟 720 次

维护注意事项

1. 升级会覆盖修改

重要:每次 OpenClaw 升级都会覆盖 probe.ts 文件,导致优化失效。

解决方案

  • 升级后检查该文件是否被还原
  • 如被还原,重复「操作指南」中的步骤二到步骤四

2. 建立升级检查清单

建议在每次升级 OpenClaw 时,执行以下检查:

# 检查 probe.ts 是否被还原(如果输出为空,说明被还原了)
grep "DEFAULT_TTL" ~/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/extensions/feishu/src/probe.ts

# 如果被还原,重新应用修改
# (参考「操作指南」步骤二到步骤四)

3. 备份文件管理

建议保留原始备份文件:

# 查看备份文件
ls -la ~/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/extensions/feishu/src/probe.ts.backup

4. 监控 API 调用

即使使用了缓存,仍建议定期监控飞书 API 调用情况:

  1. 登录飞书开放平台:https://open.feishu.cn/
  2. 进入「应用详情」→「API 调用统计」
  3. 确认调用量是否在预期范围内

进阶方案

如果不想每次升级都手动修改,可以考虑以下方案:

方案一:fork 飞书插件

  1. 在 GitHub 上 fork OpenClaw 的飞书插件仓库
  2. 应用缓存修改
  3. 使用本地路径加载插件(需要修改 OpenClaw 配置)

方案二:向官方提交 PR

  1. 在 OpenClaw 仓库提交 Issue,说明问题
  2. 建议官方添加缓存机制
  3. 等待官方合并 PR

方案三:申请更多配额

如果预算允许,可以去飞书开放平台申请更多 API 配额:

  1. 登录 https://open.feishu.cn/
  2. 进入「应用详情」→「API 配额」
  3. 点击「申请提升配额」
  4. 填写使用场景和需求

常见问题

Q1:修改后Gateway启动失败?

检查 TypeScript 语法是否正确,确保文件保存成功。可以对比备份文件:

diff ~/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/extensions/feishu/src/probe.ts \
     ~/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/extensions/feishu/src/probe.ts.backup

Q2:缓存时间可以设为无限吗?

不建议。长期缓存会导致飞书账号状态变化(如 token 过期)无法被及时发现,影响机器人正常运行。

Q3:会影响消息收发吗?

不会。缓存只影响「健康检查」API,消息的收发不受影响。即使缓存过期,健康检查失败也不会阻断消息功能。


参考资料