Files
sanguo_moziplus_v2/docs/design/mail-failure-notification.md
T
cfdaily cef76a72e0
Deploy / ci (push) Waiting to run
Deploy / deploy (push) Blocked by required conditions
Deploy / notify-deploy-failure (push) Blocked by required conditions
docs: Mail 失败通知设计文档 draft
2026-06-07 00:52:27 +08:00

5.1 KiB
Raw Blame History

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 等 @pangtongF2 两者都该通知:庞统(系统问题)+ 发件人(投递失败)
_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

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,跳过不再发通知。

# 防递归:系统通知邮件失败不再发通知
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 不触发通知