
我建了 AI 想在航空公司机组恢复上击败求解器。它输了,而那场败局变成了产品。
我为了赢而建的基准,却输掉了
我构建了 StormCrew 的第一个版本,目标是击败求解器。这就是我脑子里的全部卖点。航空公司运行控制依赖几十年历史的优化引擎,所以如果我能训练出更聪明的东西,就有一个值得讲述的故事。我花了好几周。然后我把恢复引擎拿去对标 CBC,一款在我还不会写 for 循环之前就久经考验的成熟开源混合整数求解器,结果 CBC 赢了。不是四舍五入误差那种赢。
我记得盯着两列数字,那种特定的空虚感——你设计实验是为了证明自己是对的,结果却证明自己错了。求解器更快。它的方案更便宜。它从未交回一个不可行的排班。我那套聪明的版本三项全输。
于是我做了唯一想得出的诚实之举。我改了主张,而不是改数字。
我建 AI 是为了击败求解器。求解器赢了。有意思的部分,原来是那场对决所掩盖的一切。
这一翻转才是 StormCrew 真正成为什么的脊梁,而且我认为它比我起初打算讲的故事更有用。你可以自己跑完整流程:veriprajna.com/zh-Hans/demos/airline-crew-scheduling-ai,但让我走一遍是什么改变了我的想法,因为转向本身才是重点。
风暴让枢纽停飞时,真正崩溃的是什么?
在 CBC 让我认输之后,我回头读了那些崩溃复盘,几乎没有哪次失败是「数学略微次优」。不正常运行——业内称为 IROPS,给航空公司造成约 每年 $60B(IATA)。标志性灾难是 2022 年 12 月的 Southwest,损失约 $1.2B,约 16,900 次取消,约 200 万名乘客滞留。当我追溯那些日子究竟如何瓦解时,优化器从来不是反派。
真正崩溃的是三件事。恢复 太慢:风暴让枢纽停飞时,下游连锁的重新配组仍主要是一场 4 到 12 小时的人工抢修(有出处的基准,不是我编的数字)。它 风险太高:每一次重新配组都必须遵守 FAA Part 117 的执勤与休息限制以及各航司的 工会 CBA,一次违规就是合规事件,不是脚注。而且它 太不透明:刚刚失去机组的下游航班连锁,直到那些航班已经开始取消才看得见。
最后这一点正是遗留工具错过的,也是我让演示首先展示的。在最繁忙的枢纽注入一场风暴,应用会标出 冲击半径:停飞航班,加上因轮转而失去机组的一跳下游航班。在种子场景中,全网有 53 个航班 面临风险。

自 DOT 自动退款规则(2024 年 10 月) 起,每一次超过 3 小时的连锁延误如今也是自动财务打击。于是,慢、违法或盲目的代价恰恰在工具不变时上升了。这三种失败,没有哪一种能靠更好的目标函数修好。我一直在优化那件本来就没问题的事。
我为何不再试图击败 CBC,而开始喂给它?
我与输给 CBC 和解的方式,是给它一份不同的工作。不再与求解器竞争,而是把它包起来。整条管线都是真实、确定性、种子化的代码:合成航线网与机组状态、按轮转图可达性计算冲击半径的扰动注入器、执勤生成器,然后是 CBC 作为引擎 来挑选方案,再与「什么都不做」做影子对比,最后是签名证书。
在种子风暴上运行时,生成器产生 1,762 条合法恢复执勤(其中 52 条是把机组送到需要处的调机定位),外加 53 条取消回退,合计 1,815 个候选列。CBC 求解由此得到的 1,815 变量、115 约束 最小费用集合划分至 OPTIMAL,并在约 0.11 秒 内返回方案。该场景结果:53 个航班中 52 个重新配组(98%),1 次取消,使用 34 个机组(25 个正班、9 个备用)。

航司的问题从来不是求解器太弱,而是恢复太慢、风险太高,而且直到太迟才看得见。
请注意那张截图的诚实。恢复引擎就是 CBC,面板上写着名字。 经得起检验的主张不是我的代码胜过求解器,而是方案在远不到一秒内到达——而有出处的人工流程要花 4 到 12 小时,应用把这差距公开量出来。我想把范围说清楚,因为这是演示,我拒绝把它洗成超出本分的东西:那些精确数字是一个种子合成网络的结果,不是开放世界保证。能传开的是 速度对比人工 这一主张。
合法性保证属于代码,不属于模型的判断
我有一个只有亲手构建才换来的强硬看法,让我直说。合法性保证不能活在模型的判断里。它必须活在确定性代码中,按构造成立。 让非法机组执勤永远不被推荐的方法,不是训练模型去回避,也不是给目标加惩罚项然后指望优化器绕开。而是让非法执勤从一开始就无法生成。
因此约束在生成时强制执行,而不是事后打分。Part 117 将执勤期上限设为 780 分钟,飞行时间上限 480 分钟,并要求最短过站 30 分钟;示例 CBA 将一次执勤上限设为 4 个航段。只有全部满足的执勤才会成为候选列。这就是动作掩码。非法指派不是被惩罚,而是 不可表示。无论 CBC 拿着交给它的列做什么,无论可选副驾驶事后怎么评说方案,两者都无法让非法执勤复活——因为它从未进入集合。
这给我的是不变量,而不是分数:0 次非法指派,永远,有单元测试(测试套件 3/3 通过,检查冲击半径非空、只生成合法列、恢复方案是合法划分)。分数可以回退。不变量你可以承诺。

这也是为什么,当安全故事是「模型学会了不去做」时,我不再觉得「自主 AI 运维」卖点有说服力。在这次构建中,我恰好有一次早期信任模型去遵守硬规则,它一直还好,直到那一个它不好的输入。在单次违规就是监管事件的领域,「通常合法」等于「不合法」。我宁愿删掉可能性,也不愿去监督它。
一年中最糟的那天会发生什么?
我差点发出一个会自动批准一切的版本,幸好一个场景拦住了我。把演示切换到 severe,此时事件严重到备用机组耗尽,大约只剩 30% 的机组仍在。CBC 仍找到完全合法的方案,约 0.05 秒,仍是 0 非法。但该方案会取消 53 个航班中的 20 个,即半径的 38%,远高于 OCC 的 15% 自动批准阈值。
正确做法不是默默盖章一个取消超过三分之一受影响网络的方案。于是状态翻转为 ESCALATE,需要人工签核,并显示原因。方案仍被算出、仍合法、仍展示给管制员(33 个航班恢复,半径的 62%)。只是不会自动批准。

多数「自主」卖点跳过的部分是:知道何时正确动作是不行动,把这一天交给人。
建了那道门之后,我对整个品类的感觉变了。升级不是系统失败。它是系统对糟糕一天的诚实。 一个总是给出自信答案的顾问容易演示、却危险去信任。偶尔说「这个超出你的线,你来定」的那个,才是我会真的放在凌晨 3 点管制员旁边的。
如果我是管制员,我想要的那份工件
我不断问自己,运行管制员第二天早上需要什么,答案不是仪表盘,而是一份记录。于是每一次恢复都封存进一份签名的 recovery_plan.json:扰动、按动作选定的方案(含机组与航班)、每个动作对照上限核验的具体 Part 117 与 CBA 条款、恢复挂钟时间,以及节省数字。这是 OCC 关于 为什么 推荐这次恢复的审计记录,一键可导出。

还有一个可选的方案 副驾驶,我想说清楚它处在什么位置。它是连到 LLM 的薄适配器(默认 Claude,可换提供商,或无密钥本地桥),用通俗英语解释方案。它 没有密钥时完全弃权,其余一切离线、无密钥运行,并且它位于 决策核心之外。确定性掩码、CBC 与升级门决定。模型只在事后叙述。我故意把它放在那里,因为一旦语言模型影响某个执勤是否合法,我就丢掉了整个构建过程挣来的保证。
关于节省,同样的纪律适用。在正常场景下,影子对比显示 避免了 52 次取消,以及约 避免了 $2.37M 的 DOT 退款敞口(按每名乘客 $300 的模型)相对什么都不做。这是尽可能讨好的框定,因为基线是让整个冲击半径滞留,并且标注为对该单一场景的示意。它不是标题,也绝不是我的代码胜过任何东西的证据。我第一天就把那个论点输给了 CBC。我不会用营销数字悄悄赢回来。
那么,「增强,而不是取代」究竟是什么意思?
我曾经以为增强是胆怯的选择——你建不出大胆之物时才说的话。现在我想的正相反。这里的买家已经拥有不错的求解器栈,Jeppesen 或 IBS,无法容忍推倒重来、供应商锁定,或在一年中最糟那天拿到无法解释的推荐。对那个买家说「扔掉它,换我更聪明的模型」并不大胆——那是我已用基准对自己证伪的主张。
我能诚实提供的,是他们已信任的求解器外围的那层运营层。让连锁在咬人之前可见。让非法动作无法生成,而不只是被劝阻。把小时压缩成秒。并知道何时这一天糟到正确答案是升级,而不是自动批准。演示里的一切都是合成与种子化的——网络、机组、扰动、美元数字,没有任何真实航司数据。真实的是机制,你可以端到端观看它运行:veriprajna.com/zh-Hans/demos/airline-crew-scheduling-ai。
如果你更想看它跑,而不是听我描述,这里就是端到端运行的完整流程。
自 CBC 击败我以来,这个问题我一直翻来覆去想。当你要竞争的求解器已经够好,而买家已经拥有它时,剩下要建的不是更好的答案,而是与答案更好的关系:更快、可证明合法、可见,并谦逊到会升级。那么,今年卖给你的 AI,有多少真正在解难的部分,又有多少在重新解那从未坏掉的部分?


