auto-sync: 2026-05-22 12:58:15

This commit is contained in:
cfdaily
2026-05-22 12:58:15 +08:00
parent 0b817a5e13
commit 4bfa7e2b0d
+83 -30
View File
@@ -1,6 +1,6 @@
# Spawner Monitor 设计文档
> 版本:v1.0 | 日期:2026-05-22 | 作者:庞统 | 状态:评审
> 版本:v1.1 | 日期:2026-05-22 | 作者:庞统 | 状态:评审通过(v1.1 已修正评审意见)
## 1. 背景与问题
@@ -72,8 +72,8 @@ daemon:
处理:
- 续杯次数 +1
- 超过上限(3) → ❌ failed + escalate
- 未超限 → 🔄 counter.release() → 用同一 session_id spawn(续杯)
- 超过上限(3) → ❌ failed + escalate → counter.release()
- 未超限 → 🔄 用同一 session_id spawn(续杯)counter 不 release
- 续杯 message:提示 Agent 检查历史继续未完成工作
```
@@ -89,8 +89,8 @@ daemon:
处理:
- 续杯次数 +1
- 超过上限(3) → ❌ failed + escalate
- 未超限 → 🔄 counter.release() → 用同一 session_id spawn(续杯)
- 超过上限(3) → ❌ failed + escalate → counter.release()
- 未超限 → 🔄 用同一 session_id spawn(续杯)counter 不 release
- 续杯 message:完整任务 prompt(和首次一样)
```
@@ -126,6 +126,7 @@ daemon:
- 查任务实际 API 状态
- 如果已是 done/review → 同 A1
- 如果仍是 working/claimed → 同 A2/A3(续杯,用原 session_id,不用 fallback session
- 续杯 prompt 额外提示:"之前有 fallback 执行,请调 API 检查任务当前状态和已有产出,确认是否已完成"
- 记录 outcome = "fallback_timeout",附带 warning
```
@@ -169,10 +170,11 @@ daemon:
原因:Gateway 进程挂了、网络断
处理:
- 续杯次数 +1(不计入 max_retries,用单独的 connect_retry_count
- connect_retry_count ≥ 3 → ❌ failed + escalate
- 未超限 → 🔄 等待 30s(一个 tick)后重试
- 记录 outcome = "gateway_unreachable"
- connect_retry_count +1(不计入 max_retries
- connect_retry_count ≥ 3 → ❌ failed + escalate → counter.release()
- 未超限 → 不改变任务状态(保持 claimed/working),不 release counter
- 只写 metadataoutcome=gateway_unreachable),让 ticker 下个 tick 自然重新调度
- ⚠️ 不在 spawner 里 sleep 等待(避免阻塞 monitor 逻辑)
```
### A9exit≠0 + stderr 含 rate_limit/500/503/API error
@@ -185,10 +187,11 @@ daemon:
原因:模型提供商限流、服务异常
处理:
- 续杯次数 +1(不计入 max_retries,用单独的 api_retry_count
- api_retry_count ≥ 3 → ❌ failed + escalate
- 未超限 → 🔄 等待 60s 后重试(给 API 恢复时间)
- 记录 outcome = "api_error"
- api_retry_count +1(不计入 max_retries
- api_retry_count ≥ 3 → ❌ failed + escalate → counter.release()
- 未超限 → 不改变任务状态,不 release counter
- 只写 metadataoutcome=api_error),让 ticker 下个 tick 自然重新调度
- ⚠️ 不在 spawner 里 sleep 等待
```
### A10exit≠0 + stderr 含 compaction-diag/context-overflow/timeout-compaction
@@ -201,9 +204,9 @@ daemon:
原因:compact 后模型返回错误(丢失上下文导致无法继续)
处理:
- 续杯次数 +1(计入 max_retries
- 超过上限(3) → ❌ failed + escalate
- 未超限 → 🔄 用同一 session_id spawn(续杯)
- retry_count +1(计入 max_retries
- 超过上限(3) → ❌ failed + escalate → counter.release()
- 未超限 → 🔄 用同一 session_id spawn(续杯)counter 不 release
- 记录 outcome = "compact_failed"
```
@@ -218,9 +221,11 @@ daemon:
v2 用 --session-id uuid4 已基本避免,但保留兜底
处理:
- 续杯次数 +1(不计入 max_retries
- 等待 30s 后重试
- 记录 outcome = "lock_conflict"
- lock_retry_count +1(不计入 max_retries
- lock_retry_count ≥ 3 → ❌ failed + escalate → counter.release()
- 未超限 → 不改变任务状态,不 release counter
- 只写 metadataoutcome=lock_conflict),让 ticker 下个 tick 自然重新调度
- ⚠️ 不在 spawner 里 sleep 等待
```
### A12exit≠0 + stderr 无特殊关键字
@@ -233,9 +238,9 @@ daemon:
原因:Agent 自身逻辑错误、工具执行失败、或其他未知错误
处理:
- 续杯次数 +1(计入 max_retries
- 超过上限(3) → ❌ failed + escalate
- 未超限 → 🔄 用同一 session_id spawn(续杯)
- retry_count +1(计入 max_retries
- 超过上限(3) → ❌ failed + escalate → counter.release()
- 未超限 → 🔄 用同一 session_id spawn(续杯)counter 不 release
- 记录 outcome = "agent_error"
```
@@ -280,8 +285,8 @@ daemon:
处理:
- monitor_timeout_count +1
- 未超限(< 3) → counter.release() → 再启动一轮 _monitor_process 继续等
- 超限(≥ 3,累计 31.5 分钟) → ❌ failed + escalate,不 kill
- 未超限(< 3) → 不 release counter → 再启动一轮 _monitor_process 继续等
- 超限(≥ 3,累计 31.5 分钟) → ❌ failed + escalate → counter.release(),不 kill
- 记录 outcome = "compact_hanging"
```
@@ -300,8 +305,8 @@ daemon:
处理:
- monitor_timeout_count +1
- 未超限(< 3) → counter.release() → 再启动一轮 _monitor_process 继续等
- 超限(≥ 3) → ❌ failed + escalate,不 kill
- 未超限(< 3) → 不 release counter → 再启动一轮 _monitor_process 继续等
- 超限(≥ 3) → ❌ failed + escalate → counter.release(),不 kill
- 记录 outcome = "process_hanging"
区别 a 和 b
@@ -338,6 +343,7 @@ daemon:
RETRY_PROMPT = """你收到一个续杯提醒。你的任务在执行过程中被中断了。
## 任务信息
- 项目: {project_id}
- 任务ID: {task_id}
- 标题: {title}
@@ -345,8 +351,35 @@ RETRY_PROMPT = """你收到一个续杯提醒。你的任务在执行过程中
请检查 session 历史中你之前做了什么,然后继续未完成的工作。
如果已经完成,请调 API 标记完成。
如果遇到无法解决的问题,标记失败并说明原因。"""
## 操作指令
### 查看任务当前状态
```bash
curl http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}?expand=all
```
### 如果已经完成,标记 review
```bash
curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/status \
-H 'Content-Type: application/json' \
-d '{{"status": "review", "agent": "{agent_id}"}}'
```
### 写入产出(如果之前没写)
```bash
curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/outputs \
-H 'Content-Type: application/json' \
-d '{{"agent": "{agent_id}", "type": "<类型>", "title": "<标题>", "content": "<内容>", "summary": "<摘要>"}}'
```
### 如果无法解决,标记失败
```bash
curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/status \
-H 'Content-Type: application/json' \
-d '{{"status": "failed", "agent": "{agent_id}", "detail": "<失败原因>"}}'
```
{fallback_hint}"""
```
### 续杯 spawn
@@ -371,11 +404,29 @@ await self.spawner.spawn_full_agent(
| `retry_count` | 续杯次数(A2/A3/A10/A12 | 3 | failed + escalate |
| `connect_retry_count` | 连接失败次数(A8 | 3 | failed + escalate |
| `api_retry_count` | API 错误次数(A9 | 3 | failed + escalate |
| `lock_retry_count` | Lock 冲突次数(A11 | 3 | 等待后重试 |
| `lock_retry_count` | Lock 冲突次数(A11 | 3 | ticker 下个 tick 重试 |
| `monitor_timeout_count` | monitor timeout 次数(B2/B3 | 3 | failed + escalate |
存储在 `task_attempts.metadata` JSON 中。
### counter 生命周期
```
首次 acquiredispatcher.dispatch
├─ 续杯 spawn → counter 不 release(保持占用)
├─ 继续等(B2/B3 → counter 不 release
├─ 暂时性失败回 tickerA8/A9/A11 → counter 不 release
└─ 最终完成/failed/escalate → counter.release()
```
counter 占用贯穿整个续杯链,只在以下情况 release:
- 任务最终完成(A1/A4
- 超过重试上限 → failed + escalate
- 认证失败(A7
- 假死(B1
## 8. escalate 消息格式
```
@@ -410,10 +461,12 @@ Session: {session_key}
| `src/daemon/spawner.py` | `spawn_full_agent``--timeout` + session_id 复用 | ~15 行 |
| `src/daemon/spawner.py` | 新增辅助方法:`_get_task_status``_classify_exit``_read_sessions_json``_check_lock_pid` | ~80 行 |
| `src/daemon/spawner.py` | 新增 `RETRY_PROMPT` 模板 | ~20 行 |
| `src/daemon/ticker.py` | `_check_timeouts`增加 retry_count 检查,不再直接 failed | ~15 行 |
| `src/daemon/ticker.py` | `_check_timeouts`暂时性失败(A8/A9/A11)不改状态,等 ticker 自然重试 | ~15 行 |
| `config/guardrails.yaml` | 无需改动 | — |
| `config/default.yaml` | 新增 `gateway_timeout``max_retries``max_monitor_timeouts` | ~3 行 |
注:`task_attempts` 表已有 `metadata` 列(TEXT 类型),无需改 db.py/models.py。
总计约 280 行,3 个文件。
## 10. 测试计划