Install
openclaw skills install @zhangxchao/super-train火车票智能中转方案推荐助手。查火车票/高铁票/动车票/12306余票,智能拼接中转换乘方案,学习用户偏好(坐席策略、时间偏好、中转约束)。火车票查询/余票/车次/预订/中转换乘/坐席偏好。
openclaw skills install @zhangxchao/super-train基于 flyai search-train 命令,实现火车票中转方案的智能拼接与个性化推荐。
用户提到火车票、高铁票、动车票、车次查询、余票、中转换乘、12306、坐席偏好等关键词时触发。
flyai-cli 已安装:npm i -g @fly-ai/flyai-cliflyai search-train --origin "北京" --destination "上海" 应返回 JSON该工具无需任何 API 密钥即可进行试用。为获得更好的效果,可以访问 https://flyai.open.fliggy.com/ 获取 API 密钥:
flyai config set FLYAI_API_KEY "your-key"
若执行 flyai search-train 时报错提示方法找不到,请更新 CLI 版本:
npm i -g @fly-ai/flyai-cli
详细参数说明请参阅 flyai CLI 命令参考,包含:
super-train/
├── SKILL.md ← 本文件
└── assets/
├── preferences.json ← 用户显式设定的偏好(硬约束)
└── history.json ← 历史查询与购票记录(追加写入)
查询前按优先级读取以下文件(文件不存在时跳过,不报错):
1a. 读取 preferences.json(硬约束)
配置字段与 flyai search-train 参数名完全一致,非空字段直接传递。
1b. 检查 history.json 中的常用路线
扫描最近 50 条记录,若当前 origin→destination 与历史高频路线匹配,提示用户:"你上次走这条路线是 [日期],当时选了 [transfer_city/seat_class_name],是否沿用?"
冲突处理:当临时指令与已有偏好冲突时,提示用户并提供选项:
所有用户输入在拼接到 flyai search-train 命令前,必须通过以下校验,防止 shell 注入攻击:
| 参数 | 合法字符集 | 最大长度 | 示例 |
|---|---|---|---|
| 城市名(origin/destination/transfer-city) | 中文汉字、英文字母、空格、中文括号 | 20字符 | 北京、乌鲁木齐、哈尔滨西 |
| 日期(dep-date) | 数字、连字符,须匹配 YYYY-MM-DD | 10字符 | 2026-04-05 |
| 小时(dep-hour-start/end) | 0–23 的整数+h 后缀 | 3字符 | 8h、17h |
| 车次号(transport-no) | 大写字母+数字,多个以英文逗号分隔 | 200字符 | G71,D3965 |
| 坐席名称(seat-class-name) | 中文汉字 | 10字符 | 硬卧、二等座 |
| journey-type | 仅允许 1 或 2 | 1字符 | 1 |
` $ ( ) { } ; & | \ < > ! ' " 换行符--origin "北京"assets/preferences.json 和 assets/history.json 两个文件执行查询前必须确保以下四个参数齐全,缺一不可:
| 参数 | 说明 | 缺失时的引导策略 |
|---|---|---|
| 出发城市 | --origin | 结合上下文推断(如用户常驻地),无法推断则直接询问 |
| 到达城市 | --destination | 直接询问 |
| 出发日期 | --dep-date | 识别自然语言日期("明天""下周五""五一"),无法识别则询问。需转换为 YYYY-MM-DD 格式 |
| 出发时间段 | --dep-hour-start/end | 识别自然语言时间段(“早上8点”“下午”“晚上”),转换为小时区间(如“早上8点” → --dep-hour-start 7h --dep-hour-end 9h)。未指定时使用 preferences.json 中的默认值 |
收集流程:
用户输入 → 解析已有参数
↓
四个必填参数是否齐全?(出发城市、到达城市、出发日期、出发时间段)
├─ 齐全 → 进入查询策略
└─ 缺失 → 结合上下文智能引导补全
- 一次性询问所有缺失参数,避免多轮追问
- 尽量从上下文/对话历史/用户偏好中推断
- 日期需转换为 YYYY-MM-DD 格式
- 时间段需转换为小时区间(如未指定,使用 preferences.json 中的 dep_hour_start/end 默认值)
引导示例:
用户:我想去上海
回复:好的,帮您查去上海的火车 🚄 请问:
1. 从哪个城市出发?
2. 哪天出发?几点出发?(如"明天上午9点")
时间段确认规则: 当用户未指定出发时间段时:
dep_hour_start 和 dep_hour_end 作为默认时段特殊处理:
Step 1: 查询直达方案
flyai search-train --origin {出发地} --destination {目的地} --dep-date {日期} --dep-hour-start {起始小时}h --dep-hour-end {结束小时}h --journey-type 1
↓
Step 2: 若直达无票/不满足偏好 → 进入中转流程
2a. 先询问用户坐席偏好(必须)
示例:"中转方案车程较长,请问坐席有什么偏好?"
若 preferences.json 已有 seat_class_name,提示确认即可
2b. 执行中转查询(❗中转接口不支持 --seat-class-name)
flyai search-train --origin {出发地} --destination {目的地} --dep-date {日期} -dep-hour-start {起始小时}h --dep-hour-end {结束小时}h --journey-type 2
2c. 若用户偏好卧铺 → 额外查询长途段(必须)
将所有方案中**耗时最长段**的车次号合并,一次性查询:
flyai search-train --origin {长段出发站} --destination {长段到达站} --dep-date {日期} --journey-type 1 --transport-no {车次1,车次2,...} --seat-class-name "硬卧"
• 默认查硬卧,仅用户明确指定时才查软卧
• 有票 → 更新方案坐席(如 `硬卧 + 硬座`);无票 → 标注仅支持座位
↓
Step 3: 若用户指定中转城市
flyai search-train ... --transfer-city {中转城市}
↓
Step 4: 方案评估与推荐
出发时间 + 行程时长 若覆盖 23:00–06:00 时段,则认定为过夜车硬卧 + 硬座、软卧 + 二等座 均算作符合"卧铺"偏好的方案第一段坐席 + 第二段坐席,例如:硬卧 + 硬座、二等座 + 二等座车次号 + 出发日期 + 坐席 组合作为方案唯一标识对查询结果按以下维度进行方案间相对对比(非绝对分数):
| 维度 | 权重 | 对比规则 |
|---|---|---|
| 总时长 | 40% | 含各段行程耗时 + 中转等待时间;以所有方案中最短耗时为基准,其他方案按超出比例降权;中转等待 30-90min 为宜,过短/过长降权 |
| 舒适度 | 40% | 坐席与用户偏好的匹配程度;中转次数越少越优;夜间行程(23:00-06:00)降权 |
| 价格 | 20% | 以所有方案中最低价为基准,其他方案按溢价比例降权 |
根据用户偏好动态调整权重,如"最快" → 时间权重 80%。
用户做出选择后(无论购买还是放弃),记录到 assets/history.json:
{
"ts": "2026-04-03T10:30:00+08:00",
"origin": "北京",
"destination": "桂林",
"transfer_city": "长沙",
"transport_no": "G71+D3965",
"seat_class_name": "二等座+二等座",
"dep_date": "2026-04-03",
"dep_time": "08:00",
"transfer_wait_minutes": 38,
"result": "purchased",
"note": null
}
注意:result 字段必填,abandoned 记录和 purchased 同等重要;保留已有记录,仅新增。
展示单个方案详情时使用:
| 行程段 | 区间 | 车次 | 出发 | 到达 | 耗时 | 坐席 |
|--------|------|------|------|------|------|------|
| 第一段 | 北京南→长沙南 | G71 | 08:00 | 11:12 | 3h12m | 二等座 |
| 🔄 中转 | 长沙南站内换乘 | — | — | — | 等待38min | — |
| 第二段 | 长沙南→桂林北 | D3965 | 11:50 | 16:05 | 4h15m | 二等座 |
💡 推荐理由:全程有座,中转时间充足,适合老人出行
展示多个方案对比时使用:
| 方案 | 车次 | 出发→到达 | 中转 | 总时长 | 票价 | 推荐 |
|------|------|-----------|------|--------|------|------|
| A ⭐ | G71+D3965 | 08:00→16:05 | 经长沙·38min | 7h27m | ¥553起 | 舒适度最佳 [预订]({jumpUrl}) |
| B | G525+G1545 | 09:15→16:05 | 经武汉·25min | 6h50m | ¥620起 | 时间最优 [预订]({jumpUrl}) |
| C | Z285 | 20:00→08:00+1 | 直达 | 12h | ¥450起 | 价格最低 [预订]({jumpUrl}) |
flyai search-train 接口返回值中提取,禁止自行计算或编造jumpUrl,展示预订入口:[点击预订]({jumpUrl})🔔 未开售 · 抢票,并提示开售时间(如已知)。示例:🔔 未开售(预计1月5日开售)· [设置抢票提醒]({jumpUrl})用户:明天从北京去桂林,带老人
回复:
好的,为您查询北京到桂林的火车方案 🚄
[执行查询,展示方案卡片]
注意到您带老人出行,已优先推荐有座方案。
以后都优先推荐适合老人的舒适方案吗?
- ✅ 是,记住偏好
- ❌ 否,仅这次
用户:给我最快的
系统检测:最快方案为无座,违反"绝不无座"偏好
回复:
最快方案是 G123(无座,4h),但您有"绝不无座"的偏好。
符合偏好的最快方案是 G456(二等座,4.5h)。
您选择:
- 🚀 选 G123,这次破例
- 💺 选 G456,维持偏好
- 🔧 修改偏好设置
用户选择方案
↓
提取偏好特征(坐席、时段、中转策略等)
↓
临时记录(仅本次对话有效)
↓
询问:"是否将此偏好设为默认?"
├─ 是 → 持久化到 assets/preferences.json
└─ 否 → 对话结束后自动清除
硬规则识别:当用户使用"绝不""绝对不""永远不要"等触发词时,直接保存为硬约束,无需确认。