From cef76a72e09bc33b87fdd2c4fc82f8d441a23409 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sun, 7 Jun 2026 00:52:27 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20Mail=20=E5=A4=B1=E8=B4=A5=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3=20draft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/design/mail-failure-notification.md | 123 +++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 docs/design/mail-failure-notification.md diff --git a/docs/design/mail-failure-notification.md b/docs/design/mail-failure-notification.md new file mode 100644 index 0000000..34d62ac --- /dev/null +++ b/docs/design/mail-failure-notification.md @@ -0,0 +1,123 @@ +# Mail 失败通知设计 + +> 版本:v1.0-draft | 日期:2026-06-07 | 作者:庞统 | 状态:待确认 + +## 1. 背景 + +当前 Mail 失败后没有通知发件人。F2 修复让所有 `_mark_task("failed")` 统一 @pangtong-fujunshi,但这对 Mail 不合理: + +- Mail 是 A→B 点对点通信,失败应通知发件人 A +- F2 覆盖的是 spawn 级失败(crashed/auth_failed 等),属系统基础设施问题,@pangtong 合理 +- **但 Mail 唯一特有的失败——no_reply_found(幻觉门控,Agent 没回复 request)——没有任何通知** + +### 失败场景分类 + +| 触发点 | 场景 | 当前通知 | 应该通知谁 | +|--------|------|---------|-----------| +| `_mark_task("failed")` | spawn 级失败:crashed、auth_failed、crash_limit、timeout 等 | @pangtong(F2) | 两者都该通知:庞统(系统问题)+ 发件人(投递失败) | +| `_mail_auto_complete` no_reply_found | Agent 正常退出但没回复 request 类邮件 | 无 | 发件人 | + +## 2. 设计方案 + +**核心机制**:Mail 最终标 failed 时,daemon 给 from_agent 发一封系统 inform 邮件,告知原因和处理建议。 + +### 2.1 实现位置 + +新增 `src/daemon/mail_notify.py`,统一函数 `notify_mail_failed`: + +```python +def notify_mail_failed(db_path: Path, original_mail_id: str, reason: str, detail: dict = None): + """Mail 失败后给发件人发系统通知邮件 + + 直接操作 Blackboard 创建 task,不走 POST /api/mail(避免 A9 校验)。 + """ +``` + +### 2.2 触发点 + +**触发点 1**:`_mail_auto_complete` 中 no_reply_found 标 failed 之后(dispatcher.py) + +- 位置:标 failed 成功后、return 之前 +- 调用:`notify_mail_failed(db_path, task_id, "no_reply_found")` +- from_agent 从 must_haves 解析 + +**触发点 2**:`_mark_task` 中 status=="failed" 且是 _mail 项目时(spawner.py) + +- 位置:现有 F2 @pangtong 逻辑之后 +- 条件:需要判断 task 是否属于 `_mail` 项目 +- 调用:`notify_mail_failed(db_path, task_id, reason)` +- from_agent 同样从 must_haves 解析 + +### 2.3 通知邮件内容 + +通知邮件字段: + +| 字段 | 值 | +|------|------| +| task_type | `"mail"` | +| assigned_by | `"daemon"` | +| assignee | from_agent(原邮件发件人) | +| status | `"pending"`(走正常投递流程,Agent 会收到) | +| title | `"[投递失败] {原邮件title}"` | +| must_haves.type | `"inform"` | +| must_haves.system_notify | `true`(防递归标记) | +| must_haves.in_reply_to | 原邮件 task_id | + +通知正文按 reason 区分: + +| reason | 正文模板 | 处理建议 | +|--------|---------|---------| +| `no_reply_found` | "你发送的 request 邮件「{title}」未被回复" | "建议重发邮件,或通过黑板任务方式联系" | +| `auth_failed` | "邮件「{title}」投递时认证失败" | "需检查 Agent 配置,联系姜维排查" | +| `crash_limit` | "邮件「{title}」投递时多次崩溃" | "系统异常,建议稍后重试" | +| `task_timeout` | "邮件「{title}」处理超时" | "建议重发或通过其他方式联系" | +| 其他 | "邮件「{title}」投递失败(原因:{reason})" | "建议联系庞统排查" | + +### 2.4 防递归 + +系统通知邮件本身也可能失败(spawn 崩溃等),必须防止无限递归: + +**方案**:通知邮件的 must_haves 中加 `"system_notify": true`。在触发点检查时,如果原邮件的 must_haves 含 `system_notify: true`,跳过不再发通知。 + +```python +# 防递归:系统通知邮件失败不再发通知 +meta = json.loads(original_task.must_haves) if original_task.must_haves else {} +if meta.get("system_notify"): + return # 系统邮件失败,不再递归通知 +``` + +### 2.5 from_agent 获取 + +原 Mail 的发件人存在两个等价位置: +- `task.assigned_by` +- `must_haves` JSON 的 `"from"` 字段 + +优先用 `assigned_by`(无需解析 JSON),fallback 解析 must_haves。 + +## 3. 不改的 + +| 项目 | 原因 | +|------|------| +| F2 @pangtong 逻辑 | spawn 级失败仍需要庞统知道,保留 | +| `POST /api/mail` | 不新增"系统发件人"角色,通知直接操作 Blackboard | +| no_reply_found 的标 failed 逻辑 | 只在标 failed 之后加通知,不改判定逻辑 | +| inform 类型邮件的完成逻辑 | inform 直接标 done,不存在 no_reply_found 场景 | + +## 4. 改动范围 + +| 文件 | 改动 | 预估行数 | +|------|------|---------| +| `src/daemon/mail_notify.py`(新增) | `notify_mail_failed` 函数 | ~60 行 | +| `src/daemon/dispatcher.py` | `_mail_auto_complete` 标 failed 后调 notify | ~5 行 | +| `src/daemon/spawner.py` | `_mark_task` failed 分支加 _mail 判断 + 调 notify | ~10 行 | + +总计约 75 行,3 个文件。 + +## 5. 验证方式 + +| 场景 | 验证 | 预期 | +|------|------|------| +| request 邮件无回复 | Agent 不回复 → _mail_auto_complete 标 failed | 发件人收到系统通知邮件 | +| spawn 崩溃导致 Mail failed | Agent spawn crash → _mark_task failed | 发件人收到通知 + 庞统收到 @mention | +| 系统通知邮件本身失败 | 通知邮件也 crash | 不再递归(system_notify=true) | +| inform 邮件正常完成 | Agent 收到 inform → 直接 done | 不触发通知 |