Files
sanguo_moziplus_v2/docs/design/mail-failure-notification.md
T
cfdaily 6465a6c43f
Deploy / ci (push) Waiting to run
Deploy / deploy (push) Blocked by required conditions
Deploy / notify-deploy-failure (push) Blocked by required conditions
docs: Mail 失败通知设计 v1.1 — system 发件人 + Mail 不 @pangtong
2026-06-07 01:15:59 +08:00

136 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mail 失败通知设计
> 版本:v1.1-draft | 日期:2026-06-07 | 作者:庞统 | 状态:待评审
## 1. 背景
当前 Mail 失败后没有通知发件人。F2 修复让所有 `_mark_task("failed")` 统一 @pangtong-fujunshi,但这对 Mail 不合理:
- Mail 是 A→B 点对点通信,失败应通知发件人 A,不是庞统
- **Mail 唯一特有的失败——no_reply_found(幻觉门控,Agent 没回复 request)——没有任何通知**
### 失败场景分类
| 触发点 | 场景 | 当前通知 | 应该通知谁 |
|--------|------|---------|-----------|
| `_mark_task("failed")` | spawn 级失败:crashed、auth_failed、crash_limit、timeout 等 | @pangtongF2 | 发件人(不再 @pangtong |
| `_mail_auto_complete` no_reply_found | Agent 正常退出但没回复 request 类邮件 | 无 | 发件人 |
## 2. 设计方案
### 2.1 核心机制
Mail 最终标 failed 时,daemon 以 **系统发件人(`system`** 身份给 from_agent 发一封 inform 邮件,告知原因和处理建议。
**走标准 `POST /api/mail` 流程**,新增 `system` 为合法发件人。
### 2.2 系统发件人
新增 `system` 作为 Mail 的特殊发件人:
-`mail_routes.py``VALID_AGENTS` 或校验逻辑中,允许 `from="system"`
- `system` 不是真实 Agent,没有 main session,只用于发送通知
- 防递归:通知邮件的 must_haves 中加 `"system_notify": true`,触发前检查该标记
### 2.3 触发点
**触发点 1**`_mail_auto_complete` 中 no_reply_found 标 failed 之后(dispatcher.py
- 位置:标 failed 成功后、return 之前
- 调用:`notify_mail_failed(db_path, task_id, "no_reply_found")`
**触发点 2**`_mark_task` 中 status=="failed" 且是 _mail 项目时(spawner.py
- 位置:现有 F2 @pangtong 逻辑处
- **改动**:当项目是 `_mail` 时,**不 @pangtong**,改为调用 `notify_mail_failed` 通知发件人
- 当项目不是 `_mail` 时,F2 @pangtong 逻辑不变
### 2.4 实现位置
新增 `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 失败后以 system 身份给发件人发通知邮件
走标准 POST /api/mail 流程(from=system)。
防递归:检查原邮件 must_haves.system_notify,为 true 则跳过。
"""
```
内部调用 `send_mail(from="system", to=from_agent, type="inform", ...)`
### 2.5 通知邮件内容
| 字段 | 值 |
|------|------|
| from | `"system"` |
| to | from_agent(原邮件发件人) |
| type | `"inform"` |
| title | `"[投递失败] {原邮件title}"` |
| 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.6 防递归
```python
# 防递归:系统通知邮件失败不再发通知
meta = json.loads(original_task.must_haves) if original_task.must_haves else {}
if meta.get("system_notify"):
return # 系统邮件失败,不再递归通知
```
### 2.7 from_agent 获取
原 Mail 的发件人存在两个等价位置:
- `task.assigned_by`
- `must_haves` JSON 的 `"from"` 字段
优先用 `assigned_by`(无需解析 JSON),fallback 解析 must_haves。
## 3. 改动清单
### 3.1 新增文件
| 文件 | 内容 | 预估行数 |
|------|------|---------|
| `src/daemon/mail_notify.py` | `notify_mail_failed` 函数 | ~60 行 |
### 3.2 修改文件
| 文件 | 改动 | 预估行数 |
|------|------|---------|
| `src/daemon/dispatcher.py` | `_mail_auto_complete` 标 failed 后调 notify | ~5 行 |
| `src/daemon/spawner.py` | `_mark_task` failed 分支:`_mail` 项目不 @pangtong,改调 notify | ~10 行 |
| `src/api/mail_routes.py` | 允许 `system` 作为合法发件人(from 校验) | ~3 行 |
总计约 80 行,4 个文件。
### 3.3 不改的
| 项目 | 原因 |
|------|------|
| F2 @pangtong 对 Task 的逻辑 | Task failed 仍 @pangtong,只对 Mail 不适用 |
| no_reply_found 的标 failed 逻辑 | 只在标 failed 之后加通知,不改判定逻辑 |
| inform 类型邮件的完成逻辑 | inform 直接标 done,不存在 no_reply_found 场景 |
## 4. 验证方式
| 场景 | 验证 | 预期 |
|------|------|------|
| request 邮件无回复 | Agent 不回复 → _mail_auto_complete 标 failed | 发件人收到 system 通知邮件 |
| spawn 崩溃导致 Mail failed | Agent spawn crash → _mark_task failed | 发件人收到 system 通知,**不** @pangtong |
| Task failed | Task spawn crash → _mark_task failed | @pangtongF2 不变) |
| 系统通知邮件本身失败 | 通知邮件也 crash | 不再递归(system_notify=true |
| inform 邮件正常完成 | Agent 收到 inform → 直接 done | 不触发通知 |