@@ -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-fujunshi(F2 原逻辑不变)
|
||||
- `_mail_auto_complete` 的 no_reply_found:标 failed 后通知发件人
|
||||
- 防递归:`must_haves.system_notify=true` 的邮件失败不再递归通知
|
||||
|
||||
### 20.2 失败场景与重试机制
|
||||
|
||||
所有可能的失败路径及其重试/等待机制(重试上限 max_retries=3,agent_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_failed;Task 项目 → @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_count:API 连续失败达上限(rate_limit/500/503)
|
||||
• max_monitor_timeouts:处理超时达上限(共约 31.5 分钟)
|
||||
• gateway_timeout:Agent 执行超时(已续杯重试)
|
||||
• session_stuck:Agent 会话假死(lock PID 死亡,revive 失败)
|
||||
• revive_failed:会话假死后恢复失败
|
||||
• auth_failed:Agent 认证失败(配置问题)
|
||||
• fallback_exhausted:主模型和备用模型均失败
|
||||
• agent_failed:收件人主动标记失败
|
||||
• compact_failed:上下文压缩失败
|
||||
• compact_hanging:上下文压缩长时间未完成(等待超 31.5 分钟)
|
||||
• compact_interrupted:上下文压缩被中断(已自动重试 3 次)
|
||||
• gateway_unreachable:Gateway 不可达(已自动重试 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 待实现改动
|
||||
|
||||
#### P1:api_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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user