# S03 Cancel Booking Skill

## 1. Skill Identity

- catalog_code: `S03`
- skill_id: `cancel_booking_skill`
- skill_name: `Cancel Booking Skill`
- task_type: `Cancel Booking`

## 2. Purpose

本 Skill 用于判断一条来自 `body_current` 的当前邮件事件，是否可以生成 Cancel Booking 任务。

第一版判断保持简单：

- 可以明确且安全创建取消任务：输出 `normal_task`
- 不能安全自动处理、需要人工判断或人工处理：输出 `manual_review`

本 Skill 第一版不输出 `exception_task`，也不输出 `no_action`。

进入 AI 工作台的 current 邮件事件，都应生成某种用户可见记录。对于确认已取消、感谢、FYI、noted、received、confirmed receipt、转发说明、无新的取消动作但需要展示的信息，不应路由到本 Skill；明确轻量消息应由 `message_notification_skill` 输出 `informational_message`。

本 Skill 不直接写数据库或外部系统，不写 Opera，不直接执行取消动作，不确认 Payment，不生成 Receipt。

如果本 Skill 输出 `ai_task_result` 且通过统一输出校验，AI 运行时 / Main Agent / Skill Orchestrator 后续可调用信息系统提供的统一受控 Tool / API 创建普通取消任务卡、人工复核 / 人工处理任务卡或信息系统可展示记录。真实 TaskCard ID、任务状态机、幂等控制、并发保护和执行前硬校验仍由信息系统负责。

## 3. Scope

本 Skill 覆盖以下取消对象：

- FIT Reservation
- 普通团队预订
- Group Block
- Allotment / Control Block
- 控房 / 配额

本 Skill 合并原 `Cancel Allotment Skill` 的第一版职责。Cancel Allotment 不再作为独立 Skill 出现，而是本 Skill 的内部对象类型。

本 Skill 的内部 `cancel_object_type` / subtype 只用于结构化表达、测试覆盖和任务卡展示，不拆分为多个 Skill，也不拆分为多个 Tool。

支持的 `cancel_object_type`：

- `fit_reservation`
- `group_block`
- `allotment_control_block`

## 4. When To Use

当 `body_current` 中明确提出取消既有普通预订、Group Block、Allotment / Control Block、控房或配额时，Main Agent 可以调用本 Skill。

典型语义包括：

- cancel booking
- cancel reservation
- cancel this group
- release / cancel this block
- cancel allotment
- cancel allocation
- cancel control block
- 取消控房 / 配额
- `CXL`
- 明确要求整单取消，而不是修改

`CXL` 是常见取消缩写，可作为强取消信号；但不能只因为看到 `CXL` 字样就机械路由到本 Skill。仍需确认它是 `body_current` 中的当前动作，并结合上下文判断取消对象属于 `fit_reservation`、`group_block` 还是 `allotment_control_block`。

## 5. Must Not Use When

以下情况不得使用本 Skill 生成 Cancel Booking `normal_task`：

- 当前邮件是在全新创建预订，应走 `new_booking_skill`。
- 当前邮件是在修改日期、房量、房型、人数、备注等，应走 `update_booking_amendment_skill`。
- 当前邮件只是减少房量、修改部分日期、释放部分配额，这更像 Update，不是整单取消。
- 当前邮件只是提交 Rooming List / Name List。
- 当前邮件只是收到 Voucher / Credit Voucher / payment slip / payment confirmation。
- 取消动作、`CXL` 或取消状态只出现在 `body_thread`，不在 `body_current`。

如果当前邮件没有新的取消动作，只是确认“已取消”、FYI、感谢、noted、received、confirmed receipt、转发说明等，但仍需要前端展示，则不应路由到本 Skill，应由 `message_notification_skill` 输出 `informational_message`。

## 6. Allowed Result Types

第一版只允许：

- `normal_task`
- `manual_review`

第一版不输出：

- `exception_task`
- `no_action`
- `informational_message`

原因：前三个核心 Skill 第一版先不做复杂异常任务分类，也不使用 no_action。无法安全自动处理的 current 邮件事件，统一生成结构化 `manual_review`，供信息系统创建用户可见记录；明确轻量消息通知由 `message_notification_skill` 处理。

## 7. Key Context Needed

Main Agent 应尽量提供以下上下文：

- `source_message_id`
- `source_event_index`
- 当前邮件事件文本，只能来自 `body_current`
- 当前邮件相关附件摘要
- `group_code`
- `confirmation_number`
- `routing_context`
- 既有订单上下文
- pending / open task 上下文
- active workflow 上下文
- terminated record 上下文
- 取消对象类型线索，包括 `fit_reservation`、`group_block`、`allotment_control_block`
- 可用历史证据上下文

`routing_context` 至少应覆盖：

- 同 key 是否已有有效订单记录
- 同 key 是否已有 pending / open task
- 同 key 是否已有 active workflow
- 同 key 是否已有 terminated record
- 当前对象状态是否支持继续创建取消任务

本 Skill 最关键的上下文判断是：取消对象属于 `fit_reservation`、`group_block`，还是 `allotment_control_block`。

`body_thread` 只能作为 evidence context，不得作为生成 Cancel Booking `normal_task` 的来源。

## 8. Input Requirements Summary

本 Skill 输入应包含：

- 当前取消请求摘要
- `body_current` 中的相关原文片段
- 附件摘要
- 已提取的核心 key
- routing context
- 可用历史证据上下文
- 能帮助判断取消对象类型的上下文

如果一封邮件中同时取消多个订单，Main Agent 应拆成多个 `source_event_index`。

本 Skill 不要求 Main Agent 在输入阶段固定完整 `extracted_fields` schema。

具体字段抽取、字段标准化、取消状态枚举、取消原因分类、外部系统执行规则，应放在本 Skill 后续详细字段章节或统一输出契约中维护。

### 8.1 Bundled / Shared References

本 Skill 可按需读取以下 shared reference：

- `../references/qbd_liantai_email_table_rules.md`
  - When the current message, attachment, sender, subject, or extracted evidence indicates QBD or LianTai Excel/table workflow, read this shared reference.
  - 触发条件包括：QBD / LianTai 发件人或渠道、QBD / LianTai 表头、Excel / table 附件、`CANCEL BOOKING`、`CXL`、`CANCEL ALLOTMENT`、`AMEND ALLOTMENT`、`AMD`、`CFM`、黄色 / 高亮行、红字、划线、`โรงแรม` 或酒店状态列等线索。
  - 用于判断当前表格行是否代表取消请求、黄色 / 高亮行如何拆成独立 current events、`CXL` 是否为当前动作、取消对象是 FIT Reservation / Group Block / Allotment Control Block，以及 AMD/CXL/CFM 状态是否只是历史或完成证据。

Reference 使用原则：

- 如果 QBD / LianTai reference 识别出多个当前有效黄色 / 高亮取消行，必须按行拆分：一行一个 `source_event_index`，一行一个 `ai_task_result`，一行一个下游 task card 候选；不得跨行合并。
- 即使多行共享同一 `group_code`、`confirmation_number`、日期、酒店、批次、supplier、主题或附件，也不得合并为一个 Cancel Booking task。
- 如果某一行信息不完整或不能安全处理，只让该行输出结构化 `manual_review`，不得阻塞其他可正常处理的行。
- `CXL`、红字、取消状态或状态列日期不能脱离 current 行 / 高亮 / 邮件动作上下文单独生成 Cancel Booking `normal_task`。
- 如果 reference 显示状态列 AMD/CXL/CFM + 日期只是完成或历史证据，不得把它扩大为新的取消任务。
- If any trigger signal is present but the table/highlight/row evidence is unavailable, unreadable, or ambiguous, do not guess. Output structured manual_review and include the missing table evidence in manual_review.evidence_to_check.
- `context_used` 应记录使用了哪个 reference 以及命中的规则摘要。

## 9. Decision Rules

1. 先确认取消动作是否出现在 `body_current`。
2. 如果取消动作、`CXL` 或取消状态只出现在 `body_thread`，不得输出 `normal_task`。
3. 判断当前事件是否确实是取消既有普通预订 / Group Block / Allotment / Control Block，而不是修改、部分释放、名单、Voucher 或 FYI。
4. 如果能提取到 `group_code` 或 `confirmation_number`，必须使用对应 `routing_context`。
5. 如果 current 邮件、附件、发件人、主题或抽取证据显示 QBD / LianTai Excel/table workflow，应读取 `../references/qbd_liantai_email_table_rules.md`；只有当前表格行、高亮 / 黄色行、取消动作、状态证据和取消对象类型可靠时，才可基于表格输出 `normal_task`。
6. 对 QBD / LianTai 表格中的多个当前有效黄色 / 高亮取消行，必须按行拆分；每一行生成独立 `source_event_index` 和独立 `ai_task_result`，不得跨行合并。
7. 如果当前事件明确是取消普通 Booking / Group Block / Allotment / Control Block，且 routing context 支持继续创建取消任务，输出 `normal_task`。
8. 如果不能安全自动处理，输出结构化 `manual_review`。
9. 如果当前邮件只是确认已取消、FYI、感谢、noted、received、confirmed receipt、转发说明等轻量信息，但需要用户可见记录，不应路由到本 Skill，应由 `message_notification_skill` 输出 `informational_message`。

以下情况第一版统一输出 `manual_review`：

- 找不到原订单。
- 目标对象不明确。
- 无法确认 `cancel_object_type` 是 `fit_reservation`、`group_block` 还是 `allotment_control_block`。
- 前置任务未完成。
- 订单已终止，但当前邮件又提出取消。
- 取消请求可能已经处理过。
- 重复取消请求。
- 当前邮件内容冲突。
- 关键信息缺失。
- 当前邮件像取消，但也可能是修改或释放部分房量。
- 当前邮件只有 `CXL` 字样，但无法确认它是当前取消动作。
- QBD / LianTai 表格触发信号存在，但表格、黄色 / 高亮行、行号、红字、状态列或当前行证据不可读、缺失或不明确。
- 当前邮件没有新的有效取消动作，但与既有订单上下文相关，且无法安全判断是否需要人工处理。

## 10. Normal Task Output

仅当可以安全创建 Cancel Booking 任务时，输出：

```json
{
  "result_type": "normal_task",
  "task_type": "Cancel Booking",
  "current_or_history": "current"
}
```

完整输出必须符合统一输出契约。

关键要求：

- `source_message_id` 必须原样带回。
- `source_event_index` 必须原样带回。
- `case_keys.group_code` 和 `case_keys.confirmation_number` 按实际提取结果填写。
- `visible_reason` 应说明为什么这是当前邮件中的取消请求。
- `relevant_message_excerpt` 必须来自 `body_current`。
- `attachments` 只包含当前事件相关附件。
- `context_used` 应记录用于判断的 routing context 摘要。
- `extracted_fields` 应填入可可靠抽取的取消字段。
- 如果来自 QBD / LianTai 表格行，应在 `extracted_fields` 中保留该行的附件名、sheet 名、行号或行标签、高亮状态、动作 / 状态单元格、原始日期、原始房型、原始房量、原始价格和取消状态证据。

建议第一版 `extracted_fields` 至少考虑：

- `cancel_object_type`
- `candidate_cancel_object_type`
- `cancel_scope`
- `arrival_date`
- `departure_date`
- `group_name`
- `guest_name`
- `room_quantity`
- `supplier`
- `requested_cancel_effective_date`
- `cancellation_note`

## 11. Manual Review Output

当邮件看起来与 Cancel Booking 相关，但不能安全自动创建取消任务时，输出：

```json
{
  "result_type": "manual_review",
  "task_type": "Cancel Booking",
  "current_or_history": "current"
}
```

`manual_review` 不能只是“人工看一下”。它必须带结构化信息，方便信息系统创建人工复核 / 人工处理任务卡。

即使输出 `manual_review`，也要尽力结构化抽取：

- 能确定的 `case_keys` 必须填入。
- 能确定的 `cancel_object_type` 应填入 `extracted_fields`。
- 如对象类型尚不完全可靠，可同时填入 `candidate_cancel_object_type`。
- 能确定的取消相关字段应填入 `extracted_fields`。
- 不能可靠确认的字段不要猜，填 `null`、空字符串或空数组。
- 不确定原因必须写入 `manual_review.reason_code`。
- 缺失字段写入 `manual_review.missing_fields`。
- 阻塞点写入 `manual_review.blocking_points`。
- 冲突点写入 `manual_review.conflicting_points`。
- 建议人工处理动作写入 `manual_review.suggested_human_actions`。
- 建议人工查看的证据写入 `manual_review.evidence_to_check`。

建议结构：

```json
{
  "manual_review": {
    "reason_code": "",
    "visible_reason": "",
    "review_record_type": "",
    "blocking_points": [],
    "missing_fields": [],
    "conflicting_points": [],
    "suggested_human_actions": [],
    "evidence_to_check": []
  }
}
```

`review_record_type` 可用于区分：

- `manual_review_required`
- `manual_processing_required`

典型 `reason_code` 可使用以下语义。它们只是人工复核原因代码，不是 `exception_task` 枚举：

- `TARGET_OBJECT_AMBIGUOUS`
- `ORDER_RECORD_NOT_FOUND`
- `CANCEL_ALREADY_PROCESSED`
- `CANCEL_SCOPE_UNCLEAR`
- `CANCEL_OBJECT_TYPE_UNCLEAR`
- `CURRENT_ACTION_NOT_CONFIRMED`

示例语义：

```json
{
  "case_keys": {
    "group_code": "LLT260727FA",
    "confirmation_number": null
  },
  "extracted_fields": {
    "cancel_object_type": "group_block",
    "candidate_cancel_object_type": "group_block",
    "cancel_scope": "full_cancel",
    "arrival_date": "2026-07-27",
    "departure_date": null,
    "group_name": null,
    "guest_name": null,
    "room_quantity": null,
    "supplier": "TBD",
    "requested_cancel_effective_date": null,
    "cancellation_note": "CXL request in body_current"
  },
  "manual_review": {
    "reason_code": "CANCEL_SCOPE_UNCLEAR",
    "visible_reason": "当前邮件疑似取消请求，但无法确认是整单取消还是部分释放房量。",
    "review_record_type": "manual_review_required",
    "blocking_points": ["无法确认取消范围"],
    "missing_fields": ["cancel_scope"],
    "conflicting_points": [],
    "suggested_human_actions": ["核对邮件正文和订单上下文，确认是否整单取消"],
    "evidence_to_check": ["body_current", "routing_context.matched_order_records"]
  }
}
```

## 12. Tool Invocation Placeholder

当前创建任务卡 / 可见记录的具体 Tool / API 尚未实现，本节只预留对接位置和语义契约，不定义最终 endpoint、鉴权方式或完整返回结构。

- tool_status: `TBD`
- suggested_tool_name: `create_task_card` / `TBD`
- tool_purpose: 根据信息系统统一任务卡创建接口，创建普通取消任务卡、人工复核 / 人工处理任务卡或信息系统可展示记录。
- tool_owner: 信息系统。
- caller: AI Runtime / Main Agent / Skill Orchestrator，最终由系统架构确认。
- invocation_timing: Skill 输出 `ai_task_result` 并通过 Unified Output Contract 校验后。
- payload_rule: Skill 能抽取出的字段照实填入；任务卡需要但无法抽取的字段填 `null` / empty，并在 `manual_review.missing_fields` 或 `manual_review.blocking_points` 中说明。
- must_not_call_when:
  - Skill 输出不符合统一契约
  - current 事件完全不应进入 AI 工作台记录
  - 输出内容缺少任务卡 / 可见记录创建所需的最低溯源字段
- minimum_payload_should_include:
  - `source_message_id`
  - `source_event_index`
  - `catalog_code`
  - `skill_id`
  - `result_type`
  - `task_type`
  - `case_keys`
  - `visible_reason`
  - `relevant_message_excerpt`
  - `attachments`
  - `context_used`
  - `extracted_fields`
  - `manual_review`
  - `idempotency_key` / `TBD`
- expected_tool_result: `TBD`

Tool 设计原则：

- Cancel Booking Skill 只调用一个统一任务卡创建 Tool：`create_task_card`。
- 不因为取消对象有 FIT / Group Block / Allotment Control Block 多类就拆多个 Tool。
- `manual_review` 也可以通过统一 Tool 创建人工复核卡。
- AI 不直接写数据库，只调用信息系统暴露的受控 Tool / API。
- 信息系统负责生成真实任务卡 ID、人工复核任务卡 ID 或可见记录 ID。
- 信息系统负责幂等、并发、状态机和最终落库。
- 信息系统在创建任务卡或执行前仍需做硬校验，不能只依赖 AI 判断。
- 如果 Tool 尚不可用，本 Skill 仍应输出合格的 `ai_task_result`；不得编造 Tool 调用结果。

## 13. Conflict Or Fallback Rule

- 如果无法判断 `cancel_object_type` 是 `fit_reservation`、`group_block` 还是 `allotment_control_block`，应优先查看 routing context。
- 如果仍无法判断，输出结构化 `manual_review`。
- 如果当前事件同时像取消和修改，优先看邮件语义和 routing context；仍无法拆分时输出结构化 `manual_review` 或交给 `exception_detection_skill`。
- 如果订单已终止、已被处理、或取消状态不清，不由 Main Agent 直接下结论，由本 Skill 输出结构化 `manual_review`。
- 如果 `CXL` 只出现在历史引用或旧状态中，不得仅凭该缩写生成 Cancel Booking `normal_task`。

## 14. Must Not Do

- 不得从 `body_thread` 生成 Cancel Booking `normal_task`。
- 不得只因为看到 `CXL` 就机械生成 Cancel Booking `normal_task`。
- 第一版不得输出 `exception_task`。
- 第一版不得输出 `no_action`。
- 不得输出 `informational_message`；明确轻量消息通知应路由到 `message_notification_skill`。
- 不得把 `manual_review` 输出成空泛的“人工看一下”。
- 不得在取消对象不明确时猜测 `cancel_object_type`。
- 不得在取消范围不明确时猜测整单取消或部分释放。
- 不得因为取消对象有 FIT / Group Block / Allotment Control Block 多类而拆成多个 Tool。
- 不得编造尚未实现的 Tool endpoint、鉴权、返回结构或调用结果。
- 不得绕过信息系统 Tool / API 直接创建真实 TaskCard。
- 不得直接执行取消写入。
- 不得写 Opera。
- 不得确认 Payment。
- 不得生成 Receipt。
- 不得修改数据库。
