This commit was merged in pull request #55.
This commit is contained in:
2026-06-13 01:23:33 +00:00
2 changed files with 146 additions and 2 deletions
+146 -2
View File
@@ -950,7 +950,151 @@ handler.post_complete(task_id, agent_id, outcome, db_path)
---
# §18 设计决策记录
## §20. Mail 失败通知机制
### 20.1 背景
Mail 是 A→B 点对点通信,失败应通知发件人 A,而非统一 @pangtong。
当前机制(v1.3 已实现):
- `_mark_task("failed")` 对 _mail 项目:调用 `mail_notify.notify_mail_failed` 通知发件人
- `_mark_task("failed")` 对 Task 项目:@pangtong-fujunshiF2 原逻辑不变)
- `_mail_auto_complete` 的 no_reply_found:标 failed 后通知发件人
- 防递归:`must_haves.system_notify=true` 的邮件失败不再递归通知
### 20.2 失败场景与重试机制
所有可能的失败路径及其重试/等待机制(重试上限 max_retries=3agent_timeout=630s):
| 失败类型 | 机制 | 重试次数 | 每次耗时 | cooldown | 最长总耗时 |
|---|---|---|---|---|---|
| `gateway_timeout` | 续杯 | 3 | 630s | 无 | ~31.5 分钟 |
| `crashed` | ticker 兜底 | 3 | ~2-5 分钟 | 60s + 30s ticker | ~15 分钟 |
| `api_error`rate_limit | 推 pending**待改为续杯** | 3 | ~2.5 分钟 | 120s | ~8 分钟 |
| `compact_interrupted` | 续杯 | 3 | 630s | 60s | ~34 分钟 |
| `gateway_unreachable` | 续杯 | 3 | 630s | 60s | ~34 分钟 |
| `lock_conflict` | 续杯 | 3 | 630s | 60s | ~34 分钟 |
| `fallback_timeout` | 续杯(A3b) | 3 | 630s | 60s | ~34 分钟 |
| `compact_wait` | monitor 等待 | 3 | 630s | 无 | ~31.5 分钟 |
| `compact_hanging` | monitor → release | 3 | 630s | 300s | ~31.5 分钟 + ticker |
| `max_monitor_timeouts` | monitor 上限 | 3 | 630s | 无 | ~31.5 分钟 |
| `session_stuck` | revive 1 次 | 1 | ~30s | 无 | ~30 秒 |
| `compact_failed` | 无重试 | 0 | — | 300s | 立刻 failed |
| `auth_failed` | 无重试 | 0 | — | — | 立刻 failed |
| `agent_error` | 无重试 | 0 | — | 300s | 立刻 failed |
| `no_reply_found` | 无重试 | 0 | — | — | 立刻 failed |
### 20.3 触发点
| 触发点 | 文件 | 说明 |
|---|---|---|
| `_mark_task(failed)` | spawner.py | _mail 项目 → notify_mail_failedTask 项目 → @pangtong |
| `_mail_auto_complete` no_reply_found | dispatcher.py | Agent 正常退出但没回复 request → 标 failed → 通知发件人 |
### 20.4 实现位置
- `src/daemon/mail_notify.py``notify_mail_failed` + `_is_mail_project` + 通知模板
- `src/daemon/spawner.py``_mark_task` 中 _mail/Task 分流
- `src/daemon/dispatcher.py``_mail_auto_complete` 中 no_reply_found 后调 notify
### 20.5 通知设计(v2.0 — AI Native
通知提供充足事实信息,不做硬编码处理建议。收件 AI 自行判断下一步。
**通知结构**
```
邮件投递失败通知
📧 原始邮件:「{title}」
👤 收件人:{to_agent}
❌ 失败原因:{reason_human_readable}{reason_raw}
📊 重试情况:{attempt_info}
📋 上下文信息:
{detail_formatted}
常见失败原因参考:
• no_reply_found:收件人未回复(Agent 未能识别或处理此邮件)
• crashed / max_crash_count:收件人处理时进程崩溃(已自动重试 3 次)
• max_retries:续杯耗尽(已自动重试 3 次,共约 34 分钟)
• max_api_retry_countAPI 连续失败达上限(rate_limit/500/503
• max_monitor_timeouts:处理超时达上限(共约 31.5 分钟)
• gateway_timeoutAgent 执行超时(已续杯重试)
• session_stuckAgent 会话假死(lock PID 死亡,revive 失败)
• revive_failed:会话假死后恢复失败
• auth_failedAgent 认证失败(配置问题)
• fallback_exhausted:主模型和备用模型均失败
• agent_failed:收件人主动标记失败
• compact_failed:上下文压缩失败
• compact_hanging:上下文压缩长时间未完成(等待超 31.5 分钟)
• compact_interrupted:上下文压缩被中断(已自动重试 3 次)
• gateway_unreachableGateway 不可达(已自动重试 3 次)
• lock_conflict:会话锁冲突(已自动重试 3 次)
• 其他:建议排查系统日志
——系统自动通知
```
**reason 人话翻译映射**
| reason_raw | reason_human_readable | detail 提取 |
|---|---|---|
| `no_reply_found` | 收件人未回复 | 无额外信息 |
| `crashed` | 处理时进程崩溃 | stderr_preview 前 200 字 |
| `max_crash_count` | 连续崩溃达上限 | count + stderr_preview |
| `max_retries` | 续杯耗尽 | count + retry_field |
| `max_api_retry_count` | API 连续失败达上限 | count |
| `max_monitor_timeouts` | 处理超时达上限 | count + elapsed_seconds |
| `gateway_timeout` | Agent 执行超时 | retry_count |
| `session_stuck` | 会话假死 | stuck_count |
| `revive_failed` | 假死后恢复失败 | stuck_count |
| `auth_failed` | 认证失败 | stderr_preview |
| `fallback_exhausted` | 模型全部失败 | fallback_count + fallback_reason |
| `agent_failed` | 收件人主动标失败 | 无 |
| `compact_failed` | 上下文压缩失败 | stderr_preview |
| `compact_hanging` | 压缩长时间未完成 | compact_wait_count |
| `compact_interrupted` | 压缩被中断 | 无 |
| `gateway_unreachable` | Gateway 不可达 | stderr_preview |
| `lock_conflict` | 会话锁冲突 | 无 |
| 默认 | 未知原因 | reason + stderr_preview(如有) |
**重试情况格式**
- 有重试:`"已自动重试 {count} 次,共耗时约 {total_time}"`
- 无重试:`"无法重试({reason_human_readable}"`
### 20.6 防递归
系统通知邮件(from=system)本身也可能失败:
- 检查 `must_haves.system_notify=true` → 跳过递归通知
- system 不是有效 Agent → 通知路由到 pangtong-fujunshi 代处理
### 20.7 待实现改动
#### P1api_error rate_limit 改为可恢复 retry
**当前**`_classify_outcome` 中 rate_limit/500/503 → `api_error``should_retry=False`,走推 pending 路径。
**改为**`should_retry=True`,走续杯路径。cooldown 60s。上限仍 3 次。
**改动文件**`src/daemon/spawner.py` `_classify_outcome` 的 `api_error` 分支。
**影响**`api_retry_count` 机制可以废弃(统一用 `retry_count`),但保持向后兼容暂不删除。
#### P2:通知模板更新(v2.0
**当前**`mail_notify.py` 的 `_NOTIFY_TEMPLATE` 是静态模板,不传 detail。
**改为**:动态模板,根据 reason 选择人话翻译 + 提取 detail 信息 + 格式化重试情况。
**改动文件**`src/daemon/mail_notify.py`。
**新增**`_REASON_MAP` 字典(reason → 人话 + detail 提取函数)。
### 20.8 不改的
| 项目 | 原因 |
|---|---|
| F2 @pangtong 对 Task 的逻辑 | Task failed 仍 @pangtong,只对 Mail 不同 |
| no_reply_found 的判定逻辑 | 只在判定后加通知,不改判定本身 |
| inform 类型邮件的完成逻辑 | inform 直接 done,不存在 no_reply_found |
| 外部 API 的 from 校验 | system 不走 HTTP,外部无法伪造 |
---
# §21 设计决策记录
本节记录设计过程中的关键讨论和决策,便于未来回顾。
@@ -1010,7 +1154,7 @@ handler.post_complete(task_id, agent_id, outcome, db_path)
---
## §19. 审查与验证历史
## §22. 审查与验证历史
### Step 2-5 背靠背审查(2026-06-10/11