24 KiB
M012 酒店预订邮件识别到人工确认端到端 V0.1
| 项目 | 内容 |
|---|---|
| 文档状态 | V0.1 已实现并验收;2026-08-08 补充 BR00 订单卡双区块后端收口 |
| 业务基线 | BR00-BASELINE-1,高于本仓库旧样例、旧 prompt 和旧字段语义 |
| 适用范围 | 员工上传真实 .eml 或 AgentBus 投递邮件后,到任务卡关键参数被用户确认为止 |
| 非适用范围 | 确认后的 Opera / PMS API 调用、客户回复、Invoice、独立 Manual RateCode、Rooming List 名单解析 |
| Catalog 版本 | booking-catalog-v20260808 |
| Parser 版本 | booking-email-parser-v0.1 |
本次“已实现并验收”指三封指定真实邮件覆盖的固定渠道纵向切片:普通员工手工导入、LIANTAI/QBD workbook 确定性解析、V4 建卡、字段复核与人工确认。第 4 节仍完整保存 BR00 业务语义,但未被这三封样本覆盖的 Allotment、独立 Rooming List、独立 Payment、图片语义和统一 Booking Agent fallback,不因本次切片通过而宣称已完成;其现状和边界见第 15 节。
1. 背景与架构位置
AgentBus 同时承担邮件消息入口和 Agent 平台。本项目不能把“附件解析”孤立成一次文件读取:邮件正文、当前回复、历史往来、附件版本和本次附件中的更新行共同决定业务动作。
本期沿用七层架构,并把邮件入口耦合进去:
- 信息系统接入与编排:接收 AgentBus 邮件或员工上传的
.eml,建立 SourceMessage、附件摘要、幂等键和处理批次。 - 材料预处理:拆分 current 与 quoted history,识别附件版本,对固定渠道 Excel 只选择本次候选业务行,并保留 sheet/行号/底色等证据。
- 确定性 Parser + Catalog:用版本化业务目录把渠道字段、动作别名、房型写法和数值编码归一为标准事实;不能可靠解析的字段保持未解决,不猜测。
- 当前态与邮件上下文:按会话读取历史 SourceMessage 和既有业务任务,向业务识别提供“当前邮件事实 + 必要历史事实”,历史内容不重复触发动作。
- 业务识别:程序先处理固定渠道可确定部分;失败或存在语义歧义时,把隐私最小化的材料交给 Booking Agent。输出业务事件、关联事件和通知,不直接调用 PMS。
- 确定性校验与人工确认:同一 Catalog 校验程序或 Agent 输出,生成可编辑任务卡、缺失字段、告警和证据;即使全部字段符合目录,也必须由用户确认。
- 执行适配器:确认后调用 PMS/Opera,明确不在 M012 V0.1 范围。
Catalog 不单独构成业务层。它是第 3 层的版本化确定性知识,同时被 Parser、第 5 层 Agent reference 和第 6 层校验器消费。三者必须记录同一个 Catalog 版本,避免同一来源词在不同环节被翻译成不同业务代码。
2. 目标
- 提供普通员工可用、受现有权限保护的
.eml导入 API 和可点击页面,不依赖 Debug Key。 - 保存一封实际邮件一次;相同实际邮件重复投递命中幂等,分别发送的邮件分别处理。
- 对固定渠道 Excel 按“业务范围内整行均有非白色底色”选择候选行,而不是任一单元格有颜色。
- 将候选行确定性解析成统一事实,生成 New / Update / Cancel 及 Trace、Rooming List、Payment 或 General/Risk 通知。
- 复用 SourceMessage 与 Reservation V4 任务卡、目录 lookup、人工复核、确认和审计主线。
- 在 UI 展示邮件证据、解析证据、告警、关键字段和确认阻断;用户可修正允许字段并逐卡确认。
- 用三封 2026-08-08 指定真实邮件做只读验收输入,并用去隐私合成 fixture 建立自动化回归。
- 提供 PostgreSQL 项目专属 Schema
th_hotel_booking的版本化迁移与验证脚本;远程执行前必须只读确认无同名冲突及权限边界。
3. 非目标
- 不在本期执行 PMS/Opera 操作,也不伪造执行成功。
- 不自动回复邮件、发送通知或修改 AgentBus 外部状态。
- 不以 Tour Code 代替 Group Code,也不根据样例猜 Account / Market / Source / Rate Code 映射。
- 不把价格作为前端结构化确认字段;价格只参加未来 Rate Code 映射并留在来源证据中。
- 不解析 Rooming List 旅客名单、不导入旅客信息、不做名单差异比对。
- 不将真实邮件、真实附件、旅客信息、邮箱地址或数据库密码提交到 Git。
4. 统一业务语义
4.1 生命周期与任务
订单生命周期事件:
NEW_BOOKINGUPDATE_BOOKINGCANCEL_BOOKING
同订单可附带的独立任务卡:
TRACE_RESERVATION_NOTESROOMING_LISTPAYMENT
来源邮件级通知:
S10General:整封当前邮件没有任何支持的业务任务时最多一条。S99Risk:当前邮件存在无法分类或高风险歧义时最多一条;已识别的清晰任务仍继续创建。
已知类型但缺字段时保留原类型卡并进入 REVIEW_REQUIRED,不得降格成 Risk。
4.2 名称和订单字段
Tour Code是Name的一个可能来源值。Name of Group、Group Name在本期统一为Group Name = Block Name。Group Code是独立字段,不能由 Tour Code 推导。- 一个清晰预订分组生成一个订单任务;多个 Name、多个房型、多个日期段可以属于同一订单,不按 Name 拆卡。
- New Booking 的实际总房数
< 5为FIT,>= 5为GROUP。 - 所有订单相关任务均有 Basic Information 与 Room Information。
- Basic Information 只允许由已确认发件人映射生成;映射未配置时 Account/Market/Source 留空并阻断确认。
4.3 New Booking 确认条件
通用必填:
- Booking Type
- 一个或多个 Name
- Arrival Date、Departure Date;Nights 由日期派生
- 至少一个 Room Type + Quantity
- 一个订单级 Rate Code
Group 额外必填:
- Group Code
- Group Name / Block Name
Rate Code 根据公司、房型、价格、早餐信息映射,但当前映射仍待配置。零候选或多候选都保留 New Booking,让用户选择并阻断确认;系统不得自动选择。
4.4 Update、Cancel 与关联任务
AMEND、AMD、AMEND TO等归一为 Update;旧 Group Code → 新 Group Code 仍视为同一订单链。- 每封实际 Update 邮件、每个订单生成一张新的 Update 卡,不按历史内容做业务去重。
- Cancel 只在未来 PMS 执行成功后结束订单;本期只确认 Cancel 动作参数。
- Allotment:N 个实际团生成 N 张 New 卡,并生成一个共享来源扣减/取消动作;来源动作失败不阻断实际团。
- Extra Bed 是 Trace,不是 Room Type;同一 Group Code、同一当前邮件的多个 Trace item 合并一张卡。
- Trace 的 Department 必须由用户选择 FO、HSK 或 FO+HSK 才能确认。
- Rooming List 与 Payment 的原生事项卡仍是通知型卡,不解析名单、不核验付款;其 companion Room 只补订单上下文,不改变该业务边界。
4.5 订单任务双区块与 companion Room
- 每个可定位订单的 Order Task 固定包含一张
BASIC_INFORMATION;New、Update、Cancel、Trace、Rooming List、Payment 都必须同时具备ROOM_INFORMATION,General/Risk 来源通知不适用。 - New、Update、Cancel 继续由生命周期 event 直接生成 Room Information。若同一
source_message + order_ref已有任一生命周期 event,不再为同组 Trace、Rooming List、Payment 重复生成 Room。 - 若一个 Order Task 只有 Trace、Rooming List、Payment,则以后端接收顺序中的首个关联 event 生成一张共享 companion Room,并保留该 event 的类型、transition 和
source_event_index以便追溯;不能伪装成新的UPDATE_BOOKINGevent。 - companion Room 只展示同订单已确认完成态或前置 New 计划完成态:
current_values与final_values使用该投影,proposed_values为空,change_summary为空;辅助 event 自身字段不得被当成房间变更。 - 同一 Order Task 的多张业务卡仍分别确认;Basic Information 保持前置门禁。Trace、Rooming List、Payment 专属卡、附件安全边界和确认副作用不因 companion Room 改变。
- 本增量只改变新建订单任务的 intake 行为;已持久化且已有卡片的历史 V4 Order Task 受现有幂等门禁保护,不在重放时隐式补卡。若存量也要补齐,必须另做可审计、可回滚的受控回填。
5. 邮件与上下文边界
5.1 current/history
- 当前邮件的正文、当前附件和明确的当前指令可以触发业务。
- quoted history 只用于解释当前语义、继承订单身份和检测重复,不可再次触发旧动作。
- 没有附件时不经过 Excel 预处理,直接进入上下文组装和业务识别;并非跳过 SourceMessage 接入。
- 当前正文仅有标题/签名、但附有明确更新附件时,附件是本次动作核心证据。
5.2 附件版本
- 当前邮件明确写
REV.n、use this file等版本指令时,优先当前附件。 - 当前版本没有严格候选行或只有部分底色时,可以和同会话前版本做差异,差异只作为复核证据,不直接成为自动执行事实。
- 同一事实再次出现时仍展示重复/陈旧告警,由用户判断,不静默吞掉新邮件。
6. 固定渠道预处理
6.1 共同底色口径
- 只认单元格底色,不认字体颜色、批注、筛选或条件格式推测。
- 白色、默认色、无填充不算。
- 只检查渠道 Catalog 定义的业务列范围;该范围内每个业务列都必须有非白色底色,才是
STRICT_CURRENT_CANDIDATE。 - 一条渠道业务记录可以由 anchor row 与后续 continuation rows 组成;Tour Code/酒店明细在 anchor,当前动作在后续行时,以该 logical record block 的业务列底色并集判断完整性,并同时记录 anchor/action row。不得要求所有字段出现在同一物理行。
- 只有部分业务列有底色时记为
PARTIAL_FILL_REVIEW_EVIDENCE,不自动生成事实。 - 表头颜色不触发业务行。
6.2 渠道 Profile
| Profile | 表头 | 业务列 | 动作列 | 主要来源列 |
|---|---|---|---|---|
LIANTAI_FIT |
3 | A:F | F | Tour Code=B;酒店明细=D/E |
QBD_MONTHLY |
3 | B:H | H | Tour Code=C;酒店明细=E |
LIANTAI_UPDATE |
2 | A:I | G | Tour Code=A;酒店明细=D;状态=H;备注=I |
动作别名必须由 Catalog 版本管理,至少包含:
- New:
NEW BOOKING - Update:
UPDATE、AMEND、AMD BOOKING、AMEND TO - Cancel:
CANCEL、CXL、ยกเลิก
不得为了“程序识别”无限穷举整句。Parser 先分离动作核心 token、动作日期和自由文本,再用有限别名字典归一;不能可靠归一时降级 Agent 或人工复核。
6.3 当前日期与异常年份
- 候选行的动作 marker 优先按日/月与邮件接收或发送日期匹配。
- FIT 文件若因 Excel 自动填充出现 2026、2027……2041 的异常连续年份,日/月匹配的行仍保留为候选,并加
ACTION_DATE_YEAR_ANOMALY。 - 异常年份不能作为真实业务年;入住/离店年份以邮件业务日期和 sheet 月份解析,并在不唯一时留待复核。
- QBD 中严格底色但动作日期早于当前邮件日期的行保留证据并加
STALE_STRICT_CANDIDATE,不得静默当成本次唯一事实。
7. Parser 与 Agent 分工
7.1 确定性 Parser 输出
Parser 输出事实,不输出 PMS 命令:
{
"catalog_version": "booking-catalog-v20260808",
"parser_version": "booking-email-parser-v0.1",
"source": {
"attachment_name": "safe-name.xlsx",
"profile": "LIANTAI_UPDATE",
"sheet": "safe-sheet",
"row_number": 57,
"selection_status": "STRICT_CURRENT_CANDIDATE"
},
"action": "NEW_BOOKING",
"names": ["TOUR-CODE-VALUE"],
"group_code": null,
"group_name": null,
"arrival_date": "2026-08-12",
"departure_date": "2026-08-15",
"room_items": [
{
"source_label": "U-TWN8.5",
"room_type_code": "RM3",
"room_count": 12
}
],
"rate_code": null,
"warnings": ["RATE_CODE_UNRESOLVED"]
}
固定渠道优先走程序:
- Profile、严格候选行、动作和字段均可确定时,由 Parser 生成标准事实。
- 某个字段失败时保留已确定字段,标注具体 warning;不丢弃整行。
- 需要自然语言语义、图片、复杂历史关系或未知格式时,将最小必要材料交给 Booking Agent。
- Agent 输出仍必须经过相同 Catalog 版本的确定性校验和人工确认。
7.2 初始房型映射
当前可确定映射:
U-DBL、Sup DBL→RM2U-TWN、Sup TWN→RM3U-TRP、Sup TRP、TRP→RM2DBL SUITE→SU1- 明确
TWN SUITE→SU2 FAM 6+4→RM4- Family 3/4 →
SU3
ST:1卧双标 TWN 等未被当前目录唯一覆盖的变体必须留空并复核。即使不同原始房型映射到同一 PMS code,也必须分别保留 raw label 与数量,不能按 PMS code 合并。
8. 后端接口
8.1 普通员工 EML 导入
POST /api/reservation/booking-email-intakes
multipart/form-datafile:必填,只接受.emlhotel_id:可选,仍以当前登录用户酒店上下文校验- 权限:
RESERVATION_TASK_EDIT - 文件大小遵循 Spring multipart 和本模块安全上限
- 不接收 Debug Key,不直接接受外部附件 URL
成功返回 HTTP 201:
{
"source_message_id": "123",
"duplicate": false,
"status": "TASKS_CREATED",
"catalog_version": "booking-catalog-v20260808",
"parser_version": "booking-email-parser-v0.1",
"order_task_ids": ["501"],
"source_notification_ids": [],
"warnings": [
{
"code": "RATE_CODE_UNRESOLVED",
"message": "Rate Code 需要人工选择"
}
]
}
重复投递返回同一 SourceMessage 和已有任务/通知,不重复创建。
8.2 内部 V4 契约增量
names[]是 Agent/Parser 的标准数组;V0.1 前端投影为换行分隔names_text便于编辑。group_code与group_name分别保存;兼容读取旧group_block_name/fit_name,新入口不再错误默认。booking_type、group_code、group_name、names_text、日期、房型数量、Rate Code 进入 Room Information 的最终值。nights为派生只读字段。block_id不展示。- Confirmation Number 仅当当前邮件明确提及时展示。
recognition安全块保留 profile、sheet、anchor row、action row、版本、选择状态和 warning code,不包含完整原始行、附件 URL 或旅客隐私。- 新订单在确认前允许
order_id/ target binding 未解决;人工复核不得强制用户输入已存在本地订单 ID。Update/Cancel 等既有订单动作仍要求可靠归属。 - V4 intake 必须按 4.5 的规则保证每个订单任务具有 Room Information;这是同一 Order Task 的展示/确认上下文补齐,不要求 Parser 或 Agent 在 Trace、Rooming List、Payment event 中复制完整房间字段。
GET /api/reservation/order-tasks/{orderTaskId}对 companion Room 返回与生命周期 Room 相同的安全display_payload.room_information和fields[]结构;辅助 event 采用 current-only 模型,不返回 Agenttarget_order、邮件正文、附件 URL 或原始 evidence。
9. 前端验收
新增路由:/reservation/email-intake,权限 RESERVATION_TASK_EDIT。
页面必须是真实可操作页面并连接后端:
- 点击或拖放选择一封
.eml;显示文件名、大小和移除/重新选择。 - 导入按钮有 idle、uploading、success、partial warning、duplicate 和 error 状态。
- 成功后展示 SourceMessage ID、解析版本、任务数、通知数和 warning。
- 每个创建结果可点击进入现有订单任务详情或来源通知详情;可返回任务队列。
- 任务列表提供“导入邮件”入口。
- 任务详情展示识别证据和 warning;Basic Information 必须先确认,随后业务卡可编辑/确认。
- New Booking 未绑定既有订单时隐藏或明确标注“无需现有订单 ID”,不得用不可理解的强制输入阻塞用户。
- 具备键盘焦点、可见 label、错误关联、响应式布局及 loading/empty/error 状态。
10. 安全、隐私与审计
- 原始 EML 与附件内容仅在受控 SourceMessage 原文能力或临时解析内使用;普通任务 API 只返回安全摘要。
- 日志、错误、前端结果和 migration 不输出原始正文、真实邮箱、完整原始行、OSS 签名 URL、数据库密码或其他 Secret。
- 真实样本不得复制到
server/src/test/resources;测试 fixture 必须合成并去隐私。 - 用户对 Basic、Room、Trace 等卡的复核/确认继续写现有审计日志。
- 业务操作记录保留要求为 3 个月;V0.1 先保存
retention_until/可清理时间语义,清理调度不在本期强制范围。
11. PostgreSQL 项目专属 Schema
- Schema 固定为
th_hotel_booking。 - migration 必须显式
CREATE SCHEMA IF NOT EXISTS th_hotel_booking并限定search_path或使用全限定名。 - 不修改
public,不复用旧脚本中的bookingschema,不删除或改名其他项目对象。 - 远程执行顺序:只读读取
current_database/current_user→ 列出同名 schema/对象 → 检查 CREATE/USAGE 权限 → 创建本 schema → 只在本 schema 运行 migration → 对比其他 schema 对象计数/更新时间。 - 密码只通过进程环境或交互输入,绝不写入仓库、planning 文件、命令回显或交付文本。
- 当前 Spring 应用仍以 MySQL/H2 为运行基线;PostgreSQL migration 是项目专属目标模型,数据库方言切换不在本期暗中完成。
12. 测试与完成定义
12.1 自动化
- EML:Message-ID、Conversation-ID、current/history、无附件和多个附件。
- Excel:三种 Profile、整行业务列有非白底色、部分底色、白色、表头颜色、异常年份、陈旧严格行、多个日期段。
- Parser:动作别名、房型/价格/数量、New 房量阈值、Update、Cancel、Extra Bed Trace、未解决字段。
- 业务:一单多 Name/房型/日期段不误拆;三封样本中的生命周期/linked Trace;无确定事实时单封通知;重复投递幂等。Allotment、独立 Rooming List/Payment 与图片语义不属于本次固定渠道切片的完成证据。
- API:权限、文件类型/大小、201、重复结果、错误安全、任务创建。
- V4:新订单不要求既有 order ID;Basic 先确认;关键字段阻断;确认审计。
- V4 双区块:独立 Trace、Rooming List、Payment 各自创建 Basic + companion Room + 专属卡;同组已有 New/Update/Cancel 时不重复 Room;详情返回 current-only Room 模型。
- 前端:上传成功/失败/重复、结果链接、字段渲染、warning、复核与确认请求。
12.2 三封真实邮件验收
真实样本只从用户指定绝对路径读取:
- FIT LIANTAI:动作日/月候选可被选中;异常连续年份产生 warning,不被当成 2041 年业务日期。
- QBD REV.2:当前严格行与陈旧严格行被区分;同一 Tour Code 的多个日期段和房型保持一个订单任务。
- Update Booking ครั้งที่2:严格底色行生成 9 New/1 Update;Honeymoon 和 Extra Bed 分别生成同订单 Trace;未确认映射保持待复核。
12.3 完成定义
只有以下条件同时满足才可宣布 V0.1 完成:
- 普通员工可在可点击页面上传三封真实邮件。
- 后端真实创建 SourceMessage、订单任务/通知和任务卡,重复上传不重复建任务。
- 页面可查看证据、warning、关键参数,并能完成允许范围内的修正与确认。
- 没有执行 PMS/Opera。
- 后端和前端自动化通过;本地全栈 smoke 通过。
- PostgreSQL migration 通过本地语法/作用域验证;若远程测试库凭据可安全注入且只读检查无冲突,则项目 schema 已创建并证明未触碰其他 schema。
- 自主推断规则、应用代码位置、未解决业务映射和测试证据已记录。
13. 与 M011 的关系
M011 仍描述“给 SuperAgent 的通用 Excel 高亮证据增强”,其 V1 规则是任一业务单元格有底色即可抽取。M012 在固定渠道、本期业务入口中采用更严格且已获业务确认的“业务范围整行均有非白底色”规则,并直接形成标准事实候选。
两者不应静默混用:
- M011 通用 extraction 继续供 Debug/AgentBus 证据预览。
- M012 deterministic parser 使用独立的版本化 Channel Profile 和严格行选择器。
- 后续若把 M012 规则推广到 M011,需单独 Change Request 和回归验证。
14. 待配置而非待猜测
- 发件人/渠道 → Account Code / Account Name / Market Code / Source Code 映射。
- 公司 + 房型 + 价格 + 早餐 → 唯一 Rate Code 映射。
TWN SUITE的正式房型代码。- Allotment 来源扣减动作在未来 PMS 适配器中的命令结构。
- AgentBus 生产投递的认证/重试配置和 Booking Agent profile 版本。
这些项不阻止识别和建卡,但相关卡必须保持 REVIEW_REQUIRED 或明确 warning,不能用样本值代填。
15. 实施与验收结果
2026-08-08 已完成以下纵向闭环:
- 普通员工页面
/reservation/email-intake可选择或拖放.eml,调用真实POST /api/reservation/booking-email-intakes,显示解析结果、warning,并跳转到真实 V4 订单任务或来源通知。 - 三封外部真实 EML 的 API 验收结果依次为 20、1、10 张订单任务;业务构成为 19 Update + 1 Cancel、1 Update、9 New + 1 Update,并在第三封中生成 3 张 linked Trace;重复导入不重复建任务。
- 浏览器全栈 smoke 已从任务队列进入导入页,上传第三封真实邮件,打开首张任务,选择 Account 并成功确认 Basic Information;桌面和 390px 宽度无横向溢出,fresh session 无 console warning/error。
- 后端全量测试 441 项通过,0 failure、0 error、1 skipped;真实样本 opt-in 验收 1 项通过。
- 前端 29 个测试文件共 255 项通过,TypeScript typecheck 和生产 build 通过;lint 0 error,本轮文件无新增 warning。
- 测试库已仅创建
th_hotel_booking:9 张表、8 个 identity sequence、30 个索引/约束索引;迁移前后其他项目 schema 对象计数不变,无跨 schema 外键;回滚型 smoke 后无残留测试行。 - 本期没有调用 PMS/Opera、发送外部邮件或修改 AgentBus 外部状态。
V0.1 的已知运行边界:
- 普通员工手工导入路径在请求内解析附件,SourceMessage 和任务保存附件元数据与安全识别证据,但不长期保存可下载的原始附件字节;如需原件归档/下载,应接入既有 AgentBus/OSS 原文链路后另做 checkpoint。
- 固定渠道确定性 Parser 无法形成事实时,当前手工导入路径生成 S10/S99 来源通知供人工处理;不会在该请求内自动调用外部 Booking Agent。既有 AgentBus → SuperAgent 路径继续独立存在,生产启用和统一 fallback 编排需后续配置与联调。
- Account/Market/Source、Rate Code 和未唯一覆盖的房型映射仍按本章第 14 节由用户确认,不以真实样本值自动猜测。
- 确认后的 PMS/Opera 执行仍明确不在 V0.1。
16. 2026-08-08 BR00 双区块增量追踪
| 需求项 | 后端状态 | 前端状态 | 测试状态 | 文档位置 | 当前状态 |
|---|---|---|---|---|---|
| 独立 Trace 补 Basic + companion Room | Done | Done | Passed | 本文 4.5、8.2、12.1 | Implemented |
| 独立 Rooming List 补 Basic + companion Room | Done | Done | Passed | 本文 4.5、8.2、12.1 | Implemented |
| 独立 Payment 补 Basic + companion Room | Done | Done | Passed | 本文 4.5、8.2、12.1 | Implemented |
| 同组生命周期 Room 去重 | Done | N/A | Passed | 本文 4.5 | Implemented |
| General/Risk、权限、酒店隔离与敏感数据边界不变 | N/A | N/A | Passed(既有回归) | 本文 4.5、10 | Verified |