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