# 产品规则:邮件指令识别与拆分(锁定口径) > **地位**:本页为指令「怎么认、怎么拆、谁可确认」的**产品真源**之一。 > **实现真源**:`src/services/parse/instruction-lexicon.ts`、`split-instructions.ts`、`classify.ts`。 > **约束**:解析链路**禁止**依赖 LLM / Cursor 临场理解;改规则先改本页 + 词表 + 单测。 > **日期**:2026-08-03 --- ## 0. 一句话 主题与正文**都重要、要分开用**;一封多轮邮件里**只有最新一轮指令可确认**;标准四类走业务表单,非标准/软词走「客户指令」;同线程后续回复按**新 UID**再拉,不重做旧信。 --- ## 1. 信号源(三者并列,来源可区分) | 信号源 | 用途 | UI 来源标签 | |--------|------|-------------| | **主题** | 柜号/提单/ETA/「新增预报」「DO请查收」等;可单独成指令单元 | 来自主题 | | **正文** | 按时间轮/转发标签拆段;决定「当前可确认」 | 来自当前正文 / 来自历史引用 | | **附件名** | 补 DO / 贴标清单等;**不能**仅凭正文「DO也同步上传」造空上传 DO | 来自附件名 | 冲突原则: 1. 正文**最新段**已是硬工单(换标/覆盖贴/贴标/拍照/拦截等)→ **压过**主题链路上历史「新增预报」。 2. **同线程主题未变**(仅 Re:/Fw: 前缀变化)时:后续轮次**只认该时段正文**,不得再用首封主题「新增预报」造当前/补缺预报卡;QQ 转发壳剥壳后的主题残片也不算指令。 3. 主题有、正文完全无指令时 → 才可用主题补缺(只读或非当前)。 4. 正文已覆盖同类型 → 主题**不再重复**出一张一样的卡。 5. 正文写「DO 也同步上传」但本封**无真实 DO 附件** → **不出**空的上传 DO 指令。 --- ## 2. 标准四类业务 ↔ UI | UI kind | 典型关键词(见词表) | 确认动作 | |---------|----------------------|----------| | `forecast` | 新增预报、请查收新增预报 | 预报表单(人工确认) | | `transfer` | 新增转仓、转仓(「不转仓」除外) | 批量转仓 / 到仓后降级工单 | | `work_order` | 贴标/换标/覆盖贴/操作指令、拍照、**指令性**拦截、快递单号;mail1 仅柜号+预约码 | 客户留言 SaveForm(人工确认) | | `do_upload` | DO请查收;或真实 `*DO*.pdf` 等 | 上传 DO(人工确认) | 主流程四类均需人工确认,**不**因主题残留「新增预报」自动再写 CC。 > **渠道件数 ≠ 整票指令**:主题里的 `拦截-209件` / `FedEx-29件` 只是渠道汇总;整票真实指令以「拆柜清单更新 / 派送要求」为准。禁止把渠道名写成备注「邮件动作:拦截」。 --- ## 3. 客户指令(非标准 / 软词) 下列**不**硬套工单表单,归 `customer_instruction`(只读展示轮次/正文): - 更新派送单 - 改自提 / 改为仓库自提 / 仓库自提 - 拆柜清单 / 卡转海 / 派送要求 - 按照正常预报接受、帮忙安排派单、安排派单 - 继续暂存 / 暂存等后续 - 有实质内容但无法归入标准四类的段落 --- ## 4. 多轮线程拆分与「仅最新可确认」 ### 4.1 如何切开 正文按下列边界切段(保留分隔标签到后一段,便于取时间/发件人): - `在 yyyy-mm-dd … 写道` / `On … wrote:` - `原始邮件` / `转发的邮件` / `Original Message` / `Original:` - 发件人+发送时间 等邮件头块 **不用**单独的 `Dear` 作为切段点(避免同轮内误切)。 ### 4.2 可确认范围 | 段 | `isCurrent` | 操作 | |----|-------------|------| | 最新一条有实质指令的段 | `true` | 可确认提交 | | 更早的引用轮次(标准四类) | `false` | 只读(如历史预报) | | 更早的软词「客户指令」 | `false` | **独立时间轮要出卡**:按正常预报接受 / 仓库「好的」 / 无法接收;仅丢掉无时间轮的「更新派送单」噪声 | | 纯主题补缺单元 | 一般为 `false` | 只读展示线索 | 同轮内允许多种 UI 并存(例如当前换标 + 历史预报只读)。 `work_order_actions` 仅含「转仓」、或正文已拆出转仓/预报等标准单元时,**不得**再补一张工单卡。 历史引用若正文指令相同(同线程连转同一份「拦截 + 改自提」等,仅抄送折行不同)→ **只保留最新一条**,禁止出 2/3/4 张一样的工单卡。 工单备注须列出本段全部动作:硬词(拦截/贴标)与软词(改自提)并存时都写入「邮件动作」,不得只留拦截。 ### 4.3 跳过 vs 仍展示的轮次 - **跳过(不出卡)**:QQ 转发壳、`发自我的iPhone`、空的 `Original:`、主题残片 - **仍按时间轮出卡(只读、不可确认)**:仓库 ACK「好的 / 收到」;「无法接收 / 产能有限」;「按照正常预报接受 / 安排派单」 - **同轮内 `====` 分组**(如换标 334 件两段 FBA)**不切开**,仍是这一轮一条工单 --- ## 5. 同线程后续指令(拉取幂等) | 情况 | 行为 | |------|------| | 客户在同主题下**新回复**(新 IMAP UID) | **会拉**:新邮件入库、重新拆段;仅最新段可确认 | | 已入库的旧 UID | **不再拉**(`knownUids` / messageId / rawHash 幂等) | | 超出 `IMAP_LOOKBACK_DAYS`(默认 3 天)的过旧回复 | 可能拉不到 | | 新回复未过过滤规则 | 记拉取日志为跳过,不进业务列表 | 旧邮件保持原状态;**不会**因新回复自动把旧预报/旧工单再执行一遍。 --- ## 6. 柜头 / 预报相关(与指令并存) - **提拆派**:预报时 `F_OperationType=0` 表示提拆派服务包,**不是**三个独立管理端「提柜/拆柜/派送」界面。 - **mail1 类**(仅柜号+预约码、无标准关键词):`classify` → `WORK_ORDER`;详情用 `work_order_actions` 补当前工单单元(见 `extractMailInstructions({ workOrderActions })`)。 - 模板表尾「注意:…」是填写说明,**不是**货件行。 - 卡派清单列:附件有则填;UI **隐藏整列皆空**的 FormOrder 列,避免满屏「-」。 - 工单留言内容:取当前指令段,并从 `Dear` / `原箱号` 起,去掉转发头;**禁止**用整封线程正文盖住分段留言。 --- ## 7. 实现与测试对照表 | 规则 | 代码 | 单测(示例) | |------|------|----------------| | 词表 / 软硬分流 | `instruction-lexicon.ts` | `mail4-time-rounds.test.ts` | | 拆段 / 最新可确认 / 主题补缺 | `split-instructions.ts` | `split-instructions.test.ts`、`mail4-time-rounds` | | 正文硬工单压过主题预报 | `classify.ts` | `classify.test.ts`、`instruction-product-rules` | | 线程拆单(Message 级) | `thread.ts` + poller | `thread.test.ts` | | UID 幂等拉取 | `poller.ts` `knownUids` | 集成/烟雾 | | 空 DO 不出指令 | `split-instructions` + `do-upload-extract` | `mail4-time-rounds`、`do-upload-extract` | | 工单留言清洗 | `CcWorkOrderForm` `extractWorkOrderMessageBody` | `four-business.test.ts` | 改口径流程:**改本页 → 改词表/拆分 → 改/补单测 → `pnpm test` 相关文件通过**。 --- ## 8. 与旧文档关系 - `docx/需求规格-邮件自动预报-v0.2.md` §4 类型打分仍有效;**本页补充**「拆分 / 客户指令 / 主题正文分工 / 仅最新确认」。 - 若与旧表述冲突,**以本页 + 上述实现文件为准**,并回写需求规格对应章节。 - Cursor 对话纪要**不是**运行时真源。