13 KiB
Booking 邮件 contracts-v1
| 项目 | 内容 |
|---|---|
| 文档状态 | 权威契约 |
| 契约版本 | booking-contracts-v1 |
| 对应架构 | Booking 邮件处理架构 v0.4 |
| 适用终点 | 用户确认;PMS/Opera 不在本契约执行范围 |
1. 共同信封与版本规则
七层之间传递的每个顶层对象必须包含 contract_meta 和 evidence_refs[]。禁止靠调用方上下文、数据库隐式默认值或 Agent prompt 隐式补齐版本。
| 字段 | 必填 | 说明 |
|---|---|---|
contract_version |
是 | 固定 booking-contracts-v1。不兼容变更必须新版本。 |
catalog_version |
是 | 本次使用的业务目录版本;尚未执行目录处理时填 catalog-not-applied。 |
parser_version |
是 | 本次 Parser 版本;未执行时填 parser-not-run。 |
agent_profile_version |
是 | Booking Agent profile 版本;未调用时填 agent-not-invoked。 |
processing_run_id |
是 | 一次处理运行的稳定 ID,重试使用同一 run、不同 attempt。 |
source_message_id |
是 | 平台 SourceMessage 的稳定 ID;不可使用邮件主题代替。 |
source_revision |
是 | 同一来源消息在材料重取或重放后的可追溯版本。 |
evidence_refs |
是 | 最小化证据引用;不嵌入原始邮件、完整附件、Secret 或外链 URL。 |
evidence_ref 至少包含 evidence_id、kind、source_message_id、locator、content_hash、is_current_material。kind 允许 CURRENT_BODY、QUOTED_HISTORY、ATTACHMENT、WORKBOOK_SHEET、WORKBOOK_ROW、IMAGE、OCR_TEXT、CONTEXT_RECORD、USER_CORRECTION。locator 只能表达安全定位(例如 Sheet/行号、附件逻辑 ID、上下文记录 ID),不能含签名 URL 或原文全文。
所有 issue 使用同一结构:code、severity(INFO/WARNING/BLOCKER)、scope(MESSAGE/MATERIAL/TARGET/FIELD)、message_key、evidence_refs[]、可选 retryable。用户可见文本由 message_key 和前端文案生成,避免把原文写进 API。
2. SourceMessageEnvelope
层 1 输出。 统一 AgentBus 与手工 EML,不携带业务判断。
| 字段 | 说明 |
|---|---|
message_origin |
AGENTBUS 或 MANUAL_EML。 |
external_message_id、conversation_id、in_reply_to |
邮件身份与会话链路;缺失时保留 null,不用主题猜。 |
idempotency_key |
基于可信消息身份/内容摘要形成;只处理外部重复投递,不合并两封真实邮件。 |
sender、recipients、subject |
最小化安全摘要或受控引用;不作为唯一业务事实。 |
received_at |
收件时间点(UTC)。 |
current_body_ref、quoted_history_ref |
对正文 current/history 分离后的证据引用。 |
attachment_descriptors[] |
逻辑附件 ID、文件名摘要、媒体类型、大小、内容哈希、受控原件引用。 |
不可接受:入口直接调用 Parser/Agent 后跳过 SourceMessage、入口把 AgentBus 的原始 JSON 当领域对象、或根据邮件主题创建订单。
3. MaterialPackage
层 2 输出。 描述可供解析/理解的材料,不表达最终业务类型。
| 字段 | 说明 |
|---|---|
message_envelope |
SourceMessageEnvelope 的最小必要摘要与 ID。 |
current_materials[] |
本次可触发业务的 current body、当前附件、当前图片、明确版本指令。 |
quoted_history_materials[] |
仅用于解释、继承身份、重复检查的历史材料。 |
workbook_inspections[] |
文件哈希、Sheet 名称、表头签名、候选行/列、底色证据、Profile 候选与检测结果。 |
image_inspections[] |
图片证据与可选 OCR 安全摘要;不把 OCR 当唯一事实。 |
material_selection_status |
READY、NO_ATTACHMENT、UNSUPPORTED、AMBIGUOUS、FAILED。 |
issues[] |
材料层问题,例如超限、文件损坏、多个 Profile 平分、非法 Sheet。 |
规则:current 与 quoted 必须可区分;没有附件时输出 NO_ATTACHMENT 但仍为有效 package;固定渠道只把严格候选行送入确定性 Parser,其他行只能作为复核证据。
4. ParsedFactSet
层 3 输出。 Parser 的标准事实集合;它不等于最终业务决定。
| 字段 | 说明 |
|---|---|
parser_applicability |
APPLICABLE、NOT_APPLICABLE、FAILED、AMBIGUOUS。 |
channel_profile_code |
成功解析时的固定渠道 Profile;不能唯一确定时为 null。 |
facts[] |
可追溯的标准事实,必须逐项带 fact_id、confidence_kind=DETERMINISTIC、evidence_refs[]。 |
unresolved_fields[] |
未能确定的字段及原因;未知房量、Rate Code 映射等必须保留。 |
fallback_reasons[] |
需要 Booking Agent 或 Risk 的明确原因代码。 |
issues[] |
Profile、底色、字段格式、解析失败等问题。 |
agent_input_material |
可选、受控的 Agent current-message 文本;只在本地 policy 脱敏、限长后出现。 |
facts[] 的稳定字段包括:
action_hint:NEW_BOOKING、UPDATE_BOOKING、CANCEL_BOOKING或 null;booking_type只在房量足以确定时为FIT/GROUP,否则为 null。target_identity:group_code是团号统一字段;Name of Group、Group Name、Tour Code、Group Code进入该字段。group_name只用于邮件明确给出的独立展示名称;tour_code是历史兼容镜像,不能与group_code形成两个目标。name_values只保存真正的客人/联系人姓名;字段未知必须为 null。stay:arrival_date、departure_date、nights、room_items[]、价格原始证据。room_items[]:room_type_code_hint、room_count、rate_hint、evidence_refs[];Extra Bed 不能写入房型。related_hints[]:Trace、Rooming List、Payment、Allotment source/actual、明确GROUP_CODE_REPLACEMENTold→new 关系等候选线索。
agent_input_material 是为了让 Parser fallback 能理解无固定渠道的正文,而不是第二份原始邮件:它只能绑定一个 CURRENT_BODY evidence,内容仅来自 current subject/body,按 booking-agent-current-message-v1 脱敏并限制为最多 12,000 个 code point。它必须排除 quoted history、原始附件、附件 URL、sender 原文和 Secret;该受控摘要可随 PARSED_FACT_SET 在项目 schema 保留至 retention_until,以支持同一 run 的异步 Agent/retry,不得用它还原原始邮件。
GROUP_CODE_REPLACEMENT 只接受 current body 中明确标注的 old→new 指令。单封普通文本中出现两个团号、主题猜测或 quoted history 都不能生成关系。该 relation 在 Parser fallback 中是无动作 Context/Agent clue;固定渠道仅能绑定到同一 new group code 的 UPDATE_BOOKING fact。
Parser 不得:将 room_count=0 解释为 FIT、在 Profile 平分/未知/非法 Sheet 时产生确定事实、用多行互补制造 anchor 事实、或因字段缺失把已知动作改写成 General/Risk。
5. ContextPackage
层 4 输出。 面向一封当前邮件与已识别目标的最小上下文,而不是历史全量 dump。
| 字段 | 说明 |
|---|---|
context_scope |
MESSAGE 或一个/多个 TARGET;每个 target 有独立查询边界。 |
target_resolutions[] |
输入线索、零/一/多候选、解析状态和证据;多候选不自动选择。 |
current_state |
已确认事实、有效未完成任务、计划完成态;带数据来源与更新时间。 |
conversation_context |
当前邮件的必要历史摘要、revision 关系与重复线索;历史内容不重新触发动作。 |
lifecycle_context |
New/Update/Cancel 前置链、old→new Group Code、Cancel 后冲突、Trace 顺序。 |
allotment_context |
source 团与 actual 团候选关系、库存/扣减可用性;不是最终扣减命令。 |
issues[] |
上下文缺失、多目标冲突、历史证据冲突等。 |
读取规则:优先 Group Code、明确订单 ID、已确认消息关联;无可靠目标时返回未识别,而不是全文/全库模糊扫描。
当 GROUP_CODE_REPLACEMENT 存在时,Context 必须只读取 old/new 两个明确 Group Code:old 有效已确认链且 new 不存在才是 RESOLVED;old 已取消/不存在为 UNRESOLVED,new 已存在为 CONFLICT。group_code_replacements[] 必须保留 relation ID、old/new、状态、候选 ID 和 issue。Trace 历史按 Group Code 和时间顺序只提供安全摘要(任务 ID、状态、FO/HSK、服务类型);跨封 Trace 永远是新卡,历史自由文本、附件定位和 quoted 内容不能进入 Context。
6. CandidateDecision
层 5 输出。 可以来自纯确定性合成或 Booking Agent;两者使用同一结构。
| 字段 | 说明 |
|---|---|
decision_origin |
DETERMINISTIC、AGENT、MIXED。 |
result_disposition |
IN_SCOPE_TASKS、GENERAL_NOTIFICATION、RISK_NOTIFICATION、IGNORED_DIRECTION。 |
target_decisions[] |
每个目标独立的候选动作、目标身份、字段、linked/derived actions、证据和 issues。 |
message_notifications[] |
General/Risk/ignored 的消息级结果;Risk 每封最多一条。 |
agent_trace_ref |
Agent 调用 ID/版本/安全摘要;纯 Parser 时为空。 |
issues[] |
对整封消息的无法分配问题。 |
target_decision 的 business_type 为:NEW_BOOKING、UPDATE_BOOKING、CANCEL_BOOKING、TRACE_RESERVATION_NOTES、ROOMING_LIST、PAYMENT、ALLOTMENT;action_kind 由业务类型和当前态决定。Allotment 必须显式输出 actual 与 source 的关联,不能把多个 actual 或 source 扣减隐匿在一张普通 New 卡里。
Booking Agent 的受控输入只允许 ParsedFactSet + ContextPackage;ParsedFactSet.agent_input_material 是唯一可见的正文材料,且受上文 policy 约束。若 Parser NOT_APPLICABLE 或 FAILED,也必须先创建最小的 ParsedFactSet,明确未解析原因,而不是把原始附件或 quoted history 直接交给 Agent。
7. ValidationResult
层 6 的校验输出。 只有 validation_status=VALID 的目标才可产生确认投影。
| 字段 | 说明 |
|---|---|
validation_status |
VALID、REVIEW_REQUIRED、RISK、REJECTED。 |
validated_targets[] |
目标级通过/阻断结果,不让一个目标阻断同封其他清晰目标。 |
schema_checks[] |
contracts-v1 结构和版本检查。 |
business_checks[] |
Catalog、必填、目标、生命周期、前置任务、linked action、重复/revision 检查。 |
blockers[] |
必须由用户补齐或修复才可确认的条目。 |
risk_notification |
仅隔离无法安全决定的部分;每封至多一张。 |
Validator 必须校验:版本一致性、证据存在、目标解析、字段必填、Catalog 映射、当前生命周期、已确认/未完成任务、外部重复投递、真实新邮件、revision、历史底色遗留和业务内容相同。Agent 输出无效时,目标级进入 Risk,不能绕过 Validator 建卡。
8. ConfirmationProjection
层 6 面向前端的安全输出。 它不是原始 CandidateDecision 的直通 JSON。
| 字段 | 说明 |
|---|---|
projection_status |
AWAITING_CONFIRMATION、REVIEW_REQUIRED 或不可生成。 |
confirmation_items[] |
用户可见任务/通知,按目标拆分。 |
display_fields[] |
已归一化的关键字段、展示值、来源证据、可编辑性与校验规则。 |
blocked_fields[] |
缺失/冲突字段及需用户选择的候选;不暴露内部 payload。 |
linked_actions[] |
如 Allotment source/actual、Trace 合并关系;只显示本期可确认参数。 |
safe_evidence[] |
文件/Sheet/行/图片等安全引用与脱敏摘要。 |
processing_run |
run ID、当前状态、可重试状态与安全错误摘要。 |
确认 API 只冻结用户确认的参数和审计,必须携带 processing_run_id 与并发版本。确认成功不调用 PMS/Opera,也不执行付款、库存扣减或部门流转。
9. 编排、持久化与 API 约束
- AgentBus 与手工 EML 都调用同一个
BookingMessageOrchestrator,并产生一致的SourceMessageEnvelope幂等语义。 - 同步完成且无需异步 Agent 时返回
201;进入 Agent 或异步处理时返回202与processing_run_id。 - 状态查询为
GET /api/reservation/booking-processing-runs/{runId};已有POST /api/reservation/booking-email-intakes逐步适配为统一入口。 - PostgreSQL
th_hotel_booking保存 run、attempt、版本、证据引用、候选、校验和确认投影;PARSED_FACT_SET中只允许保存受 policy 约束的 Agent 摘要,所有这些项目 schema 数据按retention_until三个月清理。旧 MySQL 只能被 Context 兼容读取,不能参与新主线写入或跨库事务。 - 所有持久化/接口代码必须使用
contract_version做版本门禁;未知未来版本 fail closed 并形成安全 Risk/技术错误记录。
10. 兼容与测试要求
- Parser 与 Booking Agent 对相同事实必须输出可比较的
CandidateDecision;差异只能通过显式decision_origin和 issue 表达。 - 每个 Contract 都需 JSON/Java fixture、schema validation、正向/错误案例及 evidence reference 检查。
- 真实邮件只作为 opt-in 外部验收;仓库只保存去隐私 fixture 与样本哈希/Profile 断言。
- 后续对字段、枚举、状态机或业务动作做破坏性变更时,必须新增 contracts-v2,而不是静默改 v1。