Complete the selective V2 checkpoint with its minimal AgentBus, object-storage, replay persistence, and validated-workbench shared dependency closure.
11 KiB
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 不得进入
.skillarchive。