Gemma 4 读取供应商 API:为旅行社构建最便宜机票日历

用本地 Gemma 4 自动生成 13 家供应商 API 映射、Chronos-Bolt 补全空白日期,构建机票最低价日历。
文章介绍了一个已上线的机票「最低价日历」项目:代理在搜索前即可看到每个日期的最低票价,从而减少对 GDS 和多家供应商的高频查询、降低 look-to-book 成本。系统用本地 Gemma 4 自动为 13 种供应商响应格式生成解析映射,成功映射 7 家,其余手写;用 Chronos-Bolt base 结合星期基线补全未被搜索的日期。回测显示覆盖率从 36% 提升到 41%,估算日期误差从 18.7% 降至 16.2%,但作者也诚实报告了集成模型在某个子集上反而更差的反例。项目强调「模型提出方案、代码用真实数据验证」的模式,以及数据敏感性如何驱动了本地化、无 GPU 依赖的技术栈选择。
旅行代理的日常工作中有一个反复出现的场景:客户在电话里问「周二比周三便宜吗?」代理只能一次次发搜索请求,每次请求都会向 13 家供应商同时发起查询。慢,而且消耗供应商配额——GDS 的 look-to-book 比率是实打实的成本。
开发者 adi-ray 用两个开源模型解决了这个问题:Gemma 4 负责解析 13 种不同格式的供应商 API 响应,Chronos-Bolt 负责填补没有搜索记录的日期,最终产出一个「哪天最便宜」的机票日历。项目在 Hacktoberfest Open-Source AI Challenge Week 2(Side Hustle 主题)中提交,已部署到真实生产环境,服务 557 条航线和 23,626 个已定价日期。

问题:每次搜索都在烧配额
这是一个 B2B 机票预订门户,子代理可以登录并跨 13 家供应商搜索和预订——包括 GDS、低成本航司、NDC 航司和固定出发日期的批发商。
看代理接电话时的操作就能发现痛点:问一个日期,搜一次;再问一个日期,又搜一次。每次搜索都扇出到所有供应商,对客户来说慢,对平台来说贵。
Fare Calendar 在代理搜索之前就回答「哪天最便宜」,它是一个日期选择器,在每个日期下方显示最低票价。信息分两类:带实心圆点的「seen」表示过去 3 天内代理实际获取的真实票价;带虚线下划线的「est.」表示模型估算,附带可能的范围。
整个系统完全基于门户自己的服务器日志构建,反映的是自家供应商返回给自家代理的票价,而非公开元搜索的结果。
GDS(Global Distribution System,全球分销系统) 是航空、酒店、租车等旅游产品的中介分销网络,Amadeus、Sabre、Travelport 是三大主要玩家。旅行社和在线预订平台通过 GDS 聚合查询多家供应商的库存和价格。
look-to-book 比率 是 GDS 商业模式的核心计量方式:供应商按「查询次数」(look)而非仅按「成交订单」(book)向分销方收费,因为每次查询都要消耗供应商的实时库存计算资源。业内典型 look-to-book 比率可达数百比一,即几百次搜索才产生一笔预订。这意味着代理在电话中反复试探日期,每一次都会真实地产生供应商成本。Fare Calendar 试图降低这种「探索性搜索」的频率,正是直接针对这一成本结构。
核心思路:让 Gemma 4 读取供应商 API
票价藏在 13 种不同的响应格式里:GDS 的 SOAP XML、Navitaire XML、IATA NDC XML,以及各种 JSON。在截止日期前手写 13 个解析器并不现实。
开发者的做法是写了一个微小的映射语言,同时适用于 JSON 和 XML,然后让 Gemma 4 来编写映射。Gemma 只看到某个供应商响应的一个精简样本。
关键在于:决定映射好坏的是代码,不是 Gemma。代码会把映射应用到 6 个真实响应上,除非满足以下条件,否则直接拒绝:大多数响应能产出数据行;票价看起来像每位成人的总价,而不是 ID 或基础票价;代理实际搜索的航线能出现。
失败反馈会以具体形式返回给 Gemma,比如:'fareComponentList' 在 …originDestinationOptionList[*] 下不存在;那里的键是 ['fareComponentGroupList']。你是不是想用 'fareComponentGroupList'?
结果是:Gemma(本地 gemma4,7.5B Q4,通过 Ollama 运行在 4 GB 笔记本 GPU 上)成功映射了 7 家供应商,每家尝试 1–6 次。IndiGo、SpiceJet 和一家 NDC 航司尝试 11 次未收敛,于是手写完成并标注为手写。
Navitaire XML 是 Navitaire(Amadeus 旗下)为低成本航空公司提供的预订系统响应格式,其结构与传统 GDS 的 SOAP XML 差异很大,字段命名和嵌套层级都自成体系。NDC(New Distribution Capability) 是 IATA 推动的 XML 标准,旨在让航司绕过 GDS 传统链路,直接向代理提供带附加服务(行李、选座)的丰富报价。SOAP 是较早期的基于 XML 的 Web 服务协议,响应通常包裹多层信封结构。
这几种格式并存,意味着同一个「最低票价」字段在不同供应商的响应里可能叫 fareComponent、totalFare、amount 或藏在 originDestinationOptionList 的不同深度。手写 13 个解析器不仅耗时,而且每当供应商微调 schema 就要维护。让模型生成映射,本质上是把「理解陌生 schema」这一重复劳动自动化,但用代码验证兜底。
让 7.5B 小模型可靠工作的几个经验
把一个小模型用于生产数据处理,踩过的坑比想象中多。
关掉思考模式。 Gemma 4 在 Ollama 中默认开启思考模式。在开发者的 GPU 上,这会把 5 秒的回答变成 58 秒,而且答案更差。实测:think=True 时 58 秒、248 tokens、答案错误;think=False 时 5 秒、9 tokens、答案正确。
留意上下文窗口。 Ollama 的默认上下文会静默截断长提示的开头,而指令恰好在那里。早期所有失败看起来都像「Gemma 忽略了规范」,实际上是它根本没看到。
反馈要指明修复方式。 「检查你的路径」只会让错误重复出现;「使用 ^^.segment.from」才能得到修复后的映射。
让语言向模型妥协。 Gemma 总是从文档根写字段路径,所以提取器现在会自动解析这些路径到行自身的祖先节点。
这种「模型提出方案,代码用真实数据验证」的模式,是让一个 7.5B 模型值得信任、能接触生产数据的关键。
Chronos-Bolt 填补空白日期
代理只搜索少数几个日期,其余的需要补全。补全分三步:先用一个提前期模型把「哪天飞」和「提前多久搜」分开,因为越临近出发票价越高;然后用 Chronos-Bolt base(开放权重、零样本、CPU 运行)预测每个未搜索区间的日历,缺失日期作为空隙传入而非编造;预测结果与该星期几的基线取平均——两者犯不同的错误,平均后优于任何一方。
估算值会按今天的提前期重新定价,并与该日期的历史搜索混合。
Chronos-Bolt 是亚马逊发布的开放权重时间序列基础模型,基于 T5 架构,将数值序列分词后以类似语言模型的方式预测未来值。所谓零样本,指它无需针对特定航线或数据集微调,直接对输入的历史序列做预测,因此可以在 CPU 上运行而不必为每条航线训练专用模型。
提前期(lead time) 指从搜索当刻到出发日期之间的天数。机票定价对提前期高度敏感——同一航班在出发前三天和提前三个月搜索,价格可能相差数倍。文章提到的「提前期模型」就是先把这种系统性涨价趋势剥离出来,让 Chronos-Bolt 只负责预测「剩余波动」,避免模型把「临近出发就贵」这一简单规律误当成日历模式。这与传统时间序列中的去趋势处理思路一致。
数据清洗占了一半工作量
真实数据远比想象中脏。一家批发商会把某些日期标为 ₹100(座位待确认),在「最低票价」日历中这意味着它每天都胜出,曾把回测误差推到 308%。现在低于 ₹500 以及偏离航线中位数 0.25×–4× 范围的票价会被丢弃并计数。
有些承运商对中转行程按航段定价,所以 DEL→HYD→BOM 的搜索可能伪装成便宜的「DEL-HYD 票价」,系统只保留实际搜索的航线。还有些供应商把「2 名成人」作为一个总价,因此只使用单人成人搜索(占总流量 85%)。
回测结果(诚实版本)
回测使用 14 个截止日(10 月 1–14 日),日历仅从更早的日志构建,与实际代理在随后两天获得的最低票价对比,覆盖 11,544 个搜索日期。
| 仅 seen 票价 | 星期基线 | Chronos-Bolt base + 基线 | |
|---|---|---|---|
| 显示票价的搜索日期 | 36% | 41% | 41% |
| 显示真实近期票价时的误差 | 1.0% | 1.0% | 1.0% |
| 估算日期的误差(591 个) | n/a | 18.7% | 16.2% |
在 5 个截止日、498 个估算日期上选择预测器时:星期基线 18.4%,Chronos-Bolt small 单独 20.1%,Chronos-Bolt base 单独 17.6%,small + 基线 17.9%,base + 基线 15.1%。
日历的价值主要是覆盖率:它给代理后续会搜索的 41% 日期提供了数字,高于 36%,而「seen」票价是准确的。估算虽然粗糙,但可测量地优于显而易见的基线——首次部署用的 Chronos-Bolt small 反而不如「同一星期几、最近几周」的简单基线。
一个诚实的警告:在另一个仅含 63 个估算日期的 5 天子集上,集成模型反而略差(20.3% vs 19.0%)。这是一个月的嘈杂、混合 GDS 和固定出发日期的历史数据,回测会随日志积累持续重跑。
为什么开源模型是唯一可行路径
数据不能离开。 供应商票价是谈判价,供应商响应是商业机密。把它们粘贴到托管 LLM 里「帮忙写解析器」根本不可行。Gemma 4 在本地运行,每个供应商响应都留在笔记本上。
每次使用零成本。 映射一个供应商是 4 GB 笔记本 GPU 上的一次性工作。557 条航线的预测在 CPU 上几分钟完成。部署的 API 不需要 GPU,也不需要 AI 供应商。
能改模型行为。 关掉思考、设置上下文大小、强制 JSON 输出,都是可控开关。闭源 API 只会让人猜测为什么答案退化。
换模型是一行代码的改动。 当 Chronos-Bolt small 输给基线时,开发者当天下午就试了一个大 4 倍的开放模型,在真实数据上回测了 5 个变体,零成本上线胜出者。
无锁定。 映射是纯 JSON,预测器可配置(FARECAL_FORECASTER、FARECAL_MODEL_ID),Gemma 可被任何兼容 OpenAI 的本地服务器替换。
部署上,Chronos 在日志所在的机器运行,写出一份 565 KB 的 forecasts.json;Render 上的 API 用 FastAPI 加标准库,不带 PyTorch。一个蓝图产出两个服务:公开演示(合成数据,免费计划)和私有服务(Starter 加持久磁盘,夜间上传预测,登录页后用签名会话 Cookie 保护,门户可通过 API key 嵌入)。
Ollama 是一个本地大模型运行与管理的开源工具,支持在消费级 GPU 上以量化格式(如 Q4,即 4 比特量化)加载模型,并提供兼容 OpenAI 的 HTTP 接口。Q4 量化把 7.5B 参数压缩到约 4 GB 显存即可运行,代价是精度略有损失,但对结构化映射这类任务仍可用。
「兼容 OpenAI 的本地服务器」意味着应用代码只需改 base URL 和模型名,就能在 Ollama、vLLM、llama.cpp 等后端之间切换。这正是文章强调的无锁定:映射结果是纯 JSON,预测器通过环境变量配置,模型可替换,因此整个系统的每一层都不依赖某一家云服务商的持续可用性或定价。对于必须本地处理商业机密数据的团队,这种可替换性是架构层面的硬性要求,而非偏好。
对 AI 应用开发的启示
这个项目不是「用 AI 做了个玩具」,而是一个能直接说明开源模型在生产环境中真实定位的案例。
核心模式是「模型提出、代码验证」:Gemma 生成映射,代码用真实响应验证并拒绝不合格结果。模型从不接触运行时的票价数据。这解决了小模型不可信的问题,也让 7.5B 参数在 4 GB 显存下完成实用工作。
另一个值得关注的点是数据敏感性驱动了架构选择。供应商谈判价不能离开本地,这直接决定了整个技术栈——本地 Gemma、本地 Chronos、CPU 服务、无 GPU 依赖。对于处理商业机密数据的团队,这是一个可复用的参考架构。
最后,回测结果本身也值得学习:开发者主动报告了集成模型在某个子集上表现更差的反例,而不是只展示最好看的数字。这种诚实度在 AI 应用宣传中并不常见。
项目代码在 GitHub 开源(adi-ray/fare-calendar),包含 FastAPI、pandas、无依赖 Web Component 和 60 个测试。真实日志、票价和私有供应商映射已被 git 忽略,仓库只包含合成数据和公开的 Amadeus 映射。
相关推荐

WorkBuddy保姆级教程:半小时跑通自动化工作流
WorkBuddy 自动化工作流保姆级拆解:为什么大多数人只把它用成聊天软件,27 个支点课程与 12 个模板如何帮助新手半小时跑通落地实操,附使用边界提醒。

Codex云端模式详解:环境配置与适用场景
本文详细解析Codex云端模式的工作原理、环境配置步骤及适用场景,包括如何创建环境、连接GitHub仓库、设置定时任务,并对比云端与本地模式的差异,帮助开发者高效利用Codex。

Claude Code免费使用真相:三条路与一个骗局
Claude Code 免费使用是否可行?本文拆解 Pro、Max 的真实成本(含汇率与税费)、社区免费方案的技术原理与隐私代价,以及假安装包、钓鱼页面等安全陷阱,并给出访客通行证、开源贡献者计划和本地模型三条合法免费路径。