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

199 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Booking 邮件 contracts-v2
| 项目 | 内容 |
| --- | --- |
| 文档状态 | 权威契约;离线实现已验收;真实运行与发布 `HOLD` |
| 契约版本 | `booking-contracts-v2` |
| 业务规则 | [Booking Business Agent v1.0](booking-business-agent-rules-v1.md) |
| 对应架构 | [Booking 邮件处理架构 v0.5](booking-email-architecture-v0.5.md) |
| 决策依据 | 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`](schemas/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`](schemas/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`](schemas/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`](schemas/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。