关于 rihla
Rihla 是面向多段行程的航班搜索命令行工具和 MCP 服务,支持灵活日期与机场选择。它为各航段的日期和机场选项报价,再按停留时长约束排列有效组合,并提供离线模拟数据。
主要功能
- 01搜索多段航班行程
- 02灵活选择机场和日期
- 03跨航段应用停留时长约束
- 04按价格排列有效组合
- 05标明票价来源及参考性质
- 06对未报价航段返回部分结果
适用场景
- 01比较多站行程的机场组合
- 02在灵活出发日期内寻找低价路线
- 03使用模拟票价离线探索行程
- 04检查票价覆盖不完整的路线
MCP 相关
stdio MCP 服务提供两个只读工具:`resolve_airports` 用于验证和补充建议的机场代码,`search_trip` 用于搜索有报价的行程。工具说明要求 Agent 在使用有配额限制的搜索前确认机场。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx rihla-mcpTRAVELPAYOUTS_TOKEN密钥Travelpayouts Data API token (free) - the primary price source. Without any keys the server runs on offline mock data.SERPAPI_KEY密钥SerpApi key (optional, 250 free searches/mo) - fills routes Travelpayouts misses with bookable Google Flights fares.RIHLA_PROFILESource profile: 'local' (all configured sources), 'hosted' (redistribution-licensed only), or 'mock' (offline).本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Rihla
Rihla(رحلة),意为 “旅程”。 名称取自 Ibn Battuta 在 14 世纪撰写的游记 — 这位走遍最远之地的探险家留下了历史记录。他从 Tangier 出发前往 Mecca, 一路远行了 29 年,行程达 75,000 英里。
灵活的多程、多机场航班搜索,可在完整行程范围内找到最便宜的路线 — 提供 CLI 和 MCP 服务,供 AI Agent 使用。
面向消费者的航班网站按单程定价。Rihla 则将整个旅程视为一个整体优化问题:
“9 月 15 日左右从 Montevideo — 或者从 Buenos Aires 出发,那里坐渡轮就能到 — 前往 Europe。停留 20–30 天,再去 Japan 待 15 天,然后回家。怎样组合最便宜?”
这个查询包含三程,每程两端都有可选机场、灵活的出发日期窗口,以及相互关联的停留时长限制。Rihla 会为每程的日期×机场网格各定价一次,然后完全通过本地计算找出最便宜的有效组合 — 因此,即使是包含 3–4 程的灵活查询,也只需少量 API 调用。
1. 1,297 USD (44 days door-to-door)
MVD -> AMS 2026-09-08 $ 383 [mock, cached] AA AA127
FRA -> NRT 2026-10-07 $ 481 [mock, cached] KL KL411
HND -> EZE 2026-10-22 $ 433 [mock, cached] AZ AZ933
2. 1,308 USD (44 days door-to-door)
MVD -> AMS 2026-09-14 $ 446 [mock, cached] AA AA127
...
请留意机场替换的效果:经 Amsterdam 入境 Europe,从 Frankfurt 离境,飞回 Buenos Aires — 每程都会从各自的机场集合中独立选出最便宜的机场组合。
快速开始
需要 Python ≥ 3.10。
pip install rihla # CLI
pip install "rihla[mcp]" # CLI + MCP server
开箱即可离线运行(无需密钥 — 使用确定性的模拟数据源):
RIHLA_PROFILE=mock rihla # the canonical demo trip above
rihla examples/queries/canonical.json # same trip, from a query file
rihla -i # build a query interactively
如需实时价格,请将 .env.example 复制为 .env,并至少添加一个
Travelpayouts 令牌(免费)。请参阅
数据源。
编写查询
查询是一个简短的 JSON 文件:包含出发机场、有序停靠点、第一程的出发日期窗口,以及在每个停靠点的停留时长。后续日期窗口会自动推导 — 无需手动计算“如果我 9 月 15 日出发并停留 20–30 天,什么时候飞往 Tokyo?”
{
"origins": ["MVD", "EZE", "AEP"],
"stops": ["EUROPE", ["NRT", "HND"], "MVD_AREA"],
"earliest": "2026-09-08",
"latest": "2026-09-22",
"stays": [[20, 30], [15, 15]],
"date_step": 3,
"top": 5
}
origins— 作为一个出发机场集合尝试的 IATA 代码;每个日期选出最便宜的机场。stops— 每个停靠点可以是区域名称或 IATA 代码列表。内置区域:EUROPE(MAD、BCN、LIS、CDG、FCO、AMS、FRA)、TOKYO(NRT、HND)、MVD_AREA(MVD、EZE、AEP)。将出发机场集合设为最后一个停靠点,即可飞回家。earliest/latest— 仅第一程的出发日期窗口。stays— 在每个中间停靠点停留的夜数[min, max](len(stays) == len(stops) - 1)。date_step— 在日期窗口内每隔 N 天采样一次(间隔越大,API 调用越少)。top— 返回的排序组合数量。currency— 可选的 ISO 4217 代码(默认USD)。
rihla -i 会引导你回答这些问题,并输出相应 JSON,方便保存以供复用。运行时添加 --links 可显示预订链接。
解读结果
Rihla 如实呈现数据质量,不会假装所有航班都能预订:
- 每个航班都会标注数据来源;如果价格仅供参考,则会标记为
cached(Travelpayouts 数据是汇总的搜索历史,并非实时票价);真实且可预订的票价则来自 SerpApi / Google Flights。 - 如果某些航段无法定价(冷门航线确实存在,缓存数据源也会有缺口),你会得到仅针对已找到航段的部分结果,绝不会悄悄给出错误的总价。
- 对于无法定价的航段,Rihla 会显示你的出发日期窗口之外最近的缓存票价 — 提示你调整或扩大日期范围。
v0.1 仅按价格排序。默认允许在一个区域内采用不同城市进出的开口行程(从一个城市进入 Europe,从另一个城市离开);不会计算区域内调位的成本。
数据源与配置文件
| 来源 | 角色 | 费用 | 备注 |
|---|---|---|---|
| Travelpayouts / Aviasales | 主要 | 免费 | 缓存数据,获得再分发许可。每次调用可获取一个月的价格,因此调用预算很低。价格仅供参考;覆盖范围取决于航线的热门程度。 |
| SerpApi(Google Flights) | 补充 | 每月免费搜索 250 次(自带密钥) | 可实际预订的票价;可补充 Travelpayouts 未覆盖的航线。仅用于未覆盖的航线。 |
| Mock | 离线 | — | 用于开发和演示的确定性虚假价格。 |
通过 .env(参见 .env.example)或环境变量配置:
TRAVELPAYOUTS_TOKEN、SERPAPI_KEY 和 RIHLA_PROFILE:
local(默认)——使用所有已设置密钥的数据源,包括 SerpApi。hosted——仅使用获得再分发许可的数据源(禁用 SerpApi:它会抓取 Google, 因此不要在公开托管的实例中提供它)。mock——强制使用离线获取器,不访问网络。
如果未设置密钥,Rihla 会回退到 Mock 数据源,并说明这一点。
MCP 服务
Rihla 附带一个 MCP 服务(stdio),让 Claude 等 Agent 可以执行行程搜索:
pip install "rihla[mcp]"
claude mcp add rihla -- rihla-mcp # Claude Code
或在任意 MCP 客户端配置中:
{
"mcpServers": {
"rihla": {
"command": "rihla-mcp",
"env": {
"TRAVELPAYOUTS_TOKEN": "your-token",
"SERPAPI_KEY": "your-key"
}
}
}
}
(服务还会从其工作目录加载 .env,因此如果你从代码检出目录运行它,env 是可选的。)
它提供两个只读工具,并强制遵守以下规范:
resolve_airports——Agent 根据旅行者提到的地点提出 IATA 代码;Rihla 会验证并补充信息(包括附近的替代机场)。成本低且不消耗配额。search_trip——定价搜索,会消耗有限配额。工具说明要求 Agent 在消耗配额前,先获得旅行者对机场的明确确认。
mcp-name: io.github.leojg/rihla
工作原理
各航段的价格相互独立——MVD→欧洲的票价不受东京日期的影响。因此,Rihla 会一次性获取每个航段的日期×机场网格,然后完全在内存中枚举符合条件的日期组合(同时遵守停留时间限制)。组合爆炸发生在本地计算中,而非 API 调用中。
core.py data model + the pure optimizer (no I/O)
fetchers/ PriceFetcher protocol: Mock / Travelpayouts / SerpApi + merge
places.py airport sets / regions
api.py search_trip: serializable query in, result dict out
cli.py thin CLI over search_trip
mcp_server.py thin MCP wrapper over the same seam
添加数据源(Duffel、Kiwi 等)只需再实现一个类,该类实现一个仅含一个方法的协议:quote(origin, dest, day) -> Quote。
**范围(v0.1):**仅搜索航班——不包含住宿、预订或支付;仅限一名成人、一个舱位。搜索会返回预订链接,但不会处理交易。
开发
git clone https://github.com/leojg/rihla && cd rihla
pip install -e ".[dev,mcp]"
pytest # offline, no keys needed
ruff check .
许可证
依据 Apache License 2.0 许可——请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。