# Gateway Watchdog 设计文档

**版本**: 1.0
**作者**: 庞统（副军师）🐦
**日期**: 2026-05-28
**状态**: 已实现

---

## 1. 问题背景

zhipu GLM-5.1 在高峰期返回 **429 限流**（"该模型当前访问量过大，请您稍后再试"，errorCode=1305），导致：

1. Gateway 调用失败，`stopReason=error`
2. Session 进入 stalled 状态
3. stalled 后无法自动恢复，必须手动重启 Gateway
4. 所有 Agent 假死，直到人工介入

### 历史记录

- 2026-05-26 09:00 CST — 429 导致假死
- 2026-05-28 ~10:00 CST — 429 导致假死，持续约 1 小时
- 累计 9 次 session.stalled 事件

## 2. 双层防护

### 2.1 第一层：跨 Provider Fallback（立即生效）

**配置**: `~/.openclaw/openclaw.json`

```json
{
  "model": {
    "primary": "zhipu/glm-5.1",
    "fallbacks": ["deepseek/deepseek-v4-pro"]
  }
}
```

- zhipu 429 时自动切 deepseek（不同 provider，不会一起死）
- deepseek-v4-pro 也是免费的，支持 1M 上下文
- Gateway 原生支持，无需额外代码

### 2.2 第二层：Watchdog 自动重启（兜底）

即使 fallback 也失败（所有 provider 同时挂），watchdog 检测到连续 429 后自动重启 Gateway。

## 3. Watchdog 设计

### 3.1 检测原理

**数据源**: Gateway 的 session jsonl 日志

```
~/.openclaw/agents/{agent-id}/sessions/*.jsonl
```

**429 精确特征**:
```json
{
  "message": {
    "stopReason": "error",
    "errorCode": "1305",
    "errorMessage": "429 该模型当前访问量过大，请您稍后再试"
  }
}
```

**判定条件**（全部满足才算 429）:
1. `stopReason === "error"`
2. `errorMessage` 包含 `"429"` 或 `errorCode` 为 `"1305"`

**不会误判**:
- 正常 stop（`stop=stop`、`stop=toolUse`）不算
- 其他 error（如 context overflow 的 errorCode 不同）不算
- `stopReason` 不是 `"error"` 的不算

### 3.2 检测流程

```
每分钟执行
    │
    ├─ Gateway health check
    │   ├─ 失败 → 直接重启（可能 Gateway 进程已死）
    │   └─ 成功 → 继续
    │
    ├─ 遍历所有 agent session jsonl
    │   ├─ 跳过 trajectory 文件
    │   ├─ 跳过 120 秒内未修改的文件（性能优化）
    │   ├─ 跳过 <100 字节的文件
    │   └─ 统计 CHECK_WINDOW(120s) 内的 429 错误数
    │
    ├─ 判断
    │   ├─ 有新 429 → consecutive++
    │   └─ 无新 429 → consecutive=0（重置）
    │
    └─ consecutive >= THRESHOLD(3) → 重启 Gateway + 重置计数
```

### 3.3 参数

| 参数 | 默认值 | 说明 |
|------|--------|------|
| CHECK_WINDOW | 120s | 检查最近多少秒的日志 |
| THRESHOLD | 3 | 连续检测到多少次 429 才重启 |
| 检测频率 | 60s | crontab 每分钟执行 |

### 3.4 状态文件

`/tmp/gateway-watchdog-429-count` — 记录连续 429 检测次数，重启后重置为 0。

### 3.5 日志

`/tmp/gateway-watchdog.log` — 每次执行的检测结果。

## 4. 文件位置

| 文件 | 路径 |
|------|------|
| 脚本 | `scripts/gateway-watchdog.sh` |
| 本文档 | `docs/design/gateway-watchdog.md` |
| 日志输出 | `/tmp/gateway-watchdog.log` |
| 状态文件 | `/tmp/gateway-watchdog-429-count` |

## 5. 部署

```bash
# crontab 每分钟执行
(crontab -l 2>/dev/null | grep -v "gateway-watchdog"; \
 echo "* * * * * /path/to/sanguo_moziplus_v2/scripts/gateway-watchdog.sh >> /tmp/gateway-watchdog.log 2>&1") \
 | crontab -
```

## 6. 运维

```bash
# 查看日志
tail -20 /tmp/gateway-watchdog.log

# 查看当前 429 计数
cat /tmp/gateway-watchdog-429-count

# 手动测试
bash scripts/gateway-watchdog.sh

# 停用
crontab -l | grep -v "gateway-watchdog" | crontab -
```

## 7. 已知局限

1. **事后检测** — 检测的是已发生的 429，不是预防
2. **重启治标** — 重启 Gateway 恢复 stalled session，但不解决 zhipu 限流根因
3. **无通知** — 重启后没有主动通知用户（可后续加飞书/邮件通知）
