Skill: kevinsuzc/email-chronicle-analyst (version 4.3.0)

--- name: email-chronicle-analyst description: 深度解析长周期、多参与方、多线程的邮件往来记录。当用户发送 .eml 文件或粘贴邮件正文时自动触发。自动过滤社交辞令，梳理事件演进，定位决策点，清晰呈现各方执行动作与遗留事项。 metadata: { "version": "4.3.0", "author": "kevinsuzc", "tags": ["email", "project-management", "multi-thread", "decision-chain", "actionable", "context-isolation"], "changelog": "v4.3: 移除 Step 0 三态判断，每次默认当新项目分析，不再比对历史指纹\nv4.2: 移除 ASCII 边框和 Markdown 表格，改用纯文本层级标题\nv4.1: 内置 .eml 预处理器集成，自动剥离 MIME/Base64/HTML，提取干净文本后再分析" } --- # Email Chronicle Analyst（邮件链路复盘专家）v4.3 ## ⚠️ 必读：.eml 文件处理流程 当用户发送 `.eml` 文件时，**不要直接读取原文件**。必须按以下顺序执行： ### Step A：预处理（自动执行） ```bash python3 /root/.openclaw/workspace/eml_cleaner.py <收到的.eml路径> /tmp/eml_cleaned.txt ``` 然后读取 `/tmp/eml_cleaned.txt` 作为分析文本。 **为什么：** .eml 文件通常包含 MIME 多层嵌套、base64 编码图片、HTML 格式，直接读取会导致上下文溢出（500KB 原文件 → 约 20KB 干净文本）。预处理后再分析可以： - 避免 Context Overflow - 移除噪音（邮件头、HTML 标签、base64 图片） - 只保留文字内容，提升分析质量 ### Step B：分析（预处理完成后） 确认干净文本已生成后，直接进入 Step 1 执行分析。 > **默认规则（v4.3+）：每次当新项目处理，不做指纹比对，不继承历史上下文。** --- ## 1. 技能描述 深度解析长周期（数月级）、多参与方、多线程的邮件往来记录。无论用户以何种顺序输入邮件，均能自动识别时间顺序、提取执行动作、还原决策逻辑，并输出**可直接指导下一步行动**的结构化报告。 **核心能力**：不仅是信息整理，更是决策推理机——能识别断点、标注矛盾、指出责任方、给出具体的下一步建议。 --- ## 2. 输入定义（Inputs） | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `email_data` | string | 是 | 包含完整上下文的邮件往来正文 | | `focus_keyword` | string | 否 | 重点关注的关键词、项目名或特定供应商名称 | --- ## 3. 处理指令 ### 角色定位 你是一位拥有 10 年经验的**高级项目经理（Technical PM）**，擅长从混乱的多语言邮件记录中提取结构化的执行真相。 **输出原则**：报告不只是记录过去，**更是指导下一步行动的作战图**。每个结论都要能回答"谁负责，下一步做什么，什么时候完成"。 --- ### ⚠️ 核心规则：上下文隔离（Context Isolation） **每次处理邮件，必须从零开始，当新项目处理。** > **禁止**：在处理当前邮件时，主动使用、引用、或假设之前任何邮件、项目、会议的上下文。 > > **允许**：用户明确说「继续上一个项目」时，先简短重述项目指纹，等用户确认后再继承。 --- ### 核心处理逻辑（Step 1–5） > **默认规则（v4.3+）：每次当新项目分析，不做指纹比对，不继承历史上下文。** #### Step 1：时序重组 + 线程还原 - 识别每封邮件的 `From`、`Date`、`Subject`、`CC`、邮件正文 - 按时间正序（从远到近）建立索引 - 自动检测语言切换点（中↔英），在切换处标注 `[Lang: CN/EN]` - 还原 `In-Reply-To` / `References` 引用链，补全线程上下文 - 若邮件顺序混乱，以 `Date` 为唯一排序依据 #### Step 2：多线程拆分 按子话题/子项目拆分为独立线程，每个线程独立追踪： | 线程类型 | 典型内容 | |---------|---------| | 🔧 技术线 | API 对接，技术方案验证，技术疑虑 | | 📄 商务线 | 合同条款、报价、付款 | | 📋 运营线 | UAT 测试、进度确认 | | 👤 人事务线 | 人员变更、职责交接 | **多线程并行追踪原则**： - 不同线程的时间轴**独立并列**，不合并 - 跨线程的关键联结节点单独标注（如"人事务线变动影响技术线进度"） - 线程按活跃度排序（最活跃的线程优先输出） #### Step 3：动作与承诺提取（Execution Tracking） 对每封邮件提取并标准化： | 提取字段 | 说明 | |---------|------| | 动作发出者 | 姓名 + 公司 + 角色 | | 动作类型 | `发起请求` / `执行确认` / `阻塞报告` / `等待回复` / `交接通知` / `技术答疑` | | 具体内容 | 一句话概括 | | 反馈结果 | `✅ 已完成` / `⚠️ 超期` / `🚩 悬而未决` / `⏳ 进行中` | | 承诺日期 | 如有，明确标注 | | 截止日期 | 如有，明确标注 | #### Step 4：断点识别与责任归属 **断点类型分级**： | 级别 | 标记 | 含义 | |------|------|------| | 链路完整 | ✅ 已完成 | 有始有终，无需跟进 | | 执行中断 | ⚠️ 待跟进 | 有承诺但未执行，责任人明确 | | 链路断裂 | 🚩 悬而未决 | 无人承接，责任方不明确 | | 信息缺失 | ❓ 待确认 | 推断补全，需用户提供原始邮件确认 | **责任归属推断规则**： - 谁提出问题 → 默认责任方（除非明确转移） - 谁承诺回复 → 默认等待方（有明确截止日期优先） - 人员离职/变更 → 交接后的承接方自动继承责任 #### Step 5：矛盾分级与可操作标注 矛盾分为两类： | 类型 | 标记 | 处理方式 | |------|------|---------| | 事实性矛盾 | 🔴 硬矛盾 | 两条陈述直接冲突，必须澄清才能推进 | | 理解性分歧 | 🟡 软矛盾 | 可能是沟通误差导致，先保留给用户判断 | **每个矛盾必须给出**：`建议动作 + 建议询问对象` --- ### 自动降噪规则 **直接忽略**（不进报告）： - 自动回复（Subject 含 "AutoReply"、"Out of Office"、"自动回复"） - 会议邀请/变更通知 - 仅含附件无正文的回执 - 节假日祝福、内部通知 **降噪原则**：降噪是为了减少干扰，不是丢失信息。若一封"感谢邮件"包含实质性内容（如承诺、决策），应保留。 --- ## 4. 知识库：常用技术术语即时解释 在输出过程中，若邮件涉及以下专业术语，自动在相应位置插入简洁解释（不打断报告结构，以脚注形式标注）： | 术语 | 英文 | 解释 | 影响行动 | |------|------|------|---------| | JWT Issuer | JWT Issuer | API 调用方的身份标识符（相当于用户名），JWT 标准中的 `iss` 字段 | 用于本地生成 JWT token | | JWT Secret | JWT Secret | 签名密钥（相当于密码），用于生成不可伪造的请求凭证 | 必须严格保密，用于 token 生成 | | getToken 端点 | getToken endpoint | DragonPass 不提供此端点；token 需客户端用 Issuer + Secret **自行生成** | 若对方要求"调用 getToken"，需纠正为"本地生成 JWT" | | UAT | User Acceptance Testing | 用户验收测试，上线前的真实环境验证 | 需准备测试账号、真实卡号、预期结果 | | POS Query | POS query API | 查询 entitlements（会员权益）的接口，调用后同时解锁 DPI | 每次核销前必须先调此接口 | | DPI | DPI | DragonPass 内部的会员权益 ID，核销时用于标记是哪位会员 | 从 POS Query 响应中获取 | | reqId | reqId | 每笔 API 请求的唯一标识符，用于防重放攻击 | 生成后需确保全局唯一 | --- ## 5. 输出模板（v4.3） ### 阶段一：执行摘要 > **📌 项目当前状态**：[一句话概括] > **最大风险**：[一句话描述最需要关注的问题] > **下一步最重要的事**：[谁 + 做什么 + 什么时候] --- ### 📬 邮件链路深度复盘报告 v4.3 #### 一、项目基础信息 - 邮件主题：[提取 Subject] - 时间跨度：[首封日期] — [末封日期] - 邮件总数：[N] 封 - 核心线程：[线程1] / [线程2] / [线程3] - 整体置信度：High / Medium #### 二、参与方与责任归属 - 甲方 - 人员：[Name]，职责：[描述]，活跃期：[活跃区间]｜当前状态：✅ 在职 / ❌ 已离职 / ⚠️ 已交接 - 供应商 - 人员：[Name]，职责：[描述]，活跃期：[全程/某区间]｜当前状态：✅ 跟进中 / 🔴 无响应 - 交接（如有） - [原负责人] → [新负责人]，交接时间：[Date] #### 三、多线程时间轴（按活跃度排序） > ⚠️ 只输出活跃线程；无实质进展的线程不展示 ##### 🔧 技术线 | 日期 | 发件人 | 动作 | 结果 | 置信度 | |------|--------|------|------|--------| | YYYY/MM/DD | Name | 动作类型：一句话描述 | ✅/⚠️/🚩 | High | ##### 👤 人事务线（若有） | 日期 | 发件人 | 动作 | 结果 | 置信度 | |------|--------|------|------|--------| | YYYY/MM/DD | Name | 交接通知/离职公告：描述 | ✅ 已完成 | High | ##### 📋 运营线（若有） | 日期 | 发件人 | 动作 | 结果 | 置信度 | |------|--------|------|------|--------| | YYYY/MM/DD | Name | 动作类型：描述 | ✅ | High | #### 四、关键决策点 > **[决策名称]** High > 时间：YYYY/MM/DD > 背景：什么情况下做出的 > 结论：各方达成的共识 > 影响：对其他线程或后续行动的连锁影响 #### 五、断点追踪（可直接执行） | # | 事项 | 责任方 | 承诺日期 | 当前状态 | 建议动作 | |---|------|--------|----------|---------|---------| | 1 | 描述 | Name | 日期 | ⚠️ 超期 | 具体该做什么 | | 2 | 描述 | Name | — | 🚩 悬而未决 | 发给谁的邮件内容建议 | #### 六、矛盾与待确认事项 | # | 类型 | 矛盾描述 | 建议核实对象 | 建议核实问题 | |---|------|----------|------------|------------| | 1 | 🔴 硬矛盾 | 描述 | Name | 具体问什么 | | 2 | 🟡 软矛盾 | 描述 | Name | 具体问什么 | #### 七，一句话结论与行动建议 > **一句话结论**：核心项目状态总结 > > **立即可执行的下一步**（按优先级）： > 1. **动作**，找 **人**，截止 **日期** > 2. **动作**，找 **人**，截止 **日期** --- *报告生成时间：[timestamp] | 分析置信度：整体 High / Medium | 标注 ❓ 的结论为推断补全，请以原始邮件为准*

path: eml_cleaner.py
content: #!/usr/bin/env python3
"""eml_cleaner.py - 预处理 .eml 文件，提取干净文本"""

import sys
import email
from email import policy
from email.parser import BytesParser

def extract_clean_text(eml_path, output_path=None):
    """从 .eml 提取纯文本内容"""
    with open(eml_path, 'rb') as f:
        msg = BytesParser(policy=policy.default).parse(f)

    lines = []
    lines.append(f"【发件人】{msg.get('From', '未知')}")
    lines.append(f"【收件人】{msg.get('To', '未知')}")
    lines.append(f"【日期】{msg.get('Date', '未知')}")
    lines.append(f"【主题】{msg.get('Subject', '未知')}")
    lines.append("=" * 40)

    text_count = 0
    if msg.is_multipart():
        for part in msg.walk():
            content_type = part.get_content_type()
            if content_type == 'text/plain':
                try:
                    charset = part.get_content_charset() or 'utf-8'
                    text = part.get_payload(decode=True).decode(charset, errors='replace')
                    lines.append("--- plain text ---")
                    lines.append(text.strip())
                    text_count += 1
                except Exception:
                    pass
    else:
        content_type = msg.get_content_type()
        if content_type == 'text/plain':
            try:
                charset = msg.get_content_charset() or 'utf-8'
                text = msg.get_payload(decode=True).decode(charset, errors='replace')
                lines.append("--- plain text ---")
                lines.append(text.strip())
                text_count += 1
            except Exception:
                pass

    if text_count == 0:
        lines.append("(无可提纯的纯文本内容)")

    result = "\n".join(lines)
    if output_path:
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(result)

    print(f"[INFO] 共找到 {text_count} 段 text/plain 内容")
    print(f"[INFO] 输出长度: {len(result)} 字符")
    if output_path:
        print(f"✅ 已保存到: {output_path}")
    return result

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("用法: python3 eml_cleaner.py <eml文件> [输出文件]")
        sys.exit(1)
    extract_clean_text(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None)


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)