"""Mail 失败通知 — 以 system 身份通知发件人""" from __future__ import annotations import json import logging from datetime import datetime from pathlib import Path from typing import Optional from src.blackboard.models import Task from src.blackboard.operations import Blackboard from src.config.agents import AGENT_IDS logger = logging.getLogger(__name__) # 邮件通知正文模板(统一模板,包含所有可能的失败原因和建议) _NOTIFY_TEMPLATE = """你的邮件投递失败了。 📧 原始邮件:「{title}」 👤 收件人:{to_agent} ❌ 失败原因:{reason} 常见失败原因及处理建议: • no_reply_found:收件人未回复。建议重发邮件,或通过黑板任务方式联系 • auth_failed:收件人认证失败。需检查 Agent 配置,联系姜维(jiangwei-infra)排查 • crash_limit:收件人处理时多次崩溃。系统异常,建议稍后重试 • task_timeout:处理超时。建议重发或通过其他方式联系 • 其他原因:建议联系副军师(pangtong-fujunshi)排查 ——系统自动通知""" def _is_mail_project(db_path: Path) -> bool: """从 db_path 推断是否为 _mail 项目""" path_str = str(db_path) return "/_mail/" in path_str or path_str.endswith("_mail.db") def notify_mail_failed(db_path: Path, original_mail_id: str, reason: str, detail: Optional[dict] = None) -> None: """Mail 失败后以 system 身份给发件人发通知邮件 直接通过 Blackboard 创建 Task,不走 HTTP API。 防递归:检查原邮件 must_hives.system_notify,为 true 则跳过。 发件人不是有效 Agent(如 system)→ 通知庞统代处理,避免广播风暴。 """ try: bb = Blackboard(db_path) original = bb.get_task(original_mail_id) if not original: logger.warning( "notify_mail_failed: original mail %s not found", original_mail_id) return # 解析原邮件元数据 meta = json.loads(original.must_haves) if original.must_haves else {} # 防递归:系统通知邮件失败不再发通知 if meta.get("system_notify"): logger.info( "Mail %s: system notify mail failed, skipping recursive notification", original_mail_id) return # 获取发件人(优先 assigned_by,fallback must_haves.from) from_agent = original.assigned_by or meta.get("from", "") to_agent = original.assignee or "" title = original.title or "" if not from_agent: logger.warning( "notify_mail_failed: cannot determine sender for mail %s", original_mail_id) return # 发件人不是有效 Agent(如 system)→ 通知庞统代处理,不触发广播 target_agent = from_agent if from_agent not in AGENT_IDS: logger.warning("Mail %s: sender '%s' is not a valid agent, routing failure notice to pangtong-fujunshi", original_mail_id, from_agent) target_agent = "pangtong-fujunshi" # 构造通知正文 text = _NOTIFY_TEMPLATE.format( title=title, to_agent=to_agent, reason=reason, ) # 创建通知邮件 Task notify_id = f"mail-{int(datetime.now().timestamp() * 1000)}" notify_meta = { "type": "inform", "performative": "inform", "is_read": False, "conversation_id": meta.get("conversation_id", ""), "in_reply_to": original_mail_id, "from": "system", "system_notify": True, } notify_task = Task( id=notify_id, title=f"[投递失败] {title}", description=text, assignee=target_agent, assigned_by="system", must_haves=json.dumps(notify_meta, ensure_ascii=False), task_type="mail", status="pending", ) bb.create_task(notify_task) logger.info("Mail %s: sent failure notification to %s (original_sender=%s, reason=%s, notify_id=%s)", original_mail_id, target_agent, from_agent, reason, notify_id) except Exception as e: logger.warning( "notify_mail_failed: failed to send notification for mail %s: %s", original_mail_id, e)