# 飞书 API 频率限制排查报告

**排查时间**: 2026-03-01
**排查范围**: `/root/.openclaw/` 及 openclaw 源码

---

## 结论（摘要）

**根本原因：Streaming Card（流式卡片）API 调用频率过高**

当飞书 channel 处于启用状态（`enabled: true`）时，每次 AI 回复都会触发一个 `FeishuStreamingSession`，在 AI 生成内容期间以 **最高 10 次/秒** 的速率向飞书 CardKit API 发送更新请求（`PUT /cardkit/v1/cards/{id}/elements/content/content`）。

对一个持续 60 秒的 AI 回复，单次对话就可产生最多 **600 次** CardKit API 请求。结合日志中显示的 12 条去重入站消息，总请求量可能高达 **数千次**，远超飞书的配额上限。

---

## 证据链

### 1. 去重文件（确认入站消息量）

**文件**: `/root/.openclaw/feishu/dedup/default.json`

记录了 **12 条**唯一入站消息（时间在 2026-02-27 至 2026-02-28 之间），说明飞书 channel 启用期间有真实的用户消息被处理。每条消息都触发了完整的 streaming 流程。

### 2. 历史配置（确认飞书曾处于启用状态）

**文件**: `/root/.openclaw/openclaw.json.bak.20260227`

```json
"feishu": {
  "enabled": true,    ← 2026-02-27 飞书处于启用状态
  "appId": "cli_a914fa42ddb81ced",
  "appSecret": "...",
  "domain": "feishu",
  "groupPolicy": "open"
  ← 注意：没有 "streaming": false 或 "renderMode": "raw"
}
```

无 streaming 配置 → 默认开启流式卡片（`account.config?.streaming !== false` → true）

### 3. Cron 任务投递失败（API 限流的间接证据）

**文件**: `/root/.openclaw/cron/runs/a3a73d1b-9aab-4230-a5a9-be9b2699a070.jsonl`

```
第 1 次运行（2026-02-27）: status="error", error="cron announce delivery failed"
第 2 次运行（2026-02-28）: status="error", error="cron: job execution timed out"
```

第 1 次运行的 "cron announce delivery failed" 意味着向飞书发送每日活动报告时失败。这与飞书 API 在 streaming 消耗大量配额后被限流的时机高度吻合。

---

## 根本原因详解

### 触发流程（feishu enabled 时）

每收到一条飞书入站消息 → `handleFeishuMessage()` → `createFeishuReplyDispatcher()` → 创建 `FeishuStreamingSession`

```
流式卡片 API 调用序列（每次 AI 回复）:
  1. POST /auth/v3/tenant_access_token/internal   ← 获取 token（有缓存，约每2小时1次）
  2. POST /cardkit/v1/cards                        ← 创建卡片（1次）
  3. im.message.create                             ← 发送卡片消息（1次）
  4. PUT  /cardkit/v1/cards/{id}/elements/content  ← 流式更新内容 ← ⚠️ 主要问题
                                                      节流：100ms = 最多 10次/秒
                                                      60秒回复 = 最多 600次请求
  5. PATCH /cardkit/v1/cards/{id}/settings         ← 关闭流式模式（1次）
```

**关键代码位置**:
`/root/.nvm/.../openclaw/extensions/feishu/src/streaming-card.ts`

```typescript
// 节流设置：仅 100ms = 最多 10次/秒
private updateThrottleMs = 100;
```

飞书 CardKit 流式更新接口的频率限制通常远低于 10次/秒（文档建议约 2-5次/秒每卡片，或按分钟计的 app 级限额）。

### 触发条件（`renderMode: "auto"` 默认）

```typescript
// reply-dispatcher.ts
function shouldUseCard(text: string): boolean {
  // 有代码块或 Markdown 表格时触发 streaming card
  return /```[\s\S]*?```/.test(text) || /\|.+\|[\r\n]+\|[-:| ]+\|/.test(text);
}
```

当 AI 回复包含代码块或 Markdown 表格时（几乎所有技术性回复），都会触发 streaming card。

---

## 次要 API 调用（每条消息额外消耗）

| 操作 | 接口 | 次数/消息 |
|------|------|-----------|
| Typing 指示器（开始） | `im.messageReaction.create` | 1 |
| 发送者名称查询 | `contact.user.get` | 1（首次，缓存10分钟）|
| 引用消息获取（如有回复） | `im.message.get` | 0-1 |
| 发送最终消息 | `im.message.create/reply` | 1 |
| Typing 指示器（结束） | `im.messageReaction.delete` | 1 |

每条消息额外约 **4-5 次** API 调用（相比 streaming 的影响较小）。

---

## 当前状态

```
channels.feishu.enabled = false  ← 飞书 channel 已禁用
plugins.entries.feishu.enabled = true  ← 插件仍启用（用于出站消息工具）
```

- **当前不触发 streaming**：飞书 channel 禁用 → 无入站 → 无 FeishuStreamingSession
- **当前仍有出站消息**：agent 通过 `message` 工具发送飞书消息（每次调用 1 次 API）
- **Cron 投递**：2 个 cron 任务每日通过飞书 announce 投递（每次触发约 1-2 次 API 调用）

**待触发风险**：若重新启用飞书 channel（`enabled: true`），问题会立即复现。

---

## 与 `ackReaction: "eyes"` + `ackReactionScope: "all"` 的关系

当前配置：
```json
"messages": {
  "ackReaction": "eyes",
  "ackReactionScope": "all"
}
```

**飞书不使用此配置**。
查阅 `bot.ts` 确认：飞书的 `handleFeishuMessage` 只使用 typing indicator（`im.messageReaction.create/delete`），不调用 `addReactionFeishu` 来添加"eyes"回应。
`ackReaction` 配置主要作用于 Slack channel（会给所有 Slack 消息加"eyes"表情）。

---

## 修复建议（仅供参考，不修改原文件）

**方案 A（最直接）**：在飞书 channel 配置中禁用 streaming

```json
// openclaw.json 中 channels.feishu 添加：
"renderMode": "raw"
// 或
"streaming": false
```

效果：AI 回复直接以普通消息发送，不创建 streaming card，CardKit API 调用归零。

**方案 B（降低更新频率）**：
如需保留 streaming 效果，可将 `updateThrottleMs` 从 100ms 提高到 1000ms+（最多 1次/秒），大幅降低 CardKit API 的调用量。但这需要修改 `streaming-card.ts`。

**方案 C（限流重试）**：
在 `updateCardContent` 中捕获 429 错误后指数退避，而不是静默忽略。

---

## 排查过程中查阅的关键文件

| 文件 | 用途 |
|------|------|
| `/root/.openclaw/feishu/dedup/default.json` | 确认入站消息量 |
| `/root/.openclaw/openclaw.json.bak.20260227` | 确认飞书启用时段 |
| `/root/.openclaw/cron/runs/a3a73d1b-*.jsonl` | 确认 cron 投递失败 |
| `/root/.openclaw/agents/main/sessions/adfae334-*.jsonl` | 分析出站消息频率 |
| `extensions/feishu/src/streaming-card.ts` | 定位 streaming card 实现 |
| `extensions/feishu/src/reply-dispatcher.ts` | 定位 streaming 触发条件 |
| `extensions/feishu/src/bot.ts` | 确认入站处理流程 |
| `extensions/feishu/src/monitor.ts` | 确认 WebSocket 启动条件 |
