From 466c6e7d3e23e36ad85dbd2573127399ab58ec1c Mon Sep 17 00:00:00 2001 From: cfdaily Date: Tue, 2 Jun 2026 22:56:38 +0800 Subject: [PATCH] auto-sync: 2026-06-02 22:56:38 --- .../08-classify-outcome-optimization.md | 86 ++++++------------- 1 file changed, 26 insertions(+), 60 deletions(-) diff --git a/docs/design/08-classify-outcome-optimization.md b/docs/design/08-classify-outcome-optimization.md index 738ebfb..763c48b 100644 --- a/docs/design/08-classify-outcome-optimization.md +++ b/docs/design/08-classify-outcome-optimization.md @@ -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 清理