IA agentique de réservation de voyage pour TMC et OTA
Nous avons construit un agent autonome de réservation de voyage dont le flux de contrôle est du Python déterministe. Le modèle ne fait qu'analyser la demande de voyage et formuler la réponse. Chaque étape de réservation enregistre une action compensatoire, de sorte que lorsqu'un tarif hôtelier expire après l'émission du billet d'avion, l'agent annule le billet dans la fenêtre de 24 heures et remet au voyageur des alternatives honnêtes au lieu de le laisser coincé.
100%
État terminal cohérent, par construction
Lot synthétique de 200 scénarios à graine fixe, contre 65% pour la référence LLM-in-control
0 / 0
Voyageurs coincés, réservations fabriquées exposées
Même lot, contre 40 et 30 pour la référence
$3.25
Dépense moyenne de recherche GDS par réservation
contre $7.57 en référence ; le GDS facture par recherche, Lufthansa a relevé ses frais le 1er janvier 2026
Tous les scénarios sont synthétiques sur un GDS et un CRS simulés ; les codes aéroport et les noms d'hôtels sont des fixtures de forme réelle, pas de l'inventaire live ni de vraies réservations.
Aucune n'est un échec de QI du modèle. Les deux sont déjà une classe de bugs 2026.
L'agent émet un billet d'avion, l'étape hôtel échoue, et sans logique de compensation il dit encore « Tout est en ordre. » Quelqu'un se retrouve avec un vol et pas de chambre. Un modèle parfait laisse encore un voyageur coincé si rien n'annule le billet.
Il invente un hôtel plausible et le réserve. L'établissement n'a jamais été dans le CRS. Rien n'a vérifié avant d'atteindre le voyageur, parce que dans une boucle de raisonnement le modèle est à la fois le proposant et le juge.
La raison pour laquelle un meilleur modèle ne corrige pas cela, c'est que les défaillances sont des événements d'infrastructure et d'usage, indépendants de la qualité du modèle. Un tarif expire entre deux appels d'API. Un hold est rejeté après l'émission d'un billet. Une tempête de recherches brûle la marge. Le raisonnement stochastique s'y compose : dix étapes à 90 percent de fiabilité donnent environ 34 percent de bout en bout, et GPT-4 avec ReAct termine de vrais itinéraires multi-jours à 0.6 percent (TravelPlanner, OSU NLP, arXiv 2402.01622). On ne s'extrait pas d'une défaillance stochastique cumulative par du prompt.
Et le déployeur assume chaque déclaration que l'agent fait. Dans Moffatt v. Air Canada (BC Civil Resolution Tribunal, 14 février 2024), la compagnie aérienne a été condamnée à payer $812.02 après que son chatbot a inventé une politique de tarifs de deuil, et l'argument selon lequel l'IA était une entité distincte a été rejeté.
Le flux de contrôle est une machine à états Python construite à la main, d'environ dix nœuds. Le LLM est confiné à deux tâches feuilles. Tout ce qui est entre les deux est déterministe.
input → extract (LLM leaf) → search → policy gate → verify gate → hold → ticket → hotel-book → commit
Le modèle analyse l'intention en langage naturel en un objet typé Pydantic TripRequest (origin, destination, date, passengers, cabin, hotel). Cet objet typé est le seul artefact structuré que le LLM produit. Il est interchangeable de fournisseur via Pydantic AI et s'exécute entièrement hors ligne avec un stub déterministe lorsqu'aucune clé n'est présente.
La politique d'entreprise vit sous forme de prédicats Python purs : économie uniquement, un plafond tarifaire de $600 par segment, des transporteurs préférés (United, American, Delta), un plafond hôtelier de $350 par nuit. Les options hors politique sont physiquement non présentables parce qu'elles sont filtrées avant de pouvoir être montrées, pas signalées après. Les familles tarifaires inconnues échouent en sécurité, traitées comme au-dessus de la politique plutôt que silencieusement comme de l'économie. Sans vol conforme à la politique, l'agent escalade vers une file humaine plutôt que de bluffer.
Chaque hôtel est confirmé contre le CRS par property_id. Un établissement que le modèle invente n'est simplement pas dans le CRS, il est donc refusé et jamais exposé, et la réservation atteint l'état terminal abstained. La barrière refuse l'inventaire non confirmé ; elle ne demande pas au modèle de noter sa propre sortie.
Chaque étape aller enregistre son action inverse au moment de l'exécution. L'émission du billet, par exemple, enregistre « annuler le billet, fenêtre de 24 heures ». Sur un échec à l'étape N, les compensations de N-1 jusqu'à 1 s'exécutent en ordre inverse, et seulement alors l'agent rend compte. C'est ce qui sépare une démo d'un produit, parce que c'est ce qui empêche un échec partiel de devenir un client coincé.
Un compteur en direct suit les dépenses de recherche GDS à $3.25 par segment, parce que les recherches sont facturées, pas seulement les réservations. Un cache L2B et une recherche différée le maintiennent plat là où un agent spéculatif re-recherche et brûle la marge. Chaque réservation écrit un journal d'événements JSON en append-only, exportable sous le nom audit-<pnr>.json, portant le modèle et la version, la demande de voyage typée, chaque verdict de nœud, chaque compensation Saga, le drapeau de divulgation EU AI Act Article 50, et l'état terminal.
Quatre boutons, côte à côte avec une vraie référence LLM-in-control sur le même scénario. Chaque capture ci-dessous est prise depuis l'application en cours d'exécution.
« ORD to SFO next Tuesday, one night downtown, company policy. » La machine à états exécute chaque nœud, confirme le Hyatt Regency SF contre le CRS par property_id, et le compteur de recherche reste à $3.25 sur une recherche en cache. État terminal : confirmed, avec un PNR.
La demande nommait un établissement fabriqué, « Tabacon Springs Eco-Lodge », un nom qui mélange deux vrais resorts et n'a pas de property_id par conception. La barrière ne trouve aucune correspondance CRS et refuse de l'exposer. L'agent s'abstient honnêtement, « Je n'ai pas pu confirmer cet établissement », au lieu d'en inventer un.
Le tarif hôtelier expire après que le vol est déjà émis. De notre côté, la Saga se déclenche : annuler le billet dans la fenêtre de 24 heures, libérer les holds, et répondre honnêtement que le billet a été annulé sans frais avec des alternatives jointes. État terminal : rolled back, voyageur en sécurité. La référence, dans le même scénario, laisse le billet émis, n'offre aucune compensation, et émet un faux « Tout est en ordre », ce qui est exactement le précédent Air Canada en train d'arriver.
Un clic exporte audit-<pnr>.json: le modèle et la version, la demande de voyage typée, chaque nœud et son verdict déterministe, chaque compensation Saga, le drapeau de divulgation EU AI Act Article 50 (les obligations de transparence s'appliquent à partir du 2 août 2026), et l'état terminal.
Les mêmes 200 réservations synthétiques, une graine fixe (42), et les mêmes défaillances d'infrastructure injectées passent dans les deux architectures. Le mix de scénarios est 50 percent happy, 20 percent hotel-fail-after-ticket, 15 percent hallucinated-entity, et 15 percent search-storm. Nos garanties tiennent par construction ; les défaillances de la référence émergent des mêmes données.
La référence est un vrai agent LLM-in-control de style ReAct exécuté sur les mêmes scénarios, un ancrage honnête plutôt qu'un épouvantail. Les chiffres ci-dessous portent sur le lot synthétique de 200 scénarios à graine fixe (benchmark.py, seed 42, n=200).
| Métrique | Agent déterministe (le nôtre) | Référence (LLM-in-control) |
|---|---|---|
| État terminal cohérent | 100.0% | 65.0% |
| Voyageurs coincés | 0 | 40 |
| Réservations fabriquées exposées | 0 | 30 |
| Dépense moyenne de recherche GDS par réservation | $3.25 | $7.57 |
Les 100 percent, 0 et 0 tiennent par construction sur ce lot synthétique à graine fixe, non comme une garantie de production en monde ouvert. L'affirmation est étroite et durable : une réservation partielle n'est jamais montrée comme confirmed, et un voyageur n'est jamais coincé. L'écart $3.25 contre $7.57 est un chiffre de marge qui tient à n'importe quelle qualité de modèle.
La garantie ne vient pas de la confiance dans le modèle. Dans cette démo, le flux de contrôle est du Python déterministe, et chaque étape aller enregistre une action compensatoire dès qu'elle s'exécute. Quand une étape échoue après l'émission d'un billet, le moteur exécute ces compensations en inverse (une Saga), annule le billet dans la fenêtre de 24 heures, et rend compte honnêtement. Un voyageur n'est jamais laissé avec un vol et pas de chambre, parce que rien ne dépend du modèle qui déciderait de nettoyer.
Non. Le GDS, le CRS, l'émission des billets et le paiement sont tous stubbés et simulés dans cette démo. L'adaptateur de fixtures est l'intégration V1, et il n'y a pas de compte Amadeus, Sabre ou Duffel live derrière. La démo prouve l'architecture du flux de contrôle et la logique de compensation, pas un pipeline de réservation de production.
C'est exactement le cas pour lequel la Saga est conçue. L'émission du billet enregistre sa propre action inverse (annuler le billet dans la fenêtre de 24 heures) au moment où elle s'exécute. Si le tarif hôtelier expire avant le commit, le moteur déclenche les compensations en ordre inverse, annule le billet sans frais, libère les holds, et remet au voyageur des alternatives honnêtes. L'état terminal est rolled back, pas confirmed et pas stranded.
Une barrière de vérification confirme chaque établissement contre le CRS par son property_id avant qu'il puisse être montré. Quand la demande nommait un établissement fabriqué dans notre démo, la barrière n'a trouvé aucune correspondance CRS et a refusé de l'exposer, et l'agent s'est abstenu honnêtement au lieu de le réserver. La barrière ne signale pas un hôtel inventé après coup ; elle le rend physiquement non présentable.
Un agent de style ReAct met le LLM aux commandes de la transaction, donc il décide quand chercher, réserver et émettre, et il n'a ni barrière ni logique de compensation. Sur le même lot synthétique de 200 scénarios à graine fixe, cette référence a exposé de l'inventaire fabriqué et laissé des billets émis sans rollback, en émettant un faux « Tout est en ordre ». Ici le LLM est un nœud feuille typé qui n'analyse que l'intention et formule la réponse ; le code déterministe possède le flux et chaque étape porte son propre undo.
Le déployeur assume chaque déclaration que son agent fait. Dans Moffatt v. Air Canada (BC Civil Resolution Tribunal, 14 février 2024), la compagnie aérienne a été condamnée à payer $812.02 après que son chatbot a inventé une politique de tarifs de deuil, et la défense « c'était l'IA » a été rejetée. La démo exporte une piste d'audit JSON par réservation avec le modèle et la version, chaque verdict de nœud, chaque compensation Saga, et un drapeau de divulgation EU AI Act Article 50, de sorte que ce que l'agent a fait est inspectable après coup.
La recherche derrière cette démo — l'architecture, la conception de la vérification, et le plan directeur d'entreprise.
Solution complète
Explorer la solution IA agentique de réservation de voyage →Les défaillances qui laissent les voyageurs coincés et inventent des hôtels sont des événements d'infrastructure, pas des problèmes de QI du modèle.
Si votre équipe pèse où placer le LLM dans un agent de réservation à enjeux élevés, et comment empêcher un échec partiel de devenir une responsabilité de type Air Canada, nous aimerions vraiment mettre nos notes en commun. Le problème est à l'échelle de l'industrie et les réponses le seront aussi.