---
name: flight-booking
description: "中国国内机票查询与预订。当用户需要查航班、买机票、订机票、查机票价格、查询订单状态、取消订单、退票或改签时使用。"
---

# Flight Booking（机票查询与下单）

国内机票查询、创建订单、支付校验与申请出票、退票与改签的全流程 skill。分步执行并在信息未确认时主动向用户确认。

> **平台说明**：当前订票通过 **Sophnet 平台**完成，支付使用平台账户余额。若余额不足导致出票失败，需提示用户前往 Sophnet 平台进行充值后再重试。

## 重要原则

- **信息完全确定才执行**：创建订单、支付与出票涉及真实资金与行程，仅在用户已明确确认行程、航班、舱位、乘机人及联系人信息后执行。
- **信息不确定时主动确认**：若用户未提供或存在歧义（如出发/到达城市、日期、航班号、舱位、姓名、手机号、证件号、性别等），必须先向用户逐项确认，不得猜测或默认填写。
- **出票前务必再次确认**：**不得在创建订单后自动连续执行出票**。第二步创建订单完成后，应先向用户展示订单号、航程与金额，**在用户明确表示「确认出票」或「去支付/出票」后再执行第三步 pay-issue**；未得到用户确认前，不要主动执行出票。
- **退票前务必二次确认**：**绝对不得在用户未明确确认的情况下执行退票。** 退票流程必须分两步：第一步用 `--dry-run` 查询并向用户展示退改签规则和手续费，第二步**必须等用户明确说「确认退票」「退吧」等确认性语句后**才执行实际退票命令。即使用户说了「我想退票」「帮我看看退票」，也只能先执行 dry-run 展示规则，**不得直接执行退票**。
- **用户必填变量**：仅需通过**环境变量**配置乘机人 `name`、`mobile`、`credentialNo`、`gender`。联系人默认与乘机人一致，无需重复设置。
- **航班查询结果展示须完整**：向用户展示航班列表时，**必须展示脚本返回的全部航班，不得截断、不得只展示前 N 条、不得"为节省篇幅"省略任何一条**。脚本已不再做条数限制，返回多少条就展示多少条（即使有几十条也必须全部列出）。每条须包含航班号、航空公司、出发/到达机场、**起飞时间**、**到达时间**（两列分开）、**经济舱价格与公务舱价格**、**机建费**、**燃油费**，不得漏列（无公务舱时显示「—」）。

## 流程概览

| 步骤 | 说明 | 脚本子命令 |
|------|------|------------|
| 第一步 | 航班查询，按经济舱价格排序并**展示全部**航班（每航班一行，含经济舱/公务舱最低价；不截断） | `search` |
| 第二步 | 创建订单：可选舱位报价查询 → 验舱验价 → 创建订单（常规） | `create-order` |
| 第三步 | 支付前校验 + 申请出票 | `pay-issue` |
| 附加 | 查询订单状态/详情 | `order-status` |
| 附加 | 取消订单（待支付等可取消状态） | `cancel-order` |
| 附加 | 退票（已出票订单，需先展示退改签规则并确认） | `refund-order` |
| 附加 | 改签（已出票订单，需先展示改签手续费，再 search 查询目标航班） | `change-order` |
| 附加 | 列出本地缓存的历史订单（用户未提供订单号时使用） | `list-orders` |
| 附加 | 保存当前乘客身份到本地文件 | `save-passenger` |
| 附加 | 列出已保存的乘客身份（未配置环境变量时选取） | `list-passengers` |

## 第一步：航班查询

在用户提供**出发城市**、**到达城市**、**出发日期（YYYY-MM-DD）** 后执行查询。出发/到达城市支持**三字码**或**城市名**，脚本会自动将城市名转为三字码（如 杭州→HGH、北京→BJS）。

**重要：当用户提到了具体航班号（如"查一下 CA1723"、"CA1723 这个航班多少钱"），必须在 search 命令中加上 `--flight-no` 参数，只查该航班，不要忽略用户指定的航班号。**

```bash
# 查询所有航班
python3 {baseDir}/scripts/flight_booking.py search --from-city 杭州 --to-city 北京 --from-date 2026-02-25

# 查询特定航班（用户指定了航班号时必须使用 --flight-no）
python3 {baseDir}/scripts/flight_booking.py search --from-city 杭州 --to-city 北京 --from-date 2026-02-25 --flight-no CA1723
```

- 支持的城市名示例：杭州、北京、上海、广州、深圳、成都、西安、重庆、南京、武汉、青岛、厦门、昆明、海口、三亚等；也可直接填三字码（如 HGH、BJS、PVG）。若用户只说「杭州到北京」，可直接用城市名执行。
- **输出**：表格列为「航班号、航空公司、出发机场（含航站楼）、到达机场（含航站楼）、起飞时间、到达时间、经济舱、公务舱、机建费、燃油费」；起飞时间与到达时间为**两列**分别展示，按经济舱价格排序，**返回当日全部航班，不做条数截断**。航站楼信息拼在机场名后（如"萧山机场T3"）。机建费和燃油费为每张票的附加税费。
- **状态保存**：查询成功后会将本次结果写入 `~/.openclaw/flight-booking/.last_search.json`，供下一步创建订单时使用；用户只需选择航班号即可下单，无需再次输入出发/到达/日期。
- 将表格展示给用户后，由用户选择要预订的**航班号**（及可选舱位：经济舱 Y / 公务舱 C，默认 Y）。

**航班查询结果向用户展示时，请务必做到信息完整、条数完整：**

- **条数（重点强调）**：脚本返回多少条就展示多少条，**严禁只展示前 15 条 / 前 N 条 / 部分条数**，也不得以"航班较多，仅展示部分"等理由省略。即使返回 30+ 条也必须**逐条全部列出**，由用户自行选择。
- **每条航班必须包含的字段**：航班号、航空公司、出发机场（含航站楼）、到达机场（含航站楼）、**起飞时间**、**到达时间**、**经济舱价格**、**公务舱价格**、**机建费**、**燃油费**。起飞时间与到达时间为**两列**，缺一不可；若某航班无公务舱则显示「—」或「无」，不得省略该列。
- **时间**：脚本输出为「起飞时间」「到达时间」两列，向用户展示时也须保留这两列，**不得用 ? 或空白代替到达时间**；若数据中有 `toTime` 必须原样展示。
- **价格**：经济舱、公务舱两列都要展示；无该舱位时用「—」表示，不得整列不显示。
- **税费**：机建费（departureTax）、燃油费（fuelTax）为每张票的附加费用，须完整展示，便于用户了解实际总价。
- 向用户呈现时，优先以表格形式完整列出上述所有列，便于用户比价和选择航班。

## 第二步：创建订单

在用户从第一步表格中**已选择航班号**后执行。行程（出发/到达/日期）与航班详情从**最近一次 search 结果**自动读取，无需用户再次输入；乘机人/联系人从环境变量读取。

必填参数（仅一项）：
- `--flight-no`：用户选择的航班号（须为最近一次 search 结果中的航班）

可选：
- `--cabin-grade`：Y（经济舱）或 C（公务舱），默认 Y

```bash
python3 {baseDir}/scripts/flight_booking.py create-order --flight-no CA1723
# 或指定公务舱
python3 {baseDir}/scripts/flight_booking.py create-order --flight-no CA1723 --cabin-grade C
```

- 若未执行过 search 或 `.last_search.json` 不存在/无效，会提示「请先执行 search 查询航班后再下单」。
- 可选：乘机人生日默认从**身份证号**（18 位）中自动解析，无需单独填写，也可用 `--passenger-birth-day` 覆盖；`--passenger-type`（默认 ADU）、`--credential-type`（默认 IDENTITY）。
- 输出：订单号 `orderNo`、PNR、超时取消时间、应付金额等；用于第三步。
- **重要**：创建订单成功后**不要立即执行 pay-issue**。应先向用户展示上述信息，待用户确认后再执行第三步出票。

## 第三步：支付前校验与申请出票

**仅在用户明确确认要出票后再执行**（例如用户说「确认出票」「去支付」「可以出票了」等）。不得在创建订单后未经用户确认就自动执行本步骤。

在用户确认后，传入订单号即可。脚本会先查询订单详情获取应付金额，再执行支付前校验与申请出票。

```bash
python3 {baseDir}/scripts/flight_booking.py pay-issue --order-no <orderNo>
```

- 脚本内部：先调订单详情接口取 `totalAmount`，再支付前校验，再申请出票；无需用户传入金额。
- 输出：成功/失败及接口返回信息。
- **再次强调**：仅当用户已确认订单信息并明确要求出票时，才执行本命令。

## 查询订单状态

用户提供订单号时，可查询订单详情与状态。

```bash
python3 {baseDir}/scripts/flight_booking.py order-status --order-no <orderNo>
```

- 输出：订单状态、处理状态、支付状态、乘客与航程等（JSON 或可读摘要）。

## 取消订单

用户提供订单号时，可取消处于可取消状态的订单（如待支付等）。

```bash
python3 {baseDir}/scripts/flight_booking.py cancel-order --order-no <orderNo>
```

- 参数：`--order-no`（必填）。
- 输出：成功时返回「订单 xxx 已取消」；失败时返回接口 code/msg。

## 退票

**仅限已出票（ISSUED）的订单才能退票。** 退票前必须先向用户展示退改签规则（退票手续费），在用户了解费用并确认后再正式执行退票。

### 退票流程（分两步）

**第一步：查询退改签规则（dry-run）**

先使用 `--dry-run` 参数查询订单的退改签规则，不实际执行退票：

```bash
python3 {baseDir}/scripts/flight_booking.py refund-order --order-no <orderNo> --dry-run
```

脚本会调用订单详情接口，校验订单是否为 ISSUED 状态，然后返回 JSON 输出，其中 `refundChangeRules` 包含每个航段的退改签规则：
- `refund_rules`：各时间阶段对应的**退票手续费**（`period` 为时间段描述，`fee` 为手续费金额）
- `change_rules`：各时间阶段对应的**改签手续费**
- `facePrice`：票面价（用于计算退款金额 = 票面价 - 手续费 + 税费）
- `baggage`：行李额说明
- `remark`：其他备注

收到结果后，**必须**将退票手续费以表格形式展示给用户，例如：

> **退票手续费规则**（航段：济南 → 昆明 KY8208）
>
> | 时间段 | 退票手续费 |
> |--------|-----------|
> | 2026-04-09 19:35前 | ¥390 |
> | 从2026-04-09 19:35至2026-04-14 19:35 | ¥455 |
> | 从2026-04-14 19:35至2026-04-16 15:35 | ¥520 |
> | 2026-04-16 15:35后 | ¥585 |

同时根据**当前时间**告知用户当前所处的退票阶段和对应手续费，并询问用户是否确认退票。

**第二步：用户确认后执行退票**

在用户明确确认要退票后（如「确认退票」「退吧」等），执行退票命令（不带 `--dry-run`）：

```bash
python3 {baseDir}/scripts/flight_booking.py refund-order --order-no <orderNo>
```

- 参数：`--order-no`（必填），为已出票的订单号。
- 输出：成功时返回「订单 xxx 退票申请已提交」及退改签规则；失败时返回接口 code/msg。

**重要：不得跳过第一步直接执行退票。必须先展示退改签规则并取得用户确认。**

## 改签

**仅限已出票（ISSUED）的订单才能改签。** 改签前需先向用户展示改签手续费规则。

### 改签流程

**第一步：查询改签手续费规则**

先调用 `order-status` 或 `refund-order --dry-run` 查询订单详情，从输出中提取 `refundChangeRules` 的 `change_rules` 字段，向用户展示改签手续费表格（格式同退票规则），告知用户当前时间段对应的改签手续费。

```bash
python3 {baseDir}/scripts/flight_booking.py refund-order --order-no <orderNo> --dry-run
```

**第二步：用户确认后查询目标航班**

用户确认改签意向并提供目标日期/航班后，执行 `search` 查询目标航班。

**第三步：执行改签**

从 search 结果中选择目标航班号提交改签。行程与航班详情从**最近一次 search 结果**自动读取；乘机人信息从环境变量读取。

必填参数：
- `--order-no`：原订单号（已出票的订单）
- `--flight-no`：改签目标航班号（须为最近一次 search 结果中的航班）

可选：
- `--cabin-grade`：Y（经济舱）或 C（公务舱），默认 Y

```bash
python3 {baseDir}/scripts/flight_booking.py change-order --order-no <orderNo> --flight-no CA1723
# 或指定公务舱改签
python3 {baseDir}/scripts/flight_booking.py change-order --order-no <orderNo> --flight-no CA1723 --cabin-grade C
```

- 脚本内部会自动调用订单详情校验 ISSUED 状态。
- 若未执行过 search 或 `.last_search.json` 不存在/无效，会提示「请先执行查询航班后再改签」。
- 输出：改签订单号 `changeOrderNo`、PNR、应付金额等；失败时返回接口 code/msg。

## 脚本与配置

- 脚本路径：`scripts/flight_booking.py`，支持子命令 `search`、`create-order`、`pay-issue`、`order-status`、`cancel-order`、`refund-order`、`change-order`、`list-orders`、`save-passenger`、`list-passengers`。
- **状态文件**：`search` 会将最近一次查询结果（含完整航班详情）写入 `~/.openclaw/flight-booking/.last_search.json`，`create-order` 和 `change-order` 仅传航班号时从该文件读取行程与舱位信息；无需用户手动维护。

### 订单缓存与 list-orders

每次调用 `order-status`、`pay-issue`、`refund-order`、`change-order` 成功时，脚本会自动将订单关键信息（订单号、状态、航程、乘客姓名等）缓存到 `~/.openclaw/flight-booking/.orders_cache.json`。

**当用户未提供订单号**（如「帮我退票」「查一下我的订单」）时，应先调用 `list-orders` 查看缓存的历史订单列表：

```bash
python3 {baseDir}/scripts/flight_booking.py list-orders
```

- 输出：JSON 格式的订单摘要列表（按最近更新时间倒序），包含 `orderNo`、`orderShowStatus`、航程、乘客等。
- 根据上下文（航程、日期、乘客姓名等）智能匹配目标订单，向用户确认后再执行后续操作。
- 若缓存为空，提示用户提供订单号或先查询订单状态。

Config（创建订单前必须设置的环境变量）

| 环境变量 | 中文说明 | 必填 |
|----------|----------|------|
| `PASSENGER_NAME` | 乘机人姓名 | 是 |
| `PASSENGER_MOBILE` | 乘机人手机号 | 是 |
| `PASSENGER_CREDENTIAL_NO` | 乘机人证件号（如身份证号） | 是 |
| `PASSENGER_GENDER` | 乘机人性别：`M` 男 / `F` 女 | 是 |

设置方式示例（在运行脚本的环境中提前配置）：

```bash
export PASSENGER_NAME="张三"           # 乘机人姓名
export PASSENGER_MOBILE="13800138000" # 乘机人手机号
export PASSENGER_CREDENTIAL_NO="310101199001011234"  # 乘机人证件号
export PASSENGER_GENDER="M"           # 乘机人性别 M=男 F=女
```

或在 `.env`、openclaw 等配置中统一管理上述变量。未设置时执行 `create-order` 会报错并提示缺失项。

**自动保存**：当用户在对话中提供了乘机人信息（姓名、手机号、证件号、性别），应自动将其保存到对应的环境变量中（通过 `export` 命令设置），无需用户手动配置。后续操作（创建订单、退票等）即可直接使用，不必重复询问。

### 乘客身份持久化

除了通过环境变量设置乘机人信息外，脚本还支持将乘客身份**持久化保存到本地文件**（`~/.openclaw/flight-booking/.passengers.json`），便于多次使用同一身份时无需重复输入。

**保存乘客身份**：当用户提供了新的乘机人信息并通过 `export` 设置好环境变量后，应立即调用 `save-passenger` 将其持久化：

```bash
python3 {baseDir}/scripts/flight_booking.py save-passenger
```

- 从环境变量 `PASSENGER_NAME`、`PASSENGER_MOBILE`、`PASSENGER_CREDENTIAL_NO`、`PASSENGER_GENDER` 读取信息并保存到 `~/.openclaw/flight-booking/.passengers.json`。
- 以证件号去重：同一证件号的记录会被更新而非重复添加。

**列出已保存的乘客身份**：当用户未提供乘机人信息（环境变量未设置）而需要创建订单、退票等操作时，应先调用 `list-passengers` 查看已保存的身份列表：

```bash
python3 {baseDir}/scripts/flight_booking.py list-passengers
```

- 输出：JSON 格式的乘客列表，每条包含序号、姓名、性别、脱敏证件号、脱敏手机号、保存时间。
- 输出中的 `_raw` 字段包含完整未脱敏的信息，供 Agent 读取后通过 `export` 设置环境变量。
- 向用户展示时使用脱敏信息（如 `张三，证件号 310101****1234`），用户选择后从 `_raw` 取完整值设置环境变量。

**使用流程**：

1. 用户需要订票但未设置乘机人信息 → Agent 调用 `list-passengers` 查看是否有已保存的身份
2. 若有已保存身份 → **必须**向用户展示列表（脱敏），让用户明确选择一个，**不得自动选取或默认使用任何一个身份**
3. 用户明确选择后（如「用第1个」「用张三」） → Agent 从 `_raw` 读取完整信息，通过 `export` 设置环境变量，然后继续执行操作
4. 若无已保存身份或用户需要使用新身份 → 引导用户提供信息，设置环境变量后调用 `save-passenger` 保存
5. **即使只有一个已保存的乘客身份，也必须向用户确认是否使用该身份，不得自动填充**

## 何时向用户确认

- 出发/到达城市不明确（如只说「北京」需确认首都/大兴）。
- 日期不明确或存在多义。
- 用户未指定航班号或舱位时，不得替用户选择。
- 若未配置乘机人环境变量（姓名、手机、证件号、性别），先调用 `list-passengers` 查看是否有已保存的身份，**必须向用户展示列表并等待用户明确选择**，不得自动将已保存身份填充到环境变量中；即使只有一条记录也需用户确认。若无已保存身份，再提示用户提供信息。
- **退票同样需要乘机人姓名和证件号**（`PASSENGER_NAME`、`PASSENGER_CREDENTIAL_NO`），若未配置则提示用户先设置后再执行退票。
- **创建订单成功后、执行 pay-issue 出票前**：必须向用户展示订单号、航程与金额，并等待用户明确确认（如「确认出票」「去支付」）后再执行出票，不得下单与出票连续自动执行。
- **退票前**：必须先通过 `refund-order --dry-run` 查询退改签规则，将退票手续费各时段金额以表格展示给用户，告知当前所处时间段和对应手续费，在用户了解费用并确认后再执行退票。
- **改签前**：必须先查询退改签规则并向用户展示改签手续费，确认原订单号、目标航班号和舱位，确认后再执行改签。改签需先执行 `search` 查询目标航班。
- **用户未提供订单号时**：先调用 `list-orders` 查看缓存订单列表，根据上下文匹配后向用户确认目标订单。
