Complete the selective V2 checkpoint with its minimal AgentBus, object-storage, replay persistence, and validated-workbench shared dependency closure.
199 lines
11 KiB
Markdown
199 lines
11 KiB
Markdown
# 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。
|