# M011 Booking Excel 高亮行预处理与 SuperAgent 调用前增强 V1 | 项目 | 内容 | | --- | --- | | 文档状态 | 当前有效;CP1 / CP2 / CP3 已实现,测试机 AgentBus 增强已开启,生产链路默认关闭;CP4 暂不推进 | | 适用范围 | AgentBus 入站邮件和 Debug EML 上传邮件中的 Excel 附件,在调用 SuperAgent Open API 前做结构化预处理 | | 当前目标 | 排除旅行名单类 Excel,抽取 Booking / 附加费类 Excel 中带背景色的业务行,生成安全 JSON 并附加到发给 SuperAgent 的 payload | | 依赖能力 | SourceMessage Inbox、Debug EML、AgentBus 自动分发、Apache POI、OSS 附件读取能力、SuperAgent Open API | ## 1. 背景 当前 AgentBus 和 Debug EML 链路都会把邮件正文、HTML、附件摘要组装成 AgentBus Outlook-like payload 后交给 SuperAgent。实际业务邮件中存在多种 Excel 附件: - 第一种是旅行团人员名单,例如包含 `护照全名`、证件号、生日、性别等字段。这类文件主要服务 Rooming List 或旅客名单整理,不应进入 Booking 更新 / 附加费抽取逻辑。 - 第二种是春节、节假日或其他附加费用表,业务上关心其中带背景色标记的行。 - 第三种是 Wyndham / Booking Update 类多 sheet 表格,业务上同样关心最近几个月 sheet 中带背景色标记的行。 如果把整份 Excel 只作为附件 URL 交给 SuperAgent,模型需要自行下载、打开、理解多 sheet 和样式,稳定性和耗时都不可控。因此本需求建议在后端调用 SuperAgent 前增加一个轻量预处理层:只提取与业务判断相关的安全结构化 JSON,把它作为邮件 payload 的补充证据。 ## 2. 目标 - 在 AgentBus 自动分发和 Debug EML 上传两条链路中复用同一套 Excel 附件预处理逻辑;当前两条链路均已接入,测试机 AgentBus 增强已开启,生产链路仍由配置默认关闭。 - 识别并排除旅行名单类 Excel,避免把人员名单误当成 Booking 更新或附加费数据。 - 对 Booking Update / Booking Surcharge 类 Excel,按最近月份筛选 sheet,只抽取有业务背景色的行。 - 把抽取结果追加到发给 SuperAgent 的 AgentBus Outlook-like payload 中,字段建议为 `attachment_extractions[]`。 - Debug EML 页面可以展示后端返回的安全抽取预览,用于验证解析是否符合预期。 - 不改变现有 SourceMessage Inbox 的来源事实定位,不直接创建订单、任务、OPERA / OHIP 操作或客户回复。 ## 3. 非目标 - 不把 Excel 高亮行直接落成订单、任务或业务状态。 - 不绕过 SuperAgent 后续 `task-results` / MCP 入站和本系统校验、人工确认流程。 - 不让前端直接调用 SuperAgent、AgentBus、OSS 或 Excel 解析服务。 - 不依赖文件名作为唯一判断依据;文件名只作为辅助信号。 - 不在普通日志、错误响应、埋点或普通业务接口中输出完整 Excel 内容、完整附件 URL、OSS 签名参数、客户敏感信息、API Key、Cookie 或 Secret。 - V1 只处理单元格背景色,不把字体颜色、批注、筛选状态或条件格式结果作为稳定业务输入;如后续需要,另开 checkpoint。 ## 4. 文件类型识别规则 后端应对每个 `.xls` / `.xlsx` 附件做 sheet 级和 workbook 级识别。推荐输出稳定类型: | 类型 | 中文说明 | V1 动作 | | --- | --- | --- | | `PASSENGER_ROSTER` | 旅行团人员名单 / Rooming List 来源名单 | 排除,不向 SuperAgent 提供行级抽取数据,只记录安全 warning 或 `excluded=true` | | `BOOKING_SURCHARGE` | 春节、节假日或其他 Booking 附加费用表 | 按月份和背景色抽取行 | | `BOOKING_UPDATE` | Booking Update / Wyndham 更新类表格 | 按月份和背景色抽取行 | | `UNKNOWN` | 未识别 Excel | 不抽取,记录安全 warning | ### 4.1 第一种表格排除规则 推荐规则: 1. 扫描每个 sheet 前 20 行,做表头归一化,忽略大小写、前后空格、换行和中英文括号差异。 2. 如果命中以下人员名单字段中的 4 个或以上,可判定该 sheet 为 `PASSENGER_ROSTER`: ```text 旅游批次 旅游日期 成团航班信息 团号 团长 姓名 护照全名 证件号 护照号 证件有效期结束 性别 生日 年龄 饮食禁忌 重大疾病 ``` 3. 如果同一个 sheet 同时存在明确 Booking 核心字段组合,应优先标记为待确认而不是直接排除。Booking 核心字段包括: ```text 酒店 / Hotel / โรงแรม 酒店回应状况 / Hotel Status / สถานะ 备注 / Remark / หมายเหตุ 入住 / Check In / วันเช็คอิน 退房 / Check Out / วันเช็คเอาท์ 房型 / Room Type 房数 / Rooms 团号 / Group Code ``` 4. 如果 workbook 中全部有效 sheet 都是 `PASSENGER_ROSTER`,则整份附件排除。 5. 如果 workbook 中部分 sheet 是人员名单、部分 sheet 是 Booking 表,应只排除人员名单 sheet,继续处理其他 sheet。 中文说明:第一种表格的排除重点是“字段组合”,不是文件名。后续即使文件名变化,只要字段结构是旅行名单,仍应排除。 ### 4.2 第二、第三种表格识别规则 `BOOKING_SURCHARGE` 推荐同时参考: - 表头或正文出现 `附加费`、`春节`、`新年`、`Surcharge`、`Gala Dinner`、`Compulsory` 等关键词。 - 存在酒店、入住 / 退房、房型、房数、费用、备注等 Booking 或费用相关字段。 - 文件名可作为辅助信号,但不能单独决定类型。 `BOOKING_UPDATE` 推荐同时参考: - sheet 名或文件名出现 `BOOKING`、`UPDATE BOOKING`、`WYNDHAM`、月份标识等关键词。 - 存在团号、酒店、入住 / 退房、房型、房数、酒店回应状态、备注等 Booking 更新相关字段。 - 多 sheet 且 sheet 名形如 `BOOKING 01-2026`、`BOOKING 02-2026` 时,优先按 sheet 月份筛选。 识别置信度不足时应输出 `UNKNOWN`,不做激进抽取。 ## 5. 最近月份筛选规则 第二、第三种表格通常包含多个月份 sheet,但当前真实样例常见滞后到 4 月、5 月。V1 默认不应死取“当前月 + 前 2 个月”,而应使用“最近 6 个月候选窗口 + 文件内最新 3 个月”的两段式规则。 ### 5.1 显式月份窗口 Debug 或后续管理配置可以显式指定月份窗口: ```text from_month=YYYY-MM to_month=YYYY-MM ``` 规则: - 优先从 sheet 名解析月份,例如 `BOOKING 01-2026` 解析为 `2026-01`。 - sheet 名存在多个日期时,优先识别 `MM-YYYY`、`YYYY-MM`、`MMM YYYY` 这类月份粒度。 - 如果 sheet 名无法解析月份,可扫描业务日期列,例如入住、退房、Booking Date,并以行级日期判断是否落入窗口。 - 返回结果中必须记录 `matched_sheets[]` 和 `skipped_sheets[]`,便于 Debug 页面核对“为什么某个 sheet 没被抽”。 ### 5.2 默认月份选择 没有显式传入 `from_month` / `to_month` 时,默认按以下规则: 1. 取基准月份 `base_month`。 2. AgentBus 链路的 `base_month` 优先来自 SourceMessage 的 `payload_received_at` 或 AgentBus 邮件 `received_at`,并按酒店本地时区转换为业务月份。 3. Debug EML 链路的 `base_month` 按本系统 Debug 上传运行创建时间计算,不再尝试按 EML 原始收件时间计算。 4. 如果来源接收时间缺失,再使用系统当前时间。 5. 以 `base_month` 为终点,向前取 `lookback_months=6` 个自然月作为候选窗口,包含基准月。 6. 从文件实际存在且落在候选窗口内的月份中,按月份倒序选择最新 `max_selected_months=3` 个。 7. 如果候选窗口内实际存在月份少于 3 个,有几个处理几个,并返回 `MATCHED_MONTHS_LESS_THAN_LIMIT` warning。 8. 如果候选窗口内没有任何月份,返回 `NO_MATCHED_MONTH_SHEET` warning,不回退到 6 个月之前的旧 sheet。 示例: ```text 邮件接收时间:2026-07-19 base_month:2026-07 lookback_months:6 候选窗口:2026-02 ~ 2026-07 文件实际月份:2026-03、2026-04 最终处理月份:2026-03、2026-04 ``` 如果文件实际月份为: ```text 2026-03、2026-04、2026-05、2026-06 ``` 最终处理: ```text 2026-04、2026-05、2026-06 ``` 中文说明:这里的“最近几个月”不要由 SuperAgent 猜,应由后端配置或调试页面参数确定。生产链路建议先用默认两段式规则,Debug 链路后续可允许调试人员手动覆盖。 ## 6. 高亮行抽取规则 V1 只抽取单元格背景色,不抽取字体颜色。 推荐 Apache POI 判断口径: - 只把 `FillPatternType.SOLID_FOREGROUND` 且前景色不是默认、自动、白色或主题默认色的单元格视为背景色标记。 - 忽略空白列、样式污染列和 Excel `max_col` 虚高带来的尾部空列。 - 先识别表头行和有效业务列,再只扫描业务列范围。 - 表头自身背景色不代表业务高亮,应从数据行开始判断。 - 一行只要任一业务列有有效背景色,就作为高亮业务行返回。 - 返回整行业务字段,同时返回 `highlight_cells[]`,说明哪些列触发了高亮。 建议保留颜色原始值,统一为 ARGB / RGB 字符串: ```json { "column": "G", "header": "Remark", "value": "Need confirm surcharge", "fill_color": "FFFFFF00" } ``` ## 7. 输出 JSON 契约 后端发给 SuperAgent 的 AgentBus Outlook-like payload 建议增加: ```json { "attachment_extractions": [ { "attachment_name": "WYNDHAM LIANTAI 2026 UPDATE BOOKING 12-05-2026 NO.1-3.xlsx", "attachment_sha256": "sha256-hex", "file_type": "BOOKING_UPDATE", "parser_version": "booking-highlight-excel-v1", "excluded": false, "skipped_reason": null, "month_filter": { "mode": "DEFAULT_LOOKBACK_LATEST_AVAILABLE", "base_month": "2026-07", "lookback_months": 6, "max_selected_months": 3, "candidate_from_month": "2026-02", "candidate_to_month": "2026-07", "available_months": ["2026-03", "2026-04", "2026-05", "2026-06"], "selected_months": ["2026-04", "2026-05", "2026-06"], "matched_sheets": ["BOOKING 04-2026", "BOOKING 05-2026", "BOOKING 06-2026"], "skipped_sheets": ["BOOKING 01-2026", "BOOKING 02-2026", "BOOKING 03-2026"] }, "sheets": [ { "sheet_name": "BOOKING 05-2026", "header_row": 3, "highlighted_row_count": 2, "highlighted_rows": [ { "row_number": 84, "row_key": "optional-stable-row-key", "highlight_colors": ["FFFFFF00"], "highlight_cells": [ { "column": "G", "header": "Remark", "value": "Need confirm surcharge", "fill_color": "FFFFFF00" } ], "row": { "group_code": "optional", "hotel": "optional", "check_in": "optional", "check_out": "optional", "room_type": "optional", "rooms": "optional", "hotel_status": "optional", "remark": "optional" }, "raw_row": { "A": "raw value", "B": "raw value" }, "warnings": [] } ] } ], "warnings": [] } ] } ``` 字段说明: | 字段 | 中文说明 | | --- | --- | | `attachment_name` | 附件原始文件名,只用于识别,不作为唯一业务判断依据 | | `attachment_sha256` | 附件内容 hash,用于排查和幂等;不得替代 SourceMessage ID | | `file_type` | 后端识别出的 Excel 类型 | | `parser_version` | 解析器版本,便于后续规则升级 | | `excluded` | 是否被排除 | | `skipped_reason` | 排除或跳过原因,例如 `PASSENGER_ROSTER`、`NO_MATCHED_MONTH_SHEET`、`UNKNOWN_EXCEL_TYPE` | | `month_filter` | 本次月份筛选模式、候选窗口、文件内可用月份、最终选中月份和 sheet 命中结果 | | `highlighted_rows[]` | 高亮业务行 | | `row` | 后端归一化后的常用业务字段;缺失时可以为空 | | `raw_row` | 原始列值映射,仅限当前高亮行;不得包含整份表格 | | `warnings[]` | 可展示安全警告,不包含 Secret、签名 URL 或完整敏感正文 | 中文说明:`raw_row` 是为了给 SuperAgent 和 Debug 页面提供核对依据,但范围必须限制在被高亮的数据行,不能把整张 sheet 原样塞进 payload。 ## 8. 接入链路位置 ### 8.1 AgentBus 自动分发 ```text AgentBus WebSocket → SourceMessage Inbox RECEIVED → 创建 platform_superagent_dispatch_run → worker 读取 SourceMessage payload 和附件引用 → Excel 附件预处理 → 把 attachment_extractions[] 追加到 AgentBus Outlook-like payload → 调用 SuperAgent Open API → 等待 SuperAgent 后续 task-results / MCP 入站 ``` AgentBus 回调线程不应同步解析大 Excel,也不应同步等待 SuperAgent。解析动作放在 dispatch worker 中,和外部调用一起由 dispatch run 追踪。 AgentBus payload 中的附件地址已是本系统可访问的 OSS 地址。M011 实现时可由后端通过受控 OSS / 存储适配器读取具体 Excel 附件内容,不需要前端参与,也不应把完整 OSS URL 或签名参数写入普通日志。 ### 8.2 Debug EML 上传 ```text Debug EML 上传 → 解析邮件并上传原始邮件 / 内联图片 / 附件 → 写入 SourceMessage Inbox → Excel 附件预处理 → 把 attachment_extractions[] 追加到 agentbus_like_payload → 调用 SuperAgent Open API → Debug 页面展示本系统阶段、SuperAgent trace、最终回答和安全抽取预览 ``` Debug EML 可以把 `attachment_extractions[]` 作为只读调试信息返回给页面。前端不得编辑后再提交,也不得把其中的 OSS URL、客户敏感信息写入日志或埋点。Debug 链路的 Excel 内容来自本次上传 `.eml` 解析出的附件字节或上传到本系统 OSS 后的对象,不依赖外部邮箱附件临时地址。 ## 9. 后端模块边界建议 建议把解析能力放在 Reservation 业务流程内,因为当前识别规则和字段归一化明显属于预订业务语义: ```text server/src/main/java/cn/nianxx/thhotel/workflows/reservation/excelimport ├── service │ └── impl └── common ├── dto ├── request ├── result └── enums ``` 建议核心服务命名: | 服务 / 类型 | 中文职责 | | --- | --- | | `ReservationBookingExcelAttachmentExtractionService` | 对邮件 Excel 附件做类型识别、sheet 筛选和高亮行抽取 | | `BookingExcelAttachmentExtractionRequest` | 输入附件文件名、内容流、hash、显式月份窗口或默认月份选择参数、来源上下文 | | `BookingExcelAttachmentExtractionResult` | 输出 `attachment_extractions[]` 中单个附件的结构 | | `BookingExcelFileType` | `PASSENGER_ROSTER`、`BOOKING_SURCHARGE`、`BOOKING_UPDATE`、`UNKNOWN` | 依赖方向: - `workflows.reservation` 可以依赖 Apache POI 做业务解析。 - Debug EML 和 AgentBus dispatch 在组装 SuperAgent payload 时调用稳定 Service / Port,不能复制两套解析逻辑。 - `platform.message` 仍只负责 SourceMessage 来源事实,不反向依赖 Reservation Excel 业务字段。 - `integrations.ai.superagent` 只负责调用 SuperAgent;如果需要拼装业务增强 payload,应通过内部编排服务或明确的 payload enricher 调用,不把 POI 解析细节放进 Open API client。 ## 10. 配置建议 | 配置 | 默认值 | 中文说明 | | --- | --- | --- | | `reservation.booking-excel-extraction.enabled` | `false` | 是否启用 Booking Excel 附件预处理总开关 | | `reservation.booking-excel-extraction.lookback-months` | `6` | 未显式指定月份窗口时,先从基准月向前取最近几个自然月作为候选窗口,包含基准月 | | `reservation.booking-excel-extraction.max-selected-months` | `3` | 在候选窗口内,从文件实际存在月份里最多选择最新几个业务月 | | `reservation.booking-excel-extraction.max-file-size` | `10MB` | 单个 Excel 附件最大解析大小 | | `reservation.booking-excel-extraction.max-sheets` | `24` | 单个 workbook 最大扫描 sheet 数 | | `reservation.booking-excel-extraction.max-rows-per-sheet` | `2000` | 单个 sheet 最大扫描行数 | | `agentbus.superagent-dispatch.include-booking-excel-extractions` | `false` | AgentBus 自动分发是否把抽取结果追加给 SuperAgent | | `agentbus.superagent-dispatch.booking-excel-download-max-size` | `10MB` | AgentBus 自动分发读取单个 Excel 附件的最大大小,避免 worker 下载异常大文件 | | `debug.eml-upload.include-booking-excel-extractions` | `false` | Debug EML 是否返回并传递抽取结果 | 中文说明:Debug EML 和测试机 AgentBus 自动分发增强已可用于验证样例 Excel 与 SuperAgent 结果稳定性;生产仍保持默认关闭,生产开启需要单独确认。 ## 11. 错误处理和安全边界 - Excel 解析失败不得导致 SourceMessage Inbox 入库失败。 - V1 解析失败或附件读取失败时记录安全 warning / 跳过结果,并继续调用 SuperAgent;除非后续配置显式要求严格失败。 - AgentBus 附件地址按本系统可访问 OSS 地址处理;后端通过受控对象存储端口读取 Excel 内容,不依赖前端传来的临时 URL。 - 安全错误摘要只记录附件名、hash 前缀、sheet 名、行号、错误类型,不记录完整单元格敏感内容、完整附件 URL 或签名参数。 - Debug 页面展示的抽取 JSON 只面向受控 dev/test 调试入口,不进入普通业务页面。 - SuperAgent 看到的 `attachment_extractions[]` 只是证据输入;最终业务写入仍必须经过本系统入站契约校验、幂等、权限边界和人工确认。 ## 12. 测试范围 后续实现时至少补充以下测试: - 人员名单类 Excel 命中 `PASSENGER_ROSTER` 并被排除。 - Booking Surcharge 类 Excel 被识别为 `BOOKING_SURCHARGE`。 - Booking Update 多 sheet Excel 被识别为 `BOOKING_UPDATE`。 - sheet 名月份 `BOOKING 01-2026` 能解析为 `2026-01`。 - 默认模式以来源接收时间的酒店本地月份为 `base_month`,生成最近 6 个月候选窗口。 - 候选窗口内文件实际存在月份超过 3 个时,只选择最新 3 个。 - 候选窗口内文件实际存在月份不足 3 个时,有几个处理几个,并返回 `MATCHED_MONTHS_LESS_THAN_LIMIT` warning。 - 候选窗口内没有实际存在月份时,不回退到更早 sheet,并返回 `NO_MATCHED_MONTH_SHEET` warning。 - 未选中 sheet 写入 `skipped_sheets[]`。 - sheet 名缺少月份时,能按入住 / 退房等业务日期列做行级兜底。 - 背景色判断忽略无填充、默认、自动、白色、表头背景色和样式污染空列。 - 有任一业务列背景色的数据行会输出到 `highlighted_rows[]`。 - 输出 JSON 包含 `highlight_cells[]`、`highlight_colors[]`、`row_number` 和归一化 `row`。 - 解析失败不影响 SourceMessage 入库;是否继续调用 SuperAgent 按配置执行。 - Debug EML 和 AgentBus dispatch 两条链路复用同一解析服务。 - 日志、错误响应和调试响应不泄漏 API Key、Cookie、Secret 或完整 OSS 签名 URL。 ## 13. 分阶段落地建议 ### CP1:解析器和样例测试(已实现) - 实现 Excel 类型识别、月份筛选和高亮行抽取。 - 使用当前三类样例文件补单元测试或 fixture 测试。 - 不接入 SuperAgent,不改 Debug 页面。 ### CP2:Debug EML 预览和 payload 增强(已实现) - Debug EML 上传后执行预处理。 - Debug 响应和 SSE 事件返回安全 `attachment_extractions[]` 预览。 - 调用 SuperAgent 时在 `agentbus_like_payload` 中包含该字段。 - 前端仍只调用本项目后端。 ### CP3:AgentBus 自动分发增强(已实现) - AgentBus dispatch worker 在调用 SuperAgent 前读取 SourceMessage 已保存附件引用,并通过后端对象存储端口下载 Excel 内容。 - 复用 `ReservationBookingExcelAttachmentExtractionService` 执行同一预处理,非空结果追加到发给 SuperAgent 的 AgentBus Outlook-like payload。 - 测试机 AgentBus 增强已开启;生产链路默认配置关闭,生产开启需单独确认。 - 附件读取或解析失败时追加安全跳过结果 / warning 并继续调用 SuperAgent;日志和 payload 不写完整附件 URL、签名参数、API Key、Cookie 或 Secret。 ### CP4:可选持久化和运营查询(暂不推进) 当前 CP4 不进入近期开发计划。如果后续需要追踪 Excel 解析历史,再单独设计持久化表,例如: ```text workflow_reservation_booking_excel_import_batch workflow_reservation_booking_excel_import_row ``` V1 不要求落库,避免在规则未稳定前引入长期数据模型。