auto-sync: 2026-06-02 22:56:38
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
# #08 Classify Outcome 优化 + Registry 清理
|
||||
|
||||
> 版本:v1.0 | 日期:2026-06-02 | 作者:庞统 | 状态:设计中
|
||||
> 版本:v1.1 | 日期:2026-06-02 | 作者:庞统 | 状态:待实施(已部分实施,本轮补完剩余项)
|
||||
>
|
||||
> v1.0→v1.1 变更:Phase -1 TCP probe 方案废弃(已转为独立 Gateway Watchdog v2);
|
||||
> cooldown 参数统一简化(A14/A15/A8/A10 均改为 60s);A7 compact retry 暂缓;
|
||||
> stderr_preview 暂缓;标注已实施项。
|
||||
> 已实施项(经司马懿 3 轮评审 approved):A3/A3b fallback 分级、api_error retry 上限、
|
||||
> crash/error cooldown 300s、fallback_retry_count 独立计数。
|
||||
> 本轮实施:A14/A15/A16/A17 拆分 + A8/A9/A10 改 retry + cooldown 统一 60s。
|
||||
|
||||
## 1. 背景与动机
|
||||
|
||||
@@ -37,49 +44,19 @@ architecture-v3.0.md §8.2 描述三层并发控制,代码已实现但存在
|
||||
2. **Registry 清理**:删除项目时同步清理 registry,discover 时同步清理孤儿
|
||||
3. **并发控制对齐**:标记 AgentProfile.max_concurrent 为 TODO 或接入 counter
|
||||
|
||||
## 2.1 Phase -1: Gateway 存活检查(新增)
|
||||
## 2.1 Phase -1: Gateway 存活检查 — ❌ 已废弃
|
||||
|
||||
### 问题
|
||||
### 原方案(TCP probe)
|
||||
|
||||
spawner Phase 0-4 只检查 agent 本地文件状态(sessions.json、lock),不检查 Gateway 进程是否活着。如果 Gateway 挂了或重启中,`openclaw agent` 命令会 hang 10+ 分钟后失败,浪费资源。
|
||||
spawner spawn 前 TCP + WebSocket Upgrade 握手探测 Gateway。实测 406 次全部 ok,但线上仍频繁发生 stalled session、lane task error、FailoverError。
|
||||
|
||||
### 方案
|
||||
### 失败原因
|
||||
|
||||
在 Phase 0 之前增加 Gateway liveness check:
|
||||
TCP 握手只能检测进程端口是否监听,无法检测 Gateway **业务可用性**(lane task 卡死、provider 全挂等)。结论:TCP probe 方案无效。
|
||||
|
||||
```python
|
||||
async def _probe_gateway(self, timeout: float = 3.0) -> bool:
|
||||
"""TCP + WebSocket Upgrade 握手探测 Gateway"""
|
||||
try:
|
||||
reader, writer = await asyncio.wait_for(
|
||||
asyncio.open_connection('127.0.0.1', 18789), timeout=timeout
|
||||
)
|
||||
writer.write(
|
||||
b'GET /ws HTTP/1.1\r\n'
|
||||
b'Host: 127.0.0.1:18789\r\n'
|
||||
b'Upgrade: websocket\r\n'
|
||||
b'Connection: Upgrade\r\n'
|
||||
b'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n'
|
||||
b'Sec-WebSocket-Version: 13\r\n\r\n'
|
||||
)
|
||||
await writer.drain()
|
||||
resp = await asyncio.wait_for(reader.read(256), timeout=timeout)
|
||||
writer.close()
|
||||
return b'101' in resp
|
||||
except Exception:
|
||||
return False
|
||||
```
|
||||
### 替代方案
|
||||
|
||||
### 处理
|
||||
|
||||
Gateway 不可达 → `AgentBusyError(reason="gateway_down")` → 任务留 pending → ticker 30s 后自动重试。
|
||||
|
||||
### 性能
|
||||
|
||||
- 延迟:~1-10ms(正常情况)
|
||||
- 无新依赖(原生 asyncio)
|
||||
- 对 Gateway 零负担(等同一次普通 HTTP 请求)
|
||||
- 验证脚本:`scripts/gateway_monitor.py`(每 10s 探测 + 日志记录)
|
||||
独立 Gateway Watchdog v2(已部署):bash 脚本每分钟扫描 Gateway **进程日志**,3 条规则检测 FailoverError/stalled/rate_limit,自动重启 + 5 分钟冷却。设计文档:`docs/design/gateway-watchdog.md`。
|
||||
|
||||
## 3. Classify Outcome 判定树 v2.0
|
||||
|
||||
@@ -182,12 +159,13 @@ Gateway 不可达 → `AgentBusyError(reason="gateway_down")` → 任务留 pend
|
||||
- exit=130 (SIGINT) 或 exit=143 (SIGTERM)
|
||||
- 原因:进程被外部中断(PM2 restart、compact 期间被杀、手动 Ctrl+C)
|
||||
- 不是代码 bug,retry 很可能成功
|
||||
- 直接 retry,不设额外 cooldown(进程已退出,session 应该是干净的)
|
||||
- retry + cooldown 60s
|
||||
|
||||
#### A15/A16: 无 JSON 输出 + stderr 关键字(可恢复,新增)
|
||||
- 原逻辑 A0 把这些统判 crashed
|
||||
- 新逻辑:stderr 含 network/compact 关键字 → 对应的可恢复分类
|
||||
- 和 A8/A7 处理一致
|
||||
- A15: network → retry + cooldown 60s
|
||||
- A16: compact → retry + cooldown 60s
|
||||
|
||||
#### A17: crashed(不确定,等 ticker 兜底)
|
||||
- 排除所有可恢复场景后,真正的"不知道什么原因崩了"
|
||||
@@ -199,14 +177,16 @@ Gateway 不可达 → `AgentBusyError(reason="gateway_down")` → 任务留 pend
|
||||
|
||||
### 3.5 cooldown 参数汇总
|
||||
|
||||
> ⚠️ v1.1 统一简化:A14/A15/A8/A10 cooldown 统一 60s,减少认知负担和参数维护。
|
||||
|
||||
| 场景 | cooldown | 理由 |
|
||||
|------|----------|------|
|
||||
| A3b fallback | 30s | 模型抖动,短冷却 |
|
||||
| A7/A16 compact | 60s | 等 compact 完成 |
|
||||
| A8/A15 network | 30s | 网络暂时性问题 |
|
||||
| A8/A15 network | 60s | 网络暂时性问题 |
|
||||
| A9 rate_limit | 60s | API 限流需要等待 |
|
||||
| A10 lock | 10s | lock 冲突很快释放 |
|
||||
| A14 interrupted | 0s | 进程已退出,session 干净 |
|
||||
| A10 lock | 60s | 统一简化 |
|
||||
| A14 interrupted | 60s | 统一简化 |
|
||||
| A17 crashed | 300s | 给 session 恢复时间 |
|
||||
|
||||
### 3.6 retry 时 cooldown 的实现
|
||||
@@ -224,24 +204,10 @@ if cls.get("cooldown_seconds"):
|
||||
|
||||
**方案**:在 `_do_retry` 开始时设 cooldown,cooldown 期间 `asyncio.sleep`,然后继续 spawn。这样不需要改 counter 逻辑。
|
||||
|
||||
### 3.7 stderr 记录增强
|
||||
### 3.7 stderr 记录增强 — 暂缓
|
||||
|
||||
`_record_attempt` metadata 新增:
|
||||
|
||||
```python
|
||||
metadata = {
|
||||
# 现有
|
||||
"status": ...,
|
||||
"summary": ...,
|
||||
"fallback_used": ...,
|
||||
"fallback_reason": ...,
|
||||
"task_status_at_exit": ...,
|
||||
# 新增
|
||||
"stderr_preview": stderr_text[:500] if stderr_text else None,
|
||||
"exit_signal": "SIGINT" if exit_code == 130 else "SIGTERM" if exit_code == 143 else None,
|
||||
"fallback_count": ..., # 累计 fallback 次数
|
||||
}
|
||||
```
|
||||
stderr_preview 和 exit_signal 写 metadata 的方案暂缓,本轮不实施。
|
||||
已有 metadata 记录 fallback_count、api_retry_count、retry_count 等计数器。
|
||||
|
||||
## 4. Registry 清理
|
||||
|
||||
|
||||
Reference in New Issue
Block a user