Files
th-hotel-simple/docs/project/requirements/booking-email-contracts-v2.md
T
鲨鱼辣椒 694c4317a3 checkpoint: complete recoverable V2 pre-separation baseline
Complete the selective V2 checkpoint with its minimal AgentBus, object-storage, replay persistence, and validated-workbench shared dependency closure.
2026-08-20 17:09:00 +08:00

11 KiB
Raw Blame History

Booking 邮件 contracts-v2

项目 内容
文档状态 权威契约;离线实现已验收;真实运行与发布 HOLD
契约版本 booking-contracts-v2
业务规则 Booking Business Agent v1.0
对应架构 Booking 邮件处理架构 v0.5
决策依据 ADR-012(修订 ADR-011)
适用终点 Layer 5 Candidate、Layer 6 Validation、Layer 7 用户确认;不执行 PMS/Opera

1. 单一权威与共同规则

新 Layer 5 运行只使用 CandidateDecisionV2。CandidateDecision v1 从未上线,不提供双写、回退、迁移或兼容 分支。冻结 Layer 3/CP4 若仍编译依赖旧 Java 类型,只能视为隔离的迁移债务,不能产生新 Layer 5 结果。

所有对象使用 snake_case JSON、稳定 ID/枚举、显式 issue code 和 evidence reference。未知字段一律拒绝。来源缺失 不得由 Agent、Prompt、历史原文或数据库暗中补造。

2. Layer3ResultV2

Layer 3 只表达当前来源事实和材料关系:

  • source_facts[] 保存物理来源单元、来源动作原文/规范值、目标、住期、房型/房量等中立事实;
  • structured_mentions[] 保存当前明确或中立的服务事项及参数;
  • target_bindings[]/neutral_relationships[] 保存材料归属、source→actual 等关系;
  • material_observations[]/display_information[]/material_dispositions[]/parse_issues[] 保存材料状态和证据。

SOURCE_ACTION_KIND 是来源事实,不是 business_type/action_kind。Layer 3 不输出 Candidate、任务卡、部门、 General、Risk 或 Allotment 派生动作。

3. 内部输入 BookingDecisionInputV2

内部 canonical input schema 为 booking-decision-input-v2.schema.json。它供 Layer 4、Layer 6、 存储和审计使用,包含:

  • 当前邮件、完整有序的邮件线程 History 正文与附件安全描述;
  • Layer3ResultV2;
  • decision_context.order_contexts[]:物理来源单元、canonical Room Type、结构性覆盖 FIT/GROUP 的 rate_options[];每个 option 包含原子 {Rate Code+早餐+餐厅} 解析结果;
  • rooming_list_resolutions[]:Layer 4 对当前附件给出的 Excel、Rooming List 标签、唯一 Tour Code 及状态;
  • 完整 evidence registry 和 canonical input_hash。

当前文件名型 resolver 只检查 current_material=true 且完整文件名匹配批准的 LLT...xlsx 规则的附件,并使用 既有材料 attributes 快照和 Evidence 形成该数组。OOXML 安全结构检查通过时为 RESOLVED;文件名命中但 bytes、身份或结构检查失败时保留中立候选,输出 tour_code=null / target_status=UNRESOLVED。旧 artifact 没有 识别快照时数组自然为空。Layer 5 不得自行打开 Excel 补做,也不得把正文关键词或普通 QBD/LianTai 更新表提升为 Rooming List。

Layer 4 不查询数据库当前订单或任务历史,也不使用历史值补齐来源字段。来源必要值缺失时,对应固定槽位保持 null/UNRESOLVED 并转人工。Layer 4 的 Room/Rate、GRPA1、早餐和名单判定规则见各自契约,不复制进 Booking Agent。

4. Agent 精简输入与决定

Agent wire schema 为 booking-business-agent-compact-input-v1.schema.json。所有白名单入站 邮件只要进入预订流程,都必须投影为此对象并调用 Booking Agent 一次。

允许字段:

  • contract_meta/source_input_hash;
  • 当前 subject/body 业务材料;
  • 完整有序的邮件线程 email_history[] 正文;
  • 当前附件说明;
  • 按物理来源单元组织的必要Layer3事实和Layer4 Room Type/Rate解析状态;
  • 语义材料、目标关系、Rooming List options、方向、输入 issue 与当次短引用。

物理禁止进入 Agent:History 附件、数据库当前订单快照、数据库历史任务、生命周期准入结论、附件 bytes/Base64、 完整Layer3技术包装、evidence registry、下载 URL、Secret、数据库/PMS 凭据。source_input_hash 回指未裁剪的内部 canonical input;短引用只在本次调用内有效。

Agent只返回 booking-business-agent-compact-decision-v1.schema.json: 业务类型、目标/单元/Rooming短引用、Booking Type、Trace语义内容、linked/derived选择、Risk和未归类原文。 它不返回source_input_hash、日期、房型、房量、价格、Rate、早餐、evidence ID、payload、稳定ID或根级disposition。 Layer 5B使用信息系统内部保存的canonical input hash关联本次结果。

组装器不是“把两个对象随便塞在一起”:它校验同一 run/message/revision/version、保持 Layer3ResultV2 不变, 按 source_unit_ref/room_item_ref 绑定来源槽位,以稳定顺序生成 order_contexts[],验证 evidence closure,并只 生成一个 canonical BookingDecisionInputV2/hash。Layer 5A从该对象投影一次并调用一次;Layer 5B按返回短引用从同一 canonical输入复制固定字段,展开为一份完整CandidateDecisionV2。

5. CandidateDecisionV2 根结构

完整Candidate schema仍为 booking-candidate-decision-v2.schema.json。它由Layer 5B根据Agent精简决定组装, decision_origin=AGENT;本地错误方向兜底可为 DETERMINISTIC,调用失败为 FAIL_CLOSED。

根集合:

  • target_decisions[]:New、Update、Cancel、Trace、Rooming List;
  • derived_actions[]:Allotment source deduction 或 source whole cancel;
  • risk_items[]:只隔离无法识别类型/必要目标或异常范围的事项;
  • unclassified_source_texts[]:未被任何任务承接的当前原文,按 sequence=0..n 保序;
  • agent_trace_ref/issues/evidence_refs。

result_disposition 是确定性汇总,不是另一个任务:

内容 disposition
至少一个 target 或 derived action,可同时有局部 Risk/未归类原文 IN_SCOPE_TASKS
无任务但至少一个 Risk RISK_NOTIFICATION
无任务、无 Risk、至少一项未归类原文 GENERAL_NOTIFICATION
受控错误方向,且其他集合全空 IGNORED_DIRECTION

FAIL_CLOSED 只能是 RISK_NOTIFICATION。General 原文不是任务、不是通知卡、无需业务确认;Risk 也不创建 可确认任务。

6. Target typed payload

business_type/action_kind/payload_type 必须匹配:

business_type action_kind payload_type
NEW_BOOKING CREATE_BOOKING NEW_BOOKING
UPDATE_BOOKING UPDATE_BOOKING UPDATE_BOOKING
CANCEL_BOOKING CANCEL_BOOKING CANCEL_BOOKING
TRACE_RESERVATION_NOTES CREATE_TRACE TRACE
ROOMING_LIST CREATE_ROOMING_LIST_NOTICE ROOMING_LIST

不存在 PAYMENT business/action/payload。付款原文只进入 unclassified_source_texts[]。

6.1 New / Update

两者 wire shape 相同但类型互斥,固定包含 booking_type/source_unit_ref/arrival_date/departure_date/room_items/ rate_code/breakfast_included/breakfast_restaurant_code。第二道门槛缺失时字段仍必须结构性存在,值可为 null/空数组, 交 Layer 6 review;不得改成 Risk。

每个 CandidateRoomItem 保存 room_item_ref/source_room_raw/source_room_name/room_type/quantity/ source_price_display/evidence_refs。不同物理来源行不合并;同一来源内映射到相同 Room Type 的房型也不聚合。 房量 wire 允许保留 null/0/负数/小数供人工修正,Layer 6 只接受精确正整数。价格只是可选原文展示,不是必填, 不存在 people/pax/total_guests。

6.2 Cancel

CancelBookingTaskPayload 只含 payload_type=CANCEL_BOOKING。目标在 target_identity 表达;不得附带住期、 房型房量、Rate、早餐、价格或 Booking Type。

6.3 Trace

TraceTaskPayload 包含非空 service_items[] 和整项唯一 department_code=FO/FO+HSK/null。每个 service item 保存 ID、GENERAL_SERVICE/EXTRA_BED、完整 service_text、可选 Room Type/数量和 evidence,不存在 parameters。 两种 service type 都必须有 service_text;Extra Bed 数量未写时 Agent 默认 1,Room Type 或合法数量缺失时保留 Trace,由 Layer 6 review。不存在单独 HSK。

6.4 Rooming List

成功 payload 只包含一个 attachment_id 和证据;一个 target 对应一个当前 Excel。多附件数组不属于成功 shape。 该 target 必须精确对应一项 RESOLVED 的 Layer 4 resolution,并保持相同 attachment、Tour Code 与材料 Evidence; 同一 Tour Code 有多份有效附件时不得接受成功 target。

6.5 Linked action

target 内只保留有真实业务含义的 GROUP_CODE_REPLACEMENT。Trace/Rooming 与 Booking 同属 Tour Code 不需要为了 展示制造 linked action。

7. Allotment derived action

derived_action_type 只允许:

  • ALLOTMENT_SOURCE_DEDUCTION:一项只引用一个物理目标 actual_target_decision_id,保存 source Tour Code 与 room_items[] 的 Room Type/房量/evidence;同 source 的两个 actual 行输出两项,不聚合;
  • ALLOTMENT_SOURCE_WHOLE_CANCEL:一个唯一 source Tour Code 一项,actual_target_decision_id=null 且房型列表为空。

两种 shape 均不包含 source old/remaining、余额、住期、Rate、早餐、价格或 contributions。Layer 5 不查库存、不做 差额运算;before→after 的 Update 行不派生 deduction。

8. Layer 6 ValidationResultV2

Layer 6 只返回目标/派生项的 VALID/REVIEW_REQUIRED/RISK/REJECTED、blocker/review,不返回替换后的业务类型或 payload。校验前后 candidate_hash 必须相同。

第二道门槛稳定 code 包括:

  • 身份:GROUP_TOUR_CODE_REQUIRED、FIT_TARGET_TOUR_CODE_OR_NAME_REQUIRED、 CANCEL_TARGET_TOUR_CODE_OR_NAME_REQUIRED;
  • New/Update:Booking Type、source unit、成对住离日期、Room Type/正整数房量、Rate Code、早餐/餐厅;
  • Tour Code 一致性:TOUR_CODE_RATE_CODE_CONFLICT/TOUR_CODE_BREAKFAST_CONFLICT/ TOUR_CODE_BOOKING_TYPE_CONFLICT;
  • Trace:部门、Extra Bed Room Type/数量;
  • Allotment:source、实际目标引用、Room Type/正整数扣减量;
  • Layer 4 等值:候选已知值不得与其唯一来源单元的住期、房型、数量、价格、Rate/早餐冲突。
  • Rooming List 等值:成功 target 必须精确匹配唯一 resolved resolution 的 attachment、Tour Code 与材料 Evidence; 同 Tour Code 多份有效附件、伪造 attachment/Tour Code/Evidence 均进入本地拒绝或人工边界。

本期 Layer 6 不查询数据库生命周期,不生成 NEW_BOOKING_ALREADY_EXISTS、 BOOKING_LIFECYCLE_* 或 PRIOR_TASK_REQUIRES_COMPLETION。邮件 History 只作为 Layer 5 的语义上下文,不能被 Layer 6 当成订单存在性或任务先后证据。

9. 投影、持久化与发布边界

  • Layer 7 只为 target/derived 建立可确认项;Risk 是不可确认通知,未归类原文只保存在 Candidate artifact;
  • 当前无 V2→V4 authoritative writer;本契约不授权创建 TaskCard;
  • SHADOW/CAPTURE_ONLY 不得写业务任务;AUTHORITATIVE、真实数据库、真实 SuperAgent/Profile/Secret、PMS/Opera 均需另行批准;
  • Main Prompt 与 Skill 是两个独立交付物,Main Prompt 不得进入 .skill archive。