
Ik bouwde een AI om de solver te verslaan bij airline crew recovery. Hij verloor, en dat verlies werd het product.
De benchmark die ik bouwde om te winnen, en verloor
Ik bouwde de eerste versie van StormCrew om de solver te verslaan. Dat was het hele pitch in mijn hoofd. Airline operations control draait op tientallen jaren oude optimalisatie-engines, dus als ik iets slimmers kon trainen, had ik een verhaal dat de moeite waard was. Ik besteedde er weken aan. Toen benchmarkte ik mijn recovery-engine tegen CBC, een volwassen open-source mixed-integer solver die al in de praktijk beproefd was voordat ik een for-loop kon schrijven, en CBC won. Niet met een afrondingsfout.
Ik herinner me hoe ik naar de twee kolommen met cijfers staarde en dat specifieke holle gevoel voelde dat je krijgt wanneer het experiment dat je ontwierp om jezelf gelijk te bewijzen, in plaats daarvan bewijst dat je ongelijk hebt. De solver was sneller. Zijn plannen waren goedkoper. Hij gaf nooit eens een onhaalbaar schema terug. Mijn slimme versie verloor op alle drie.
Dus deed ik het enige eerlijke dat ik kon bedenken. Ik veranderde de claim, niet de cijfers.
Ik bouwde de AI om de solver te verslaan. De solver won. Het interessante bleek alles te zijn wat die strijd verborg.
Die omkering is de ruggengraat van wat StormCrew uiteindelijk werd, en ik denk dat het een nuttiger verhaal is dan het verhaal dat ik wilde vertellen. Je kunt het hele ding zelf draaien op veriprajna.com/nl/demos/stormcrew-irops-crewherstel-voor-luchtvaart-in-seconden-legaal-vanuit-de-constructie, maar laat me doorlopen wat mijn mening veranderde, omdat de pivot het punt is.
Wat breekt er echt wanneer een storm een hub aan de grond houdt?
Ik ging terug en las de meltdown-postmortems nadat CBC me had vernederd, en bijna geen van de falen was "de wiskunde was iets suboptimaal." Irregular operations, wat de industrie IROPS noemt, kosten airlines ongeveer $60B per jaar (IATA). De canonieke ramp, Southwest in december 2022, liep uit op ongeveer $1.2B, met rond de 16,900 annuleringen en ongeveer 2 miljoen gestrande passagiers. Toen ik naging hoe die dagen echt ontrafelen, was de optimizer nooit de schurk.
Drie dingen gaan in plaats daarvan mis. Recovery is te traag: wanneer een storm een hub aan de grond houdt, is het opnieuw bemannen van de cascade stroomafwaarts nog grotendeels een handmatige scramble van 4 tot 12 uur (een onderbouwde benchmark, geen getal dat ik verzon). Het is te riskant: elke herbemanning moet FAA Part 117 dienst- en rustlimieten respecteren en een per-carrier vakbonds-CBA, en één enkele overtreding is een compliance-incident, geen voetnoot. En het is te ondoorzichtig: de cascade van stroomafwaartse vluchten die zojuist hun crew verloren, is onzichtbaar totdat die vluchten al annulleren.
Dat laatste is wat de legacy-tools missen, en het is het eerste dat ik de demo liet tonen. Injecteer een storm bij de drukste hub en de app markeert de schokradius: de gegrondzette vluchten plus de one-hop stroomafwaartse vluchten die hun crew verliezen via de rotatie. In het seeded scenario staan 53 vluchten op het spel over het netwerk.

Sinds de DOT auto-refund rule (okt. 2024) is elke cascadevertraging van 3 uur of meer nu ook een automatische financiële klap. Dus de kosten van traag, illegaal of blind zijn precies omhooggegaan terwijl de tooling hetzelfde bleef. Geen van die drie falen wordt opgelost door een betere objectieffunctie. Ik had precies het enige geoptimaliseerd dat al prima was.
Waarom stopte ik met proberen CBC te verslaan en begon ik hem te voeden?
Ik maakte vrede met mijn nederlaag tegen CBC door hem een andere baan te geven. In plaats van met de solver te concurreren, wikkelde ik hem in. De pipeline is allemaal echte, deterministische, seeded code: een synthetisch airline-netwerk en crew-state, een verstoring-injector die de schokradius berekent via graph reachability over de rotatie, een duty-generator, dan CBC als de engine die het plan kiest, dan een shadow-compare tegen niets doen, dan een ondertekend certificaat.
Wanneer ik het op de seeded storm draai, produceert de generator 1,762 legale recovery-diensten (52 daarvan deadhead-repositioneringen om crew te verplaatsen waar ze nodig zijn), plus 53 annulerings-fallbacks, voor 1,815 kandidaatkolommen in totaal. CBC lost de resulterende 1,815-variabele, 115-constraint minimum-cost set-partition op tot OPTIMAL en retourneert een plan in ongeveer 0.11 seconden. Resultaat op dat scenario: 52 van 53 vluchten opnieuw bemand (98 procent), 1 annulering, 34 crews gebruikt (25 line en 9 reserve).

Het probleem van de airline was nooit dat de solver te zwak was. Het was dat de recovery te traag, te riskant en onzichtbaar was tot het te laat was.
Let op de eerlijkheid van die screenshot. De recovery-engine is CBC, met naam op het paneel. De duurzame claim is niet dat mijn code een solver out-optimaliseert. Het is dat het plan ruim binnen een seconde aankomt waar het onderbouwde handmatige proces 4 tot 12 uur kost, en de app meet die kloof openlijk. Ik wil precies zijn over de scope, want dit is een demo en ik weiger hem tot meer te maken dan hij is: die exacte cijfers zijn resultaten van één seeded synthetisch netwerk, geen open-world-garantie. De snelheid-versus-handmatig-claim is degene die reist.
De legaliteitsgarantie hoort in code, niet in het oordeel van een model
Ik heb een sterke mening die ik alleen verdiende door dit te bouwen, dus laat me het botweg stellen. Een legaliteitsgarantie kan niet in het oordeel van een model leven. Die moet in deterministische code leven, by construction. De manier waarop je voorkomt dat een illegale crew-duty ooit wordt aanbevolen, is niet door een model te trainen om die te vermijden, en niet door een penalty-term aan het objectief toe te voegen en te hopen dat de optimizer eromheen stuurt. Het is door de illegale duty onmogelijk te maken om überhaupt te genereren.
Dus de constraints worden afgedwongen op generatietijd, niet achteraf gescoord. Part 117 begrenst een duty-periode op 780 minuten, vliegtijd op 480 minuten, en vereist een minimale sit van 30 minuten; de voorbeeld-CBA begrenst een duty op 4 segmenten. Alleen duties die aan al die eisen voldoen, worden ooit kandidaatkolommen. Dit is action masking. Een illegale toewijzing wordt niet bestraft, die is niet-representeerbaar. Wat CBC ook doet met de kolommen die hij krijgt, en wat de optionele copilot later ook over het plan zegt, geen van beide kan een illegale duty weer tot leven wekken, omdat die nooit in de set zat.
Dat geeft me een invariant in plaats van een score: 0 illegale toewijzingen, ooit, unit-getest (de testsuite is 3/3 geslaagd, en controleert dat de schokradius niet leeg is, dat alleen legale kolommen worden gegenereerd, en dat het herstelde plan een legale partitie is). Een score kun je laten regresseren. Een invariant kun je beloven.

Dit is ook waarom ik "autonomous AI ops"-pitches niet meer overtuigend vind wanneer het veiligheidsverhaal is "het model heeft geleerd om het niet te doen." Ik vertrouwde tijdens deze build precies één keer een model om een harde regel te respecteren, vroeg, en het ging goed tot de ene input waar het niet ging. In een domein waar één enkele overtreding een regulatory event is, is "meestal legaal" hetzelfde als "niet legaal." Ik verwijder liever de mogelijkheid dan die te bewaken.
Wat gebeurt er op de slechtste dag van het jaar?
Ik had bijna een versie verscheept die alles automatisch zou goedkeuren, en ik ben blij dat een scenario me stopte. Zet de demo op severe, waar de gebeurtenis zo erg is dat reserves uitgeput zijn en slechts ongeveer 30 procent van de crews overblijft. CBC vindt nog steeds een volledig legaal plan, in ongeveer 0.05 seconden, nog steeds 0 illegal. Maar dat plan zou 20 van 53 vluchten annuleren, wat 38 procent van de radius is, ruim boven de 15 procent auto-approve-drempel van de OCC.
De juiste zet daar is niet stilzwijgend een plan afstempelen dat meer dan een derde van het getroffen netwerk annuleert. Dus slaat de status om naar ESCALATE, human sign-off required, met de reden getoond. Het plan wordt nog steeds berekend, is nog steeds legaal, wordt nog steeds getoond aan de controller (33 vluchten hersteld, 62 procent van de radius). Het wordt alleen niet automatisch goedgekeurd.

Het deel dat de meeste "autonome" pitches overslaan is weten wanneer de juiste actie is om niet te handelen, en de dag aan een mens over te laten.
Het bouwen van die gate veranderde hoe ik over de hele categorie denk. Escalatie is niet het systeem dat faalt. Het is het systeem dat eerlijk is over een slechte dag. Een adviseur die altijd een zelfverzekerd antwoord teruggeeft, is makkelijk te demonstreren en gevaarlijk om te vertrouwen. Degene die af en toe zegt "deze zit boven je lijn, jij beslist" is degene die ik daadwerkelijk om 3 uur 's nachts naast een controller zou zetten.
Het artefact dat ik zou willen als ik de controller was
Ik bleef mezelf afvragen wat een operations controller de ochtend erna nodig zou hebben, en het antwoord was geen dashboard, het was een dossier. Dus verzegelde elke recovery tot een ondertekende recovery_plan.json: de verstoring, het gekozen plan actie voor actie met crew en vlucht, de specifieke Part 117- en CBA-clausule per actie gecheckt tegen zijn plafond, de recovery-wall-clock, en de besparingscijfers. Het is het auditdossier van de OCC van waarom dit herstel werd aanbevolen, in één klik te exporteren.

Er is ook een optionele plan copilot, en ik wil duidelijk zijn over waar die zit. Het is een dunne adapter naar een LLM (standaard Claude, provider-verwisselbaar, of een keyless lokale bridge) die het plan in gewoon Engels uitlegt. Hij onthoudt zich volledig zonder een key, al het andere draait offline en keyless, en hij leeft buiten de beslissingskern. Het deterministische mask, CBC en de escalatiegate beslissen. Het model narrateert alleen achteraf. Ik zette hem daar expres, want het moment dat het taalmodel beïnvloedt of een duty legaal is, heb ik de garantie verloren die ik de hele build heb verdiend.
Over de besparingen geldt dezelfde discipline. Op het normale scenario toont de shadow-compare 52 vermeden annuleringen en ongeveer $2.37M vermeden DOT-refund-exposure (een model van $300 per passagier) versus niets doen. Dat is de meest flatterende framing mogelijk, omdat de baseline is de hele schokradius te stranden, en het is gelabeld als illustratief voor dat ene scenario. Het is geen headline, en het is zeker geen bewijs dat mijn code iets out-optimaliseert. Ik verloor dat argument aan CBC op dag één. Ik ga het niet stilletjes terugwinnen in een marketingcijfer.
Dus wat betekent "augment, don't replace" eigenlijk?
Ik dacht vroeger dat augmentation de timide keuze was, het ding dat je zegt wanneer je het gedurfde niet kunt bouwen. Nu denk ik het omgekeerde. De koper hier bezit al een goede solver-stack, Jeppesen of IBS, en verdraagt geen rip-and-replace, lock-in, of een onverklaarde aanbeveling op de slechtste dag van hun jaar. Tegen die koper zeggen "gooi het weg voor mijn slimmere model" is niet gedurfd, het is een claim die ik al voor mezelf heb ontkracht met een benchmark.
Wat ik eerlijk kan bieden is de operationele laag rond de solver die ze al vertrouwen. Maak de cascade zichtbaar voordat die bijt. Maak illegale zetten onmogelijk te genereren in plaats van slechts ontmoedigd. Klap uren samen tot seconden. En weet wanneer de dag slecht genoeg is dat het juiste antwoord escaleren is, niet auto-approve. Alles in de demo is synthetisch en seeded, het netwerk, de crews, de verstoring, de dollarcijfers, nergens echte airline-data. Wat echt is, is het mechanisme, en je kunt het van begin tot eind zien draaien op veriprajna.com/nl/demos/stormcrew-irops-crewherstel-voor-luchtvaart-in-seconden-legaal-vanuit-de-constructie.
En als je het liever ziet dan me het te horen beschrijven, hier is het hele ding van begin tot eind.
Hier is de vraag die ik niet meer loslaat sinds CBC me versloeg. Wanneer de solver waarmee je concurreert al goed is, en de koper hem al bezit, is wat er nog te bouwen is geen beter antwoord. Het is een betere relatie met het antwoord: sneller, aantoonbaar legaal, zichtbaar, en nederig genoeg om te escaleren. Dus hoeveel van de AI die je dit jaar wordt verkocht lost daadwerkelijk het harde deel op, en hoeveel lost opnieuw het deel op dat nooit kapot was?


