You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mail-yubao/docs/邮件指令提取规则.md

372 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 邮件指令提取规则(对照实现)
> **用途**:给产品/运营对照「接下来没见过、格式很怪」的邮件,判断系统会不会拉进库、认成哪类指令、字段从哪来。
> **约束**:解析**不走大模型**,全部是词表 + 正则 + 固定模板。新格式要么命中现有规则,要么漏/错,不会「猜一下」。
> **实现真源**:`src/services/parse/`(词表 `instruction-lexicon.ts`、分类 `classify.ts`、拆段 `split-instructions.ts`、流水线 `pipeline.ts`)。
> **产品口径**:`docx/产品规则-邮件指令识别与拆分.md`(冲突时以代码为准,并回写产品页)。
> **日期**:2026-08-14
---
## 0. 一句话
一封信要变成可确认的业务卡,必须连过四道门:
1. **拉取**:过滤规则 / 业务兜底认为「相关」才入库。
2. **分类**:主题+正文+附件名打分,得到整封主类型。
3. **拆指令**:按时间轮切开,每段映射到 UI 卡(预报 / 转仓 / 工单 / 上传 DO / 客户指令)。
4. **抽字段**:柜头来自主题模板或正文标签;货件行几乎只来自 **xlsx/xls/csv**(卡派表或贴标指令单)。
任何一步没命中,后面再「看起来像业务」也不会自动补全。
```mermaid
flowchart TD
A[IMAP 新信] --> B{拉取过滤}
B -->|SKIP_FILTER / 黑名单| Z[不入库]
B -->|入库| C[classify 打分]
C --> D[split 拆时间轮]
D --> E{有表格附件?}
E -->|xlsx/xls/csv| F[卡派表 / 贴标指令单]
E -->|无表| G[仅柜头 + 空货件]
F --> H[详情指令卡]
G --> H
H --> I[人工确认写入 CC]
```
---
## 1. 拉取:什么样的信根本进不来
实现:`src/services/imap/pull-filter.ts` + `classify.ts` `isBusinessRelevantMail`。
| 顺序 | 条件 | 结果 |
|------|------|------|
| 1 | 发件人在黑名单,或主题/正文命中关键词黑名单 | **不拉** `SKIP_BLACKLIST` |
| 2 | 发件人在白名单 | **拉** |
| 3 | 主题/正文命中设置页关键词 | **拉** |
| 4 | 过滤全空时走业务兜底 | 见下表 |
| 5 | 已配置过滤但以上都不中 | **不拉** `SKIP_FILTER` |
业务兜底(过滤全空才用)任一为真即拉:
- 分类结果不是 `UNKNOWN`
- 主题/正文/附件名命中:`新增预报|转仓|换标|覆盖贴|贴标|拍照|拆柜清单|卡转海|拦截|改自提|留仓|卡派|装箱|货件清单|船名|航次|柜型|40HQ|20GP|ETA|预报资料|拆柜|柜号|快递单号|工单|DO|DO请查收`
- 文本或附件名里有 **ISO 柜号** `[A-Z]{4}` + 7 位数字(如 `WHSU8127240`)
- 附件名像卡派/装箱/packing/清单/预报/转仓/换标/贴标/货件/DO
- 有表格附件 **且** 正文弱物流词:`资料|清单|柜|仓|FBA|shipment|container`
**未见过的信在这里就会丢:**
- 纯英文、无柜号、无上表关键词(例如 “please book this container”)
- 只写「请安排」「帮忙处理」而无标准词
- 柜号不是 ISO 四字母+七数字(短柜号、带校验位空格、中文「柜」无号)
- 设置了关键词过滤,但新客户用了别的叫法,又不在白名单
---
## 2. 整封分类(主类型)
实现:`classifyMail`。优先级(同分取更靠前):
`DO_UPLOAD` > `NEW_CONTAINER` > `TRANSFER` > `WORK_ORDER`
总分 **< 40 → `UNKNOWN`**(列表里常变成忽略/无法确认)。
| 信号 | 加分 | 落到 |
|------|------|------|
| `DO请查收` / 请查收 DO / 上传 DO / 文件名独立 `DO` 的 PDF | 55(无真实 DO 且主题已是拆柜清单则降到 25) | DO_UPLOAD |
| `新增预报` / `请查收新增预报` | 50 | NEW_CONTAINER |
| 附件名 `数据模版` 或 `预报资料\|货件清单\|装箱清单` 的表格 | 45(已有预报分则 +15) | NEW_CONTAINER |
| `新增转仓` 或 `转仓`(排除 `不转仓`) | 50 | TRANSFER |
| 硬工单:贴标/拦截/拍照/快递单号,或换标/覆盖贴/贴好拍照 | 45 | WORK_ORDER |
| 软词:拆柜清单 / 卡转海 / 派送要求 / 改自提 / 更新派送单 / 留仓 | 45 | WORK_ORDER |
| 附件名 `换标\|贴标指令` | 30 | WORK_ORDER |
| 正文最新段已是硬工单 | 压过主题上的历史「新增预报」/DO | WORK_ORDER |
| 分类仍 UNKNOWN,但有 ISO 柜号或「预约码」 | 40 兜底 | WORK_ORDER(mail1 类) |
**会错的典型:**
- 主题还挂着「新增预报」,最新回复是换标 → 会压成工单(这是刻意的)。
- 「拦截-209件」只是渠道汇总,**不当**整票拦截;真正拦截必须正文另有「拦截」且不是 `拦截-数字件`。
- 文件名叫 `卡派资料.xlsx` **不会单独**打成预报,还要有「新增预报」或「数据模版/装箱清单」等。
- 英文 “new booking / transload / relabel” **不加分**。
---
## 3. 多轮拆段 → 指令卡
实现:`split-instructions.ts` + `instruction-lexicon.ts`。
### 3.1 怎么切开
按边界切(保留分隔标签到后一段):
- `---- 原始邮件 / 转发的邮件 / Original Message ----`
- `在 yyyy-mm-dd … 写道` / `在 yyyy年m月d日 … 写道` / `On … wrote:`
- `发件人` + `发送时间/日期` 邮件头
- 行内嵌套 `> 在 … 写道`(QQ 引用)
**不用**单独的 `Dear` 当切段点。
弱切(独立成行才切):`新增预报|请查收新增预报|新增转仓|更新派送单|拆柜清单|派送要求|换标|覆盖贴|贴标指令|出库|留仓|拆分|DO请查收|请查收 DO`
### 3.2 关键词 → UI kind
| UI | 关键词(节选,见词表) | 确认动作 |
|----|------------------------|----------|
| `forecast` | 新增预报、请查收新增预报;正文 `柜号:XXXX1234567 派送单,请查收`;提柜+柜型+卡派/拆柜 | 预报表单 |
| `transfer` | 新增转仓、转仓(`不转仓` 除外) | 批量转仓;CC 到仓闸门失败则降级工单 |
| `work_order` | 贴标/换标/覆盖贴/操作指令、拍照、拦截(非渠道件数)、快递单号;软词拆柜清单/卡转海/派送要求/改自提/更新派送单/改地址派送/点前送达 | 客户留言 / 转渠道 / 拆分 / 出库 |
| `do_upload` | DO请查收、请查收 DO、文件名独立 DO 的 PDF | 上传 DO |
| `customer_instruction` | 按照正常预报接受、帮忙安排派单、继续暂存 | **只读**,不提交 |
跳过(不出卡):QQ 转发壳、`发自我的iPhone`、空 Original、主题残片。
**可确认的一般是最新一轮有实质指令的段。** 历史预报/转仓可只读展示;历史转仓现在也允许单独执行。
同线程主题只变 Re:/Fw: 时,**不得**再用首封主题「新增预报」给最新轮再造一张预报卡。
### 3.3 附件挂到哪张卡
实现:`match-segment-attachments.ts`。顺序:
1. 主键交叉:附件名与本段出现同一 `FBA…` / `YT…` / `X0…`(FNSKU)/ `WL…` 唛头。**不用 ISO 柜号**给工单分附件。
2. 正文/主题出现该文件主名(去扩展名、去 `(1)`)。
3. 仅**当前可确认段**角色兜底:文件名含卡转海 / 换标|覆盖贴|操作指令|贴标交付。
4. `*DO*.pdf` 只进上传 DO 卡。
5. 卡派/装箱 xlsx 是解析源,默认不进工单附件。
6. 跳过 `*邮箱.pdf` / QQ 截图。
---
## 4. 预报(NEW_CONTAINER / forecast)
### 4.1 主题柜头:两种互斥模板
**A. 客户+提单预报(`bl-plus-template.ts`)**
剥掉 `Fw:/转发/新增预报N:` 后,用 **`+`(或全角+)** 分段。无 `+` 才按空格切,二者互斥。
常见形态:
`新辰泽+WHLC027G597465+WHSU8127240+洛杉矶+40HQ+EDT2026.04-25+ETA2026.05-15+船名航次HMM EMERALD 013E+提拆派+不带托架`
能抽出:客户名、提单、柜号、目的港(**美国港口白名单**)、柜型、ETD/ETA、船名航次、服务(提拆派/直送)、是否带车架。
客户名不得是柜号/提单/「贴标/转发/柜号」等。目的港未进白名单(洛杉矶/长滩/纽约/萨凡纳/奥克兰/西雅图/塔科马/休斯顿/迈阿密/查尔斯顿等)→ **不写港口**。
标签型(首段就是 `柜号:` / `提单号:`,客户不在提单前):
`Fw: Fw:回复:柜号:TRHU8524770+提单号:EGLV143661466958+封号:EMCEJJ0635+YWZD+不带车架+整柜直送`
| 段 | 落到 |
|----|------|
| `柜号:TRHU8524770` | 柜号 |
| `提单号:EGLV143661466958` | 提单 |
| `封号:EMCEJJ0635` | **备注**(预报 UI 无封号格) |
| `YWZD` | 客户(排除已知字段后仅剩 1 个独立缩写) |
| `不带车架` | 车架 = 不带 |
| `整柜直送` | 业务类型 = 直送 |
**带「名称:值」前缀的段,不得去抢客户名。** 前缀已经说明这是有名字的字段;对不上预报 UI 的(封号/铅封/预约码等)一律进备注。无前缀的短码(如 YWZD)才参与客户竞争。
**B. 工单/拆柜清单主题(`ops-subject-template.ts`)**
形态:`柜号+提单+船名航次:…+ETA:…+FedEx-29件+拦截-209件+…`
一旦命中 B,**不再当 A 的客户+提单预报**。渠道 `Xxx-N件` 只作汇总,不是整票指令。
### 4.2 正文柜头补全(`extract-container-header.ts`)
只认**带标签**的字段,不扫全文乱截:
| 字段 | 规则 |
|------|------|
| 柜型 | `\b(45HC\|45HQ\|40HC\|40HQ\|40GP\|20GP\|20HQ\|45GP)\b` |
| ETA | `ETA\|预计抵港\|抵港日\|到港` + 日期 |
| ETD | `EDT\|ETD\|预计开船\|开船日\|离港` + 日期 |
| 提单 | `提单(号)\|B/L\|BLCopy` + 号;否则跳过 ISO 柜号取裸 `ABCD`+数字串 |
| 装货港 | `装货港\|起运港\|POL` |
| 目的港 | `目的港\|卸货港\|码头\|POD` + 白名单 |
| 船司 | 显式「船司」> 提单前 2–4 位前缀 > 正文别名 > 船名首词 |
| 车架 | `带车架/带托架` vs `不带车架/不带托架` |
| 操作 | `提拆派` → 0;`直送` → 2 |
| 运输方式 | 柜型/船名航次/海运提单前缀 → 海运;否则航司别名 → 空运 |
| 封号/铅封 | `封号\|铅封\|封条` + 号 → **备注**(预报 UI 无此格) |
**正文没有这些标签时,柜头对应格就是空的**(例如只写 “arriving LA next week”)。
附件解析结果优先;邮件文本只补空字段。
### 4.3 货件行(几乎只认表格)
选表优先级(文件名):`数据模版|提拆派|卡派资料|卡派清单|装箱清单|packing` > 其它表格 > `换标|覆盖贴|贴标|操作指令`。
**卡派表**(`packing-list.ts`):
- 优先 sheet 名含「卡派」,否则第一张表。
- 前 15 行找表头:同时有「渠道」+(件数或 CTNS)+(仓库或 FBACode)。
- **必有列**:仓库ID/`FBACode`、渠道/`Transporter`、件数/`CTNS`。缺一则 `PARSE_FAILED`,再尝试贴标指令单。
- 「注意:…」模板说明行不是货件。
- 渠道映射:卡派→TRUCK,UPS/FEDEX/DHL/USPS/SF/4PX/YunExpress,自提,留仓/存仓,换标,扣货/拦截。超 40 字或「注意:」开头不当渠道。
列名别名见代码 `HEADER_ALIASES`(中英混排、`Custmer Ref No.` 错拼也认)。
**贴标指令单**(`label-instruction.ts`)表头含「贴标项目 / 详细操作指令 / 新FBA / 覆盖贴 / 换标」或同时有柜号+操作箱数。
`lineage.template=label-instruction` 时:**预报货件列表必须空**,换标行不当 SaveContainer 货件。
无表格:预报可仅柜头进待确认;货件列表空。PDF/图片卡派表 **不会**当货件解析(转仓截图 OCR 是另一条路)。
### 4.4 柜头 Instruction / 备注(不是货件行备注)
| 表单格 | CC 字段 | 实际来源 |
|--------|---------|----------|
| Instruction | `F_Instruction` 拆柜指引 | ① 预报正文段(含新增预报/提单/柜型/ETA/船名,且不含换标/贴标)截 500 字;② 否则 `船名航次 xxx`;③ 贴标单则用首行操作说明(预报卡会改回 ①②) |
| 备注 | `F_MemoRemark` | **优先**:主题/正文里带标签但对不上预报 UI 的字段(`封号:EMCEJJ0635`、`铅封:…`、`预约码:…` 等),多段用 ` / ` 拼接。**否则回退**:`客户名 / 提拆派或直送`。贴标 sheet 标题含贴标/换标/指令时预报卡会清掉。 |
| 货件「行备注」 | `F_Remark` | 卡派列「备注 / Remark / Instruction / 配送时段」 |
**未映射标签 → 备注(硬规则)**
- 段内已有 `名称:值`(或 `名称:值`)前缀,说明这是有名字的字段,**禁止**拿去填客户。
- 名称能对上预报 UI(柜号/提单/柜型/ETA/ETD/船名航次/目的港/装货港/船司/客户/运输方式/业务类型)→ 填对应格。
- 名称对不上(封号、铅封、预约码,以及其它未知标签)→ 整段原文进备注。
- 无前缀的剩余短码:仅当排除已知字段后**恰好剩 1 个**才当客户;剩多个则全部进备注,不强猜客户。
- 已占用的「柜号:」「提单号:」会从备注里剥掉,避免和柜号/提单格重复;**封号保留**。
去 QQ/Outlook/iPhone 转发壳后再填 Instruction。
---
## 5. 转仓(TRANSFER)
- 关键词:`新增转仓` / `转仓`,排除 `不转仓`。
- 货件:卡派表行备注「转 POC2」「转VGT2」等 → `extractTransferPairs`。
- 截图/OCR 表(`transfer-instruction-table.ts`):
- 标准:`地址 PSC2 箱数 7 … 仓库重定向 POC2`
- 宽松:同行 `地址 X … 仓库重定向 Y`
- 仓码归一:FWA4 / PSC2 / POC2 / VGT2(含 OCR 错写 EWAD、WAAD4 等)
- 确认时查 CC 是否已到仓;不可转则自动改提交工单。
**会漏:** 英文 transload、没有「地址/仓库重定向」的自由句、目标仓不是上述几种代码、只在 PDF 里画了箭头。
---
## 6. 工单(WORK_ORDER)
硬动作:`贴标` `拦截` `拍照` `快递单号`,同义 `换标` `覆盖贴` `贴好拍照`。
软词(也走工单卡,不硬套某张 CC 工单界面):拆柜清单、卡转海、派送要求、改自提、更新派送单、改地址派送、点前送达。
mail1:主题只有柜号+预约码、无标准词 → 分类工单,详情用 `work_order_actions` 补卡。
留言取**当前段**,从 `Dear` / `原箱号` 起,禁止整封线程盖住本段。
面板分流(管理端/客户端):拦截/改渠道 → 转渠道;数量拆分 → Split;出库/贴标文件 → 出库;其余 → 客户留言。
---
## 7. 上传 DO
文件名:`DO请查收` / `提货单` / `delivery order`,或 **PDF 且独立单词 DO**(`WHSU8127240-DO.pdf`、`xxx-DO.pdf`)。
正文写「DO 也同步上传」但**没有**这类附件 → **不出**空 DO 卡。
柜号:已有柜头 > 文件名 ISO 柜号 > 主题/正文第一个 ISO 柜号。
`.docx` / 图片扫描件不叫 DO、文件名是 `DeliveryOrder_final.pdf` 无独立 `DO` 词 → 可能认不出。
---
## 8. 金样覆盖(已按这些规则调过)
| 样例 | 期望 | 规则依赖 |
|------|------|----------|
| 邮件1 仅柜号 TIIU8073522 | 工单兜底 | ISO 柜号、无关键词 |
| 邮件2 MATU + 卡派 xlsx | 转仓 | `新增转仓` + 卡派表 |
| 邮件3 拆柜清单+渠道件数 | 工单 | 工单主题模板 + 软词;渠道件数不是拦截指令 |
| 邮件4 预报+换标+DO | 工单为主,预报/DO 可并存 | 最新段硬工单压预报;卡派表优先于换标表;DO 文件名 |
这四封能过,**不代表**换一套客户模板也能过。
---
## 9. 覆盖缺口清单(用来对照新邮件)
把新邮件贴到下面问:会在哪一层失败?
### 9.1 进不了库
- [ ] 没有 ISO 柜号,也没有第 1 节关键词
- [ ] 纯英文 booking / sailing / transload / relabel / hold
- [ ] 已配关键词过滤,新叫法不在列表、发件人不在白名单
- [ ] 主题乱码(GBK/UTF-8 错码)导致关键词对不上
### 9.2 能进库但类型错 / UNKNOWN
- [ ] 同义说法:新增订舱、做预报、改仓、贴新标、hold cargo、stop shipment
- [ ] 「转」但没写「转仓」;「不要转仓」以外的否定
- [ ] 预报附件不叫数据模版/装箱清单/卡派资料
- [ ] 主题预报 + 正文最新段换标(会变成工单,可能符合产品,但预报卡不是当前)
- [ ] 预报与 DO 分数接近(close_call),以优先级表为准
### 9.3 类型对但字段空
- [ ] 主题不用 `+` 分隔(用空格/逗号/表格/HTML)
- [ ] 港口不在美国白名单(如西雅图别称未收录、欧洲港)
- [ ] 柜型写成 40'HQ / 40 尺柜 / HC 无数字
- [ ] 日期无 ETA/ETD 标签(只写 5/15)
- [ ] 船名不写「船名航次」四字
- [ ] 只写「备注:」标签、没有 `封号:` 这类字段名 — 当前**不**从「备注:」抽备注;未映射的 `名称:值` 才会进备注
- [ ] 封号写在无 `+`、无冒号的自由句里(如 “seal EMCEJJ0635”)
### 9.4 有附件但货件为空 / 错表
- [ ] 卡派是 PDF、图片、Word,不是 xlsx/xls/csv
- [ ] 表头不叫渠道+件数+仓库(例如全英文 Warehouse / Qty 但没有渠道)
- [ ] 缺仓库ID/渠道/件数三列之一
- [ ] 只有换标指令单被当成预报行(文件名若不像卡派,可能先吃换标表;预报卡会清空货件)
- [ ] 表在 zip 内层路径/加密压缩包
- [ ] 附件过大被拒
### 9.5 转仓 / 工单 / DO 细节
- [ ] 转仓目标仓不是 PSC2/POC2/FWA4/VGT2
- [ ] 工单附件只靠柜号关联(规则故意不用柜号分发)
- [ ] DO 是 JPG/扫描件,或文件名无独立 DO
### 9.6 编码与客户端壳
- [ ] QQ 转发壳、iPhone「发自我的」会剥;剥完若没剩指令词则跳过该段
- [ ] 正文 HTML 未转纯文本时,标签可能打断正则
- [ ] 繁体:新增預報、轉倉、貼標 — **词表是简体**,繁体会漏
---
## 10. 扩展新格式时改哪里
不要在详情页临场「理解」一封信。按层改:
| 缺口 | 改 |
|------|----|
| 拉不进来 | 设置页关键词/白名单,或 `BUSINESS_TEXT_RE` / `isBusinessRelevantMail` |
| 类型错 | `classify.ts` 加分 + `instruction-lexicon.ts` 词表 + 单测 |
| 拆不出卡 | `KEYWORD_RULES`、切段边界、`split-instructions` |
| 主题柜头 | `bl-plus-template` 或 `ops-subject-template`(先判断像 A 还是 B);未映射标签进 `memo_bits` → 备注 |
| 正文柜头 | `extract-container-header.ts` 标签正则;港口 `us-ports.ts`;船司 `carrier-aliases.ts` |
| 卡派列名 | `packing-list.ts` `HEADER_ALIASES` |
| 贴标表 | `label-instruction.ts` `LABEL_HEADER_ALIASES` |
| 转仓 OCR | `transfer-instruction-table.ts` + 仓码归一 |
| DO 文件名 | `do-upload-extract.ts` |
| 附件挂段 | `match-segment-attachments.ts` |
流程:**先在本页记下「哪一层缺口」→ 改对应文件 → 补 `tests/unit` 或 fixtures → `pnpm test` 相关文件。**
---
## 11. 结论(给评审用)
当前规则覆盖的是:**简体中文 + 固定口头禅 + ISO 柜号 + `+` 主题模板 + 中英列名接近金样的 xlsx**。
对「还没拿到的邮件」:
- **同类客户、换柜号/换船名/换附件内容**:一般能覆盖。
- **换一套邮件习惯**(英文、繁体、无 `+`、无「新增预报」、PDF 清单、新仓码、新列名):**不能默认覆盖**,会按第 9 节某一层失败,需要加词或加模板,而不是调模型。
建议每来一类新失败信:先标失败层(拉取 / 分类 / 拆段 / 柜头 / 表格),再决定是加关键词还是加模板,避免一条超级正则吃所有格式。