Skip to content

Cet article aborde les limites du client et de la plateforme qui sous-tendent l’expérience utilisateur et les interactions avec Ask DoorDash : comment nous sommes passés des carrousels de hackathon à ce modèle, comment un seul élément sert trois lecteurs, et comment le contexte et l’état restent cohérents à mesure que les consommateurs naviguent sur DoorDash. Il fait suite à « Créer l’assistant DoorDash : aperçu technique » et nos analyses approfondies Développement d’Ask DoorDash (2e partie) : l’intelligence, Développement d’Ask DoorDash (3e partie) : Évaluation, et Développement d’Ask DoorDash (4e partie) : une plateforme pour créer et faire évoluer des agents.


Ask DoorDash transforme des demandes telles que « Prépare-moi un dîner à 60 $ pour 10 personnes », « Aide-moi à faire des tacos au poulet » ou encore « Des en-cas pour la semaine » en listes de courses modifiables. L’application génère une liste comprenant des produits disponibles à l’achat, leurs prix, des photos, des contrôles de quantité, ainsi que des boutons simples permettant de remplacer n’importe quel article et de passer commande. Pour les demandes liées aux restaurants, le système renvoie des établissements et des plats qui s’intègrent directement dans le flux d’achat natif. Ces deux fonctionnalités s’exécutent sur le même client et la même infrastructure de plateforme. Cet article se concentre sur le cas d’utilisation des courses alimentaires, car c’est celui qui met le plus à l’épreuve toutes les complexités majeures liées à la création d’une interface intuitive pour les achats pilotés par l’utilisateur. Les listes de courses contiennent généralement dix à vingt articles, font l’objet de plusieurs cycles de révision et doivent facilement tenir compte des contraintes des consommateurs, telles que le budget et les restrictions alimentaires.

Les sessions utilisateur de juillet 2026 ont montré que, lors des sessions consacrées à l’épicerie où une liste de courses était affichée, les consommateurs effectuaient en moyenne près de deux interactions avec l’interface utilisateur chacun. Environ un tiers de ces sessions aboutissaient à l’ajout de la liste au panier. L’assistant saisit l’intention générale des consommateurs et la synthétise. Le résultat se présente sous forme de composants natifs, qui permettent aux consommateurs d’affiner leur sélection de manière précise. Les modifications simples mettent immédiatement à jour un artefact de référence, tandis que les changements nécessitant un raisonnement déclenchent un nouveau tour de l’agent.

Afin d’offrir une expérience d’achat collaborative efficace entre le consommateur et l’agent, ces derniers doivent s’intégrer à une interface interactive s’appuyant sur des données commerciales en temps réel et sur le contexte actuel de l’application utilisée par le consommateur. Lorsqu’un consommateur effectue des achats dans une application, il s’attend à voir d’un seul coup d’œil des articles réels qu’il peut acheter. Le texte peut décrire cet environnement, mais ce sont les composants interactifs basés sur l’image qui permettent aux consommateurs de s’y déplacer concrètement. Le contexte a également son importance. Les consommateurs qui ouvrent DoorDash ont des attentes différentes de celles qu’ils ont vis-à-vis d’une application de chat polyvalente. Ils font leurs achats en parcourant les boutiques et les pages, plutôt qu’en décrivant ce qu’ils souhaitent. Les rencontrer là où ils se trouvent est une pièce essentielle du puzzle.

Les grandes choses ont souvent des débuts modestes

L’assistant d’épicerie de DoorDash a vu le jour dans le cadre d’un hackathon. Nous savions dès le départ que l’intégration des composants existants de DoorDash serait un élément clé de tout ce que nous allions développer. Les premières versions s’appuyaient largement sur des carrousels : d’abord une liste de magasins à proximité, puis un carrousel de résultats de recherche pour chaque article recherché par l’utilisateur. Ils s’affichaient en ligne, chacun accompagné de son terme de recherche. Le résultat ressemblait essentiellement à une transcription de discussion dans laquelle des résultats commerciaux avaient été collés.

D'un point de vue fonctionnel, cela ressemblait beaucoup à ce que nous proposons aujourd'hui. En termes d'expérience utilisateur, c'était moins abouti, et il est intéressant d'en examiner les détails.

Après avoir trouvé un ensemble de magasins, l’assistant demandait lequel explorer et attendait que la réponse soit saisie. Chaque carrousel représentait un article dont le consommateur avait besoin, et le fait de choisir une option y ajoutait cet article au panier. Chacun s’affichait avec ses options étalées sur toute la largeur de l’écran, le choix restant ouvert. Ainsi, préparer ses courses pour la semaine consistait à parcourir la liste une sélection à la fois, sans possibilité d’accepter un ensemble d’articles en une seule fois.

Derrière tout cela se cachait un défaut qui se manifestait dans les deux sens : l’interface avait été conçue avant tout pour présenter le travail de l’assistant. Chaque carrousel était précédé de la requête qui l’avait généré, et les résultats s’affichaient dans l’ordre où ils avaient été récupérés. Les termes de recherche et les ensembles de résultats bruts constituent la matière première d’un agent. Présenter directement les résultats revient, dans le contexte de l’interface, à afficher une trace de pile. L’assistant s’en remettait tout aussi volontiers à l’interface, traitant les éléments un par un, car l’écran ne pouvait afficher qu’une pile de carrousels, un par élément. Chacun avait été conçu en fonction des limites de l’autre plutôt qu’en fonction de l’utilisateur.

La leçon à retenir n’était pas que la réutilisation de composants existants était une erreur ; c’est justement grâce à leur caractère familier que l’assistant donne l’impression d’être DoorDash. Nous avions simplement créé un « conteneur » inadapté pour les accueillir. C’est ce conteneur qui détermine l’ordre dans lequel ils apparaissent, ce à côté de quoi ils sont placés, et ce qu’un consommateur peut en faire. Avant tout, un utilisateur a besoin d’une interface d’achat plutôt que d’une interface conversationnelle. Nous avions conçu une interface conversationnelle qui, par hasard, contenait des composants d’achat. La réutilisation des composants nous a fourni le vocabulaire. Les assembler pour former une interface d’achat était la partie la plus difficile.

Ce qui a survécu au hackathon, c'est la base : l'expérience repose aujourd'hui sur cette même infrastructure de streaming mise en place à l'époque. En revanche, la couche de décodage qui transformait les résultats des outils en interface utilisateur n’a pas perduré. L’assistant n’affiche désormais plus du tout les résultats des outils. À la place, nous avons consacré du temps à mettre en place une architecture d’artefacts qui sépare les besoins des utilisateurs de ceux liés au stockage et aux agents. En d’autres termes : nous avons cessé de décoder les données de travail de l’agent pour les afficher et avons commencé à construire le système autour des besoins des différents composants et utilisateurs du système.

Figure 1 : La même demande d'achats, à un an d'intervalle. La version développée lors du hackathon affichait un carrousel par article, chacun étant précédé de la requête qui l'avait généré ; le choix était laissé au consommateur. Aujourd'hui, l'assistant affiche chaque liste sous forme de carte dans la conversation, les articles les plus demandés étant déjà sélectionnés.

Un objet, trois objectifs

Une liste de courses compte trois lecteurs : le service qui la stocke, l'agent qui la modifie et la personne qui fait ses courses. Chacun d'entre eux doit voir des informations différentes. C'est en les séparant que les modifications apportées par le consommateur s'affichent immédiatement et restent définitives, sans nécessiter un nouveau cycle de traitement.

Le formulaire enregistré constitue la source de référence : il s'agit d'un blob JSON conservé en tant qu'artefact dans Managed Agent Services, comme décrit dans l'article consacré à la plateforme. Il doit être complet et constitue la seule copie faisant autorité.

L’agent ne lit pas directement ce bloc de données. Une liste de courses interactive contient bien plus de détails que ce dont un modèle a besoin pour la plupart des échanges suivants. Répéter à chaque tour de conversation le nom de chaque produit, son prix, sa quantité, ses substituts et toutes les métadonnées d’affichage viendrait faire double emploi avec ce que le consommateur a réellement demandé. Ainsi, lorsqu’un agent génère un widget, ce qui reste dans le contexte de la conversation est un résumé concis : le magasin et le contenu de la liste. Lorsqu’un tour de conversation nécessite l’état exact, l’agent lit l’artefact et renvoie une vue réduite dont les métadonnées d’affichage ont été supprimées. Cette vue est sans ambiguïté et suffisamment concise pour permettre un raisonnement peu coûteux. Une liste médiane occupe entre 40 et 60 Ko au format JSON. Ce qui reste dans le contexte de la conversation est une seule ligne d’environ 240 caractères, soit environ 250 fois plus petite.

Ce que voit le consommateur est encore différent, et cela prend plusieurs formes. Dans la conversation, la liste s’affiche sous la forme d’un widget compact : le magasin, le nom de la liste, l’heure d’arrivée estimée, quelques photos d’articles et un bouton « Ajouter au panier » indiquant le montant total estimé. Cela suffit pour évaluer la liste d’un seul coup d’œil sans quitter le chat. Un bouton « Voir la liste » ouvre la vue en pleine page, qui présente la liste complète et tout ce dont on a besoin pour la parcourir en détail. Les deux vues sont conçues pour être comprises immédiatement et permettre d’agir directement.

Un détail illustre à quel point ces points de vue divergent. Lorsque l’agent choisit un produit pour un article de la liste, il conserve les autres produits correspondant à la même recherche, et le consommateur peut passer de l’un à l’autre d’un simple geste. Il conserve dix candidats par article, et 99,7 % des articles présentent suffisamment de résultats pour remplir ce quota ; ainsi, presque chaque ligne affichée au consommateur comporte une sélection et neuf alternatives. La vue du consommateur affiche chacune de ces alternatives. La vue de l'agent n'en affiche aucune, car c'est au consommateur qu'il revient de faire son choix (à moins qu'on ne le demande explicitement à l'assistant).

Figure 2 : Une liste, trois points de vue. Le consommateur voit les prix, les photos et une option d’échange derrière chaque ligne. L’agent ne voit que le magasin et le contenu de la liste. L’artefact contient toutes ces informations, y compris neuf alternatives rien que pour les feuilles de lasagnes.

Garder les pieds sur terre

Dans le cas d’un panier contenant un yaourt à la framboise, en cliquant sur « Recommander des associations pour ce panier », deux suggestions s’affichent : du muesli, « car cela se marie bien avec votre yaourt », et des myrtilles, « pour ajouter des fruits à votre yaourt » (voir figure 3). Le cas de contraste existe déjà dans notre propre produit, sur le même écran : la page du panier comporte un carrousel d’articles complémentaires qui propose des articles qu’un consommateur pourrait souhaiter ajouter, sans aucune justification. Notre agent de panier aurait très bien pu renvoyer exactement cela : une liste d’articles plausibles. Les articles auraient très bien pu être les mêmes dans les deux cas. C’est la justification qui indique au consommateur que la suggestion est raisonnée plutôt que simplement plausible, et comme chaque widget est également un lieu d’action, cette distinction a une réelle utilité.

C’est ce qu’apporte l’ancrage : la différence entre une expérience d’achat captivante et une simple nouveauté. Si le résultat fourni par l’assistant n’est pas concret, les consommateurs n’ont guère de raison de délaisser l’expérience riche que l’application DoorDash leur offre déjà. Le texte continue de jouer un rôle, mais dans un second plan. Nous avons délibérément intégré la logique de l’assistant au sein des widgets et autour de ceux-ci, au lieu d’afficher les cellules d’éléments exactement comme elles apparaissent ailleurs dans l’application.

L'ancrage doit également s'appliquer au niveau des données, et c'est là que le travail est réparti. Un agent choisit un type de contenu pris en charge et fournit les données commerciales correspondantes ; le client iOS se charge de la présentation et de l'interaction, en mappant ce contenu sur les composants natifs du système de conception de DoorDash. Une demande d'épicerie génère une liste de courses modifiable avec les quantités, les substitutions et un sous-total. Une demande de restaurant peut générer des fiches de magasin, des fiches d'articles ou des suggestions de panier.

C’est cette séparation qui assure la fiabilité des données elles-mêmes. Les informations commerciales contenues dans un widget ne proviennent pas du modèle. Les prix, les stocks, les horaires d’ouverture et le contenu du panier sont extraits des mêmes systèmes de référence que ceux utilisés par le reste de l’application ; ainsi, un prix affiché dans Ask correspond au prix indiqué sur la page de la boutique à ce moment-là. Le contenu typé établit également un contrat stable entre le backend et le client. La prise en charge d’un nouveau widget ne se résume pas à une simple modification de l’invite. L’agent doit savoir quand l’utiliser et quoi y insérer, et le client a besoin d’un schéma nommé ainsi que du code permettant de le décoder et de l’afficher. Au-delà de cela, un nouveau widget parvient au consommateur via les mêmes mécanismes de conversation et de diffusion en continu que tous les autres.

Figure 3 : Deux types de suggestions concernant le même panier. Une seule d'entre elles explique pourquoi.

Les widgets en tant que canaux d'E/S

Chaque nouveau widget que nous avons intégré a renforcé le caractère fonctionnel de l’assistant. Cela tient autant aux données d’entrée qu’aux données de sortie. Une règle régit l’ensemble : une modification déterministe reste au niveau du client et s’applique directement à l’artefact, tandis qu’une modification nécessitant un jugement déclenche un tour d’agent. Les exemples ci-dessous s’articulent autour de cette distinction.

La version du hackathon demandait au consommateur quel magasin il souhaitait, puis attendait qu’il le saisisse. Elle n’avait aucun moyen de choisir elle-même ; aujourd’hui, ce choix est automatique. De nombreuses autres questions nécessitent encore l’intervention du consommateur, et la saisie directe de texte peut s’avérer peu efficace pour recueillir les réponses. La saisie de texte est une contrainte à thread unique : le consommateur connaît peut-être toutes les réponses, mais ne peut les fournir qu’à la vitesse à laquelle il tape ou parle.

C'est là que les widgets prennent tout leur sens. Un consommateur peut avoir dix opérations à effectuer sur une liste de courses, chacune d'entre elles étant simple. Prises individuellement, elles sont insignifiantes ; mais décrites en mots, elles constituent une tâche complexe. C'est la différence entre regarder par-dessus l'épaule de quelqu'un et lui dire sur quels boutons appuyer, et appuyer soi-même sur ces boutons.

Les questions de clarification structurées constituent l’exemple le plus évident, et ce modèle est emprunté aux agents de codage : l’outil AskUserQuestion de Claude Code remplit la même fonction pour les développeurs. Nous proposons un widget qui interrompt le consommateur avec une petite série de questions (à choix multiples et à sélection multiple), lui permettant ainsi d’orienter la conversation à des moments véritablement ouverts. Ce widget est devenu un pilier central du modèle d’interaction de l’agent dédié à l’épicerie, et il est désormais adopté par l’agent dédié à la restauration afin de simplifier ce même type d’échange. Nous l’intégrons à notre plateforme de mémoire client, qui réduit considérablement les choix possibles, mais il subsiste néanmoins des lacunes dans les connaissances qui nécessitent l’intervention du consommateur. Il est inévitable de poser des questions. Mais nous pouvons le faire d’une manière qui permette au consommateur de répondre facilement et efficacement.

C’est dans la vue en liste pleine page que la collaboration entre les entrées et les sorties prend tout son sens. Un consommateur peut modifier la quantité d’un article, supprimer un article, le remplacer par l’une des alternatives que l’assistant a trouvées à côté, et ajouter la liste finale à son panier. Chacune de ces actions manipule directement l’artefact. Aucune ne nécessite que l’assistant agisse au nom du consommateur, et aucune n’entraîne d’allers-retours avec le LLM. Le cas du remplacement permet de mieux exploiter les ensembles de candidats générés lors du hackathon. Les alternatives parmi lesquelles le consommateur fait son choix sont les autres candidats que l’assistant a récupérés lors de la sélection de l’article. La liste de toutes les options disponibles pour le consommateur remplissait auparavant un carrousel. Désormais, elles se trouvent à un simple clic de la décision, au lieu d’être dispersées devant le consommateur sous la forme d’une question ouverte.

Changer de magasin est l’exception, et c’est justement cette exception qui présente un intérêt particulier. Il ne s’agit pas d’une modification de la liste existante. Cela se transforme en une demande d’assistance qui génère une nouvelle liste pour l’autre magasin. Cette interaction est également rare : elle a constitué la première action dans 0,1 % des sessions engagées, ce qui correspond à peu près à ce à quoi on pourrait s’attendre si la limite était fixée à peu près au bon endroit. Si cette limite est mal placée, soit l’utilisateur attend que l’assistant fasse quelque chose qu’il aurait pu faire d’un simple geste, soit il effectue lui-même une série de gestes alors qu’il aurait pu bénéficier du jugement de l’assistant.

L'artefact garantit la sécurité de la séparation, et les deux parties y écrivent différemment. Une modification de quantité, une suppression ou un échange empruntent la voie rapide : le client met à jour la liste sur place et la modification apparaît immédiatement. Une modification de stockage déclenche en revanche un tour d'agent, car la liste entière doit être recoupée avec un catalogue différent. Une révision d'agent n'écrase pas ce qui s'y trouvait ; elle produit une nouvelle liste dérivée de la précédente.

Il en résulte qu'une modification directe fait autorité. Avant qu'un tour de parole ne modifie la liste, l'agent consulte la liste actuelle plutôt que de se baser sur la transcription ; ainsi, si le consommateur supprime le lait d'avoine, le tour de parole suivant commence à partir d'une liste ne comportant plus de lait d'avoine.

Le comportement observé sur cette même période permet de déterminer où se situe cette limite. Immédiatement après la première requête, près des trois quarts des premières actions suivantes ont été effectuées via un composant. À partir du deuxième message, la tendance s'inverse, et les actions de suivi saisies au clavier constituent la majorité à tous les niveaux de profondeur que nous avons mesurés. Les commandes absorbent les ajustements immédiats et structurés ; le langage prend le relais lorsque le changement est plus important ou plus difficile à exprimer par un simple clic.

Figure 4 : La liste à la limite d'un tour. Le consommateur retire les pommes directement dans la vue de la liste, sans aller-retour avec le modèle. Lorsque le magasin change de tour, l'agent lit l'artefact plutôt que la transcription ; ainsi, la liste qu'il reconstitue dans le nouveau magasin ne contient toujours pas ces pommes.

Chaque révision effectuée par un assistant reste active

Lorsque l'assistant modifie une liste de courses, la version qu'il remplace reste affichée à l'écran et reste utilisable.

Ce n'était pas notre première implémentation. La première version masquait les listes obsolètes : dès qu'une révision était publiée, la liste qu'elle remplaçait ne pouvait plus être consultée ni modifiée. Une seule liste active, sans encombrement. Cela semblait être un comportement plus ordonné. En réalité, cela donnait l'impression que les révisions avaient un coût élevé. Si le fait de demander une modification risquait d'effacer une liste qui vous convenait, la solution la plus sûre était de cesser de demander, ce qui allait à l'encontre de l'intérêt même d'un processus collaboratif.

Ainsi, chaque modification apportée par un assistant génère désormais un nouvel élément dérivé du précédent, et chacun reste à l'endroit où il a été créé dans la conversation. Il suffit de remonter dans l'historique pour retrouver toutes les versions antérieures, toujours accessibles : un consommateur peut ouvrir une liste antérieure, la modifier ou l'ajouter à son panier à la place de la dernière version. Demander une modification ne coûte rien, car ce qu'il avait il y a un instant est accessible d'un simple glissement de doigt.

Toutes les listes créées par l’assistant ne deviennent pas forcément des versions visibles par le consommateur. Lorsqu’il établit une liste de propositions et conclut que le magasin ne dispose pas des articles demandés, cette liste n’est jamais affichée. Le consommateur est informé par un message écrit que certains articles manquaient en magasin et qu’une nouvelle liste est en cours de préparation. C’est l’un des rares cas où le texte remplit directement cette fonction, et c’est là la raison qui sous-tend tout le reste de cet article : l’interaction sert à simplifier, tandis que le texte sert à justifier.

Quand une liste se transforme en panier

Un assistant qui modifie directement le panier peut supprimer un article que le client souhaitait acheter ou en ajouter un qu'il n'a jamais confirmé, sans même lui demander son avis. Une seule erreur de ce genre suffit à perdre la confiance du client, car cette erreur passe inaperçue et concerne précisément l'article qu'il s'apprête à payer.

Nous proposons donc à la place une « liste de courses ». Celle-ci peut contenir des dizaines d'articles et faire l'objet de plusieurs itérations : qu'il s'agisse de supprimer un produit que le consommateur possède déjà, de modifier une quantité, de remplacer un produit ou de réadapter une partie d'une recette, aucune de ces modifications n'affecte le panier. Pour valider la liste, il faut cliquer explicitement sur « Ajouter au panier » : c'est la structure la plus simple que nous ayons trouvée pour donner au consommateur un réel contrôle sans qu'il ait à superviser chaque étape.

Il faut encore faire le rapprochement entre ces deux éléments, et la question intéressante est de savoir qui s’en charge. L’assistant analyse le contenu du panier tout en établissant la liste, mais il ne modifie pas automatiquement cette dernière pour l’aligner : si une recette nécessite deux bananes, la liste en demande deux, qu’il y en ait déjà quatre dans le panier ou non. Établir la liste en fonction de ce qui a été demandé exactement, puis la faire correspondre avec le contenu du panier lors d’une étape distincte, permet de regrouper toutes les décisions au même endroit.

Cette étape intervient au moment de la validation. Lorsque la liste et le panier se recoupent, le consommateur voit s'afficher les doublons et choisit soit de remplacer ces quantités, soit de les ajouter. C'est un petit moment, mais ce sont justement ces petits moments qui font que l'on gagne ou que l'on perd la confiance.

Prise en compte du contexte dans l’ensemble de DoorDash

Il ne suffit pas que l’assistant soit présent à un seul endroit ; il doit être accessible depuis n’importe où dans l’application. Le fait d’ouvrir l’assistant est déjà un signal fort d’intention, mais qui reste ambigu. À ce moment-là, un consommateur peut être en train de passer commande auprès de son restaurant préféré, de faire ses courses pour la semaine, de chercher une nouvelle paire d’écouteurs ou de demander de l’aide concernant une commande passée. Un consommateur qui s’est rendu sur la page d’une épicerie en particulier envoie un signal beaucoup plus précis : il est probable qu’il ait l’intention de remplir son panier dans ce magasin. C’est en transmettant ce contexte à l’assistant que celui-ci peut se concentrer sur l’intention probable au lieu de poser des questions.

Le système de portée que nous avons conçu à cet effet couvre à la fois le client et la passerelle. Le client associe à chaque requête des éléments clés du contexte de la demande : la catégorie sous laquelle l'utilisateur s'est connecté (épicerie, restaurant), le magasin qu'il consulte, ainsi que quelques identifiants associés. La passerelle utilise la catégorie pour déterminer vers quel agent acheminer la requête, et les autres éléments pour fournir aux outils les identifiants dont ils ont besoin.

Scope est délibérément conçu comme un ensemble plat de paires clé-valeur plutôt que comme un schéma typé, et c’est le client qui décide de son contenu. Les clés non reconnues sont transmises telles quelles au lieu d’être rejetées, ce qui permet à une nouvelle interface de commencer à envoyer du contexte sans attendre une modification de la passerelle. La seule exception concerne le sujet lui-même : en ajouter un nouveau implique d’indiquer à la passerelle vers quel agent elle doit l’acheminer. Ainsi, si la création d’une nouvelle interface est peu coûteuse, celle d’un nouveau domaine ne l’est pas.

Le sujet correspond également à la partie qui s’applique uniquement au premier message d’un contexte. Cela donne à l’assistant une base solide sur laquelle s’appuyer sans être trop contraignant, et c’est ce qui permet aux utilisateurs de sortir d’un contexte lorsqu’ils en font le choix explicite. Un utilisateur qui utilise l’assistant dans un restaurant peut quitter complètement le contexte du restaurant en lui demandant : « Trouve-moi une recette pour ce plat et prépare-moi une liste de courses pour que je puisse le préparer chez moi. » Cette demande a un coût, mais celui-ci se traduit par un simple détournement du parcours plutôt que par une impasse : l’agent « restaurant » doit reconnaître que la demande sort de son domaine de compétence et la renvoyer avant qu’un autre agent ne la reprenne.

Dans certains cas, les scopes peuvent également nous permettre de contourner complètement le modèle. Prenons l’exemple du point d’entrée « Repasser la commande de mon dernier panier ». On pourrait simplement envoyer cette phrase à l’assistant sous forme de message, et bien que cela fonctionne, l’ajout d’un scope nous permet d’être plus précis. Le cas « repasser la commande » est en effet déterministe ; nous pouvons donc contourner le LLM et renvoyer une liste de courses presque immédiatement. Ce que l’on obtient en retour est un widget de liste de courses classique : le consommateur peut modifier les quantités, échanger des produits ou demander à l’assistant de la réviser dans le chat, exactement comme il le ferait avec n’importe quelle autre liste. Notez que ce contournement ne fonctionne que si le magasin dispose encore de tous les articles du panier précédent ; si un article n’est pas disponible, la demande est transmise à l’agent chargé des courses, qui reconstitue la liste de la même manière que s’il s’agissait d’une demande saisie manuellement.

La gestion de la portée est également ce qui permet d'avoir une conversation unique et continue tout au long d'une session d'application, mais cela ne fonctionne que parce que la portée, l'association à un agent et l'état de la session sont maintenus séparés. Nous considérons la portée comme un contexte propre à chaque tour de parole. L’association, décrite précédemment dans cette série d’articles, permet de maintenir les tours de parole suivants avec l’agent qui a résolu le tour précédent. L’association et la transcription (la conversation proprement dite) sont toutes deux associées à la conversation plutôt qu’à la portée ; ainsi, un client passant d’un magasin à un autre peut réorienter l’association vers un nouvel agent de domaine sans perdre le fil de la conversation. Plusieurs agents se spécialisent en arrière-plan, et l’ensemble continue de se comporter comme un seul et même assistant.

Figure 5 : Une même conversation, lancée depuis deux endroits différents. La portée suit chaque requête ; ainsi, une requête effectuée depuis Safeway alimente la liste de Safeway.

Conclusion

L'intégration des agents dans les produits grand public n'en est encore qu'à ses débuts. Cependant, les fondements commencent à se dessiner clairement. Les widgets constituent l'espace où les consommateurs effectuent leurs tâches, tandis que la conversation entre le consommateur et l'agent sert à les guider. Il existe actuellement trois « lecteurs » de notre artefact « liste de courses », chacun ayant besoin d'une vue différente de celle-ci. Nous utilisons la partie visible par l’utilisateur pour ancrer le consommateur dans des produits réels et pour permettre d’effectuer des modifications courantes d’un simple clic. À mesure que les agents prennent en charge une plus grande partie du travail, nous nous attendons à ce que ce que le consommateur doit voir et ce sur quoi l’agent doit raisonner continuent d’évoluer, et la frontière entre une interface d’application et une interface de chat deviendra de plus en plus difficile à tracer.

La plateforme d’expérimentation de DoorDash gère plus de 60 000 « feature flags » répartis dans environ 623 dépôts. Nous avons identifié plus d’un millier de « feature flags » obsolètes ; en d’autres termes, la fonctionnalité qu’ils contrôlaient autrefois est désormais pleinement déployée, mais le « flag » et sa logique de branchement restent présents dans le code, où ils ne servent plus à rien. Le nettoyage manuel d’un seul de ces « feature flags » prend entre une et deux heures à un ingénieur. Avec environ 2 300 nouveaux « flags » créés chaque mois, le retard ne fait que s’accumuler.

Nous avons donc mis au point un système LLM multi-agents qui traite de bout en bout les signalements obsolètes, depuis un ticket Jira jusqu’à une pull request (PR) prête à être fusionnée, sans aucune intervention humaine, à l’exception d’un ingénieur chargé de confirmer la valeur cible et la validation de la PR. Sur les 50 indicateurs obsolètes les plus récents qu’il a traités, le système a généré des PR exploitables pour 45 d’entre eux, à un coût moyen de 4,79 $ et en 13,8 minutes par indicateur, permettant ainsi d’économiser d’innombrables heures de travail d’ingénierie. Le système que nous avons développé devait non seulement être efficace, mais sa fiabilité devait reposer sur l’exhaustivité plutôt que sur l’exactitude ; en cas d’échec de l’agent, celui-ci devait échouer en toute sécurité, sans apporter de modifications au code susceptibles de provoquer des dysfonctionnements.

Cet article explique pourquoi les outils prêts à l'emploi ne permettaient pas d'atteindre cet objectif dans notre base de code, comment nous avons conçu le système et les enseignements que nous avons tirés lors de son déploiement. Remarque : cet article a fait l'objet d'une évaluation par les pairs et a été accepté dans le cadre du volet « industrie » de la conférence ICSME 2026.

Pourquoi il vaut mieux automatiser la suppression des « feature flags » obsolètes

Les « feature flags » constituent un pilier de la mise en production moderne des logiciels. Ils nous permettent de déployer des fonctionnalités progressivement, de mener des tests A/B et d’activer des « kill switches » en cas de problème. Mais chaque « feature flag » a un cycle de vie, et une fois qu’une fonctionnalité est entièrement déployée, son « feature flag » devient obsolète. Les « feature flags » obsolètes sont souvent considérés comme une dette technique, car le temps d’un ingénieur a plus de valeur lorsqu’il est consacré au développement de nouvelles fonctionnalités plutôt qu’à leur nettoyage. 

Sa logique conditionnelle n'a plus aucune utilité, mais elle ne reste pas pour autant inoffensive :

  • Les « feature flags » obsolètes peuvent rester dans le code pendant des années. Des études antérieures ont montré qu'environ 25 % des « feature flags » persistent pendant plus de huit ans dans les projets open source ; les professionnels du secteur signalent régulièrement que leur suppression est fastidieuse et qu'elle est sans cesse reportée.
  • Un indicateur obsolète constitue une dette technique. Chaque indicateur obsolète ajoute une branche, un cas limite et un peu plus de texte à lire et à analyser, ce qui peut s’avérer particulièrement pénible en cas d’incident.
  • Les indicateurs obsolètes peuvent constituer un risque opérationnel. Un indicateur obsolète maintient actif un chemin d'exécution inactif qui peut être réactivé par accident. Dans un cas bien documenté, une société de courtage a réutilisé un indicateur obsolète lors d'un déploiement, ce qui a déclenché pour 460 millions de dollars de transactions non intentionnelles en seulement 45 minutes.

Si l'on multiplie ces dizaines de milliers de drapeaux par un taux de création mensuel à quatre chiffres, il apparaît clairement que le nettoyage manuel ne peut tout simplement pas suivre le rythme. Éliminer manuellement notre arriéré d'anciennes données nécessiterait des milliers d'heures de travail d'ingénierie qui seraient mieux employées à développer de nouvelles fonctionnalités.

Valeurs dynamiques

Chez DoorDash, les « feature flags » sont appelés « valeurs dynamiques » (DV). Ils s'appuient sur notre plateforme d'expérimentation. Dans cet article, le terme « DV » est synonyme de « feature flag ».

Le cycle de vie d'un DV suit le schéma suivant : création → expérimentation → déploiement → obsolescence → suppression. Nous considérons qu'un DV est obsolète lorsqu'il répond à l'ensemble des critères suivants :

  1. Il n'a pas été modifié depuis 90 jours.
  2. Il est toujours référencé dans le code.
  3. Il n'est pas encore en fin de vie (archivé ou retiré).
  4. Il ne figure pas sur la liste des exclusions.

Une tâche cron quotidienne crée un ticket Jira « STALEDV » pour chaque DV obsolète identifié et l'attribue à son créateur. Cela marque le début de la file d'attente à partir de laquelle notre système de nettoyage opère.

Flux de travail agentique vs outils de nettoyage basés sur l'AST

L'approche automatisée la plus connue pour le nettoyage des indicateurs est Piranha et son successeur polyglotte, qui simplifient les conditions dépendantes d'indicateurs en effectuant une correspondance de motifs dans l'arbre de syntaxe abstraite (AST). Cela fonctionne bien lorsqu'un indicateur est lu via un appel direct à l'API, ce qui permet à une règle de réécriture d'effectuer une correspondance de motifs.

Mais ce n’est pas ainsi que l’on accède aux DV chez DoorDash. Nos services utilisent un modèle de wrapper à trois couches avec injection de dépendances :

// Layer 1: RuntimeKeys.kt – the DV name as a string constant
object RuntimeKeys {
  const val ENABLE_X = “enable_feature_x”
}

// Layer 2: FeatureFlags.kt – a DI wrapper that calls the DV client
@Singleton
class FeatureFlags @Inject constructor(
    private val dvClient: DynamicValuesClient
) {
  fun shouldEnableX(userId: String): Boolean =
    dvClient.getBoolean(ENABLE_X, withContext(“user_id” to userId), false)
}

// Layer 3: business logic – uses the flag via dependency injection
class MyService @Inject constructor(
    private val featureFlags: FeatureFlags
) {
  fun process(userId: String) {
    if (featureFlags.shouldEnableX(userId)) handleNewPath()
    else handleOldPath()
  }
}

Il s'agit là d'une séparation claire des préoccupations, mais cela signifie que le nom du DV, l'appel d'API et la logique conditionnelle se trouvent chacun dans un fichier différent, reliés uniquement par des appels de méthode et l'injection de dépendances. Il s'agit là de relations sémantiques, et non syntaxiques ; les comparateurs de motifs AST ne peuvent pas les suivre. 

Détermination de la valeur cible

Il existe un autre problème qu’un outil fonctionnant uniquement à partir du code source ne peut pas résoudre : la détermination de la valeur cible et la consultation en temps réel de l’état de déploiement. Un outil qui se contente de lire le code source pourrait, sans s’en rendre compte, coder en dur une valeur erronée et modifier le comportement de l’application sans le signaler. Pour s’assurer que l’outil effectue correctement le nettoyage du code, il est nécessaire de connaître l’état de déploiement en temps réel du drapeau de fonctionnalité, qui réside dans la plateforme d’expérimentation et non dans le dépôt. Par exemple, la plateforme d’expérimentation de DoorDash prend en charge l’utilisation de drapeaux de fonctionnalité de type entier, chaîne de caractères et JSON. Une lecture en temps réel de la valeur cible garantit que l’outil modifie le code avec la valeur correcte du drapeau de fonctionnalité, qui reflète l’état actuel du système. De même, les informations relatives au déploiement constituent également un élément important de l’équation. Une expérimentation peut avoir été déployée à 60 %, puis abandonnée. L’outil devrait disposer de l’intelligence nécessaire pour déterminer qu’il s’agit d’un déploiement partiel et demander confirmation à l’utilisateur avant d’effectuer tout nettoyage. Cela peut soit inciter l’utilisateur à achever le déploiement ou la restauration avant d’utiliser l’outil pour effectuer le nettoyage, soit l’inviter à ignorer le nettoyage pour le moment.

Ampleur et complexité

Enfin, même une simple variable de données booléenne peut concerner entre cinq et vingt fichiers, une fois pris en compte la constante, le wrapper, chaque point d’appel, ainsi que la mise à jour ou la suppression des tests concernés. C’est un travail qui nécessite de comprendre le code, et pas seulement de faire correspondre la syntaxe. C’est exactement pour cette raison que nous avons choisi d’utiliser des agents LLM. Mais la tâche ne se résumait pas à rédiger une instruction et à demander à l’agent d’effectuer le nettoyage ; cela n’aurait pas permis d’obtenir un nettoyage efficace en termes de coûts et de temps, avec des garde-fous à grande échelle.

Un pipeline en deux phases avec intervention humaine

Le système repose sur l'Agent Development Kit (ADK) de Google et se déroule en deux phases, séparées par un seul contrôle manuel, comme le montre la figure 1.

Figure 1 : Architecture du système. Un agent d’orchestration exécute l’analyse de la phase 1, récupère les DV obsolètes depuis Jira, interroge la plateforme d’expérimentation sur l’état du déploiement via le protocole MCP (Model Context Protocol) et effectue une recherche dans la base de code ; un ingénieur vérifie ensuite le rapport. La phase 2 lance alors des agents de nettoyage en parallèle dans des arborescences Git isolées, qui modifient le code, le testent, vérifient la couverture de test et effectuent une analyse syntaxique avant d’ouvrir une pull request.

Phase 1 : Analyse et rapport

Un agent orchestrateur, en l'occurrence Claude Sonnet, traite les requêtes concernant les DV périmées. Pour chaque DV, il :

  1. Récupère les tickets Jira DV périmés via l'outil en ligne de commande d'Atlassian.
  2. Interroge la plateforme d'expérimentation de DoorDash via le protocole MCP (Model Context Protocol) pour obtenir les métadonnées de la variable dépendante (DV) : son UUID, son pourcentage de déploiement et sa valeur cible.
  3. Détecte le référentiel local et recherche toutes les références au code relatives au DV.
  4. Génère un rapport structuré proposant une valeur cible et répertoriant les fichiers concernés pour chaque DV.

Cette intégration MCP est le héros méconnu de la conception. En analysant l'état actuel du déploiement en production, l'agent détermine la valeur cible correcte au lieu de se fier à des suppositions issues du code. Par exemple, une variable de données (DV) introduite pour une fonctionnalité qui a finalement été abandonnée affiche un taux de déploiement de 0 % sur la plateforme ; l'agent sait donc qu'il doit la supprimer en utilisant la valeur de référence, plutôt que de supposer que la fonctionnalité est activée.

Point de contrôle humain

Avant toute modification du code, un ingénieur examine le rapport et valide la valeur cible de chaque DV. Il s’agit d’un mécanisme de sécurité délibéré destiné aux cas ambigus, tels que les déploiements partiels ou les expériences abandonnées, où la réponse correcte nécessite un jugement humain. Par exemple, une expérience peut avoir été déployée à 60 %, puis abandonnée. Ce contrôle humain permet à l’utilisateur d’intervenir en décidant, par exemple, s’il convient de terminer le déploiement ou de revenir en arrière avant que le nettoyage ne soit effectué.

Phase 2 : Nettoyage en parallèle

Pour chaque DV confirmé, l'orchestrateur lance un agent de suppression dédié — Claude Opus —, chacun s'exécutant dans son propre arborescence Git isolée. Jusqu'à quatre agents s'exécutent simultanément par dépôt, ce paramètre étant configurable. Chacun d'entre eux effectue un nettoyage complet et ouvre une pull request attribuée au propriétaire du DV.

À l'intérieur d'un agent de délogement

Chaque agent de suppression exécute un flux de travail itératif comme suit :

  1. Rechercher toutes les occurrences de la variable DV, y compris la constante, la fonction d'encapsulation et chaque point d'appel.
  2. Définissez une stratégie de nettoyage en fonction du type de la variable dynamique (booléenne, entière ou chaîne de caractères) et de son utilisation.
  3. Appliquez les modifications ; remplacez les appels à la fonction wrapper par la valeur cible et simplifiez les conditions qui sont désormais constantes.
  4. Supprimez la fonction d'encapsulation, la définition et la constante.
  5. Mettez à jour ou supprimez les tests concernés, par exemple en supprimant les branches de test « désactivées ».
  6. Vérifiez que la compilation s'effectue correctement.
  7. Vérifiez que les tests sont réussis.
  8. Vérifiez que le taux de couverture des correctifs est d'au moins 95 % sur les lignes modifiées.
  9. Lancez le linter Detekt.
  10. Si une validation échoue, identifiez le problème, corrigez-le, puis réessayez.

L'autonomie sur une base de code ne fonctionne que si les garde-fous sont réels. Quatre mécanismes assurent la sécurité des agents :

  • Isolation de l'arborescence de travail Git : chaque agent travaille dans une arborescence de travail dédiée, ce qui évite tout chevauchement entre les agents simultanés ; en cas d'échec du nettoyage, il suffit de supprimer l'arborescence de travail pour effacer les données.
  • Un délai d'expiration fixe d'une heure par agent : cela permet d'éviter qu'une session bloquée ne s'éternise.
  • Validation de la couverture des correctifs JaCoCo: cette règle impose une couverture de test d'au moins 95 % sur les lignes modifiées par l'agent.
  • Analyse statique Detekt: elle garantit le respect des conventions de style et détecte les problèmes courants.

Nous exécutons également Gradle avec l'option –no-daemon afin d'éviter toute contamination de l'état entre les arborescences de travail lors des exécutions parallèles. Il est essentiel de noter qu'un agent n'est pas autorisé à ouvrir une demande de pull tant que les tests et l'analyse statique n'ont pas été validés localement.

Résultats

Mettre en place un système est une chose, mais une seule question compte lorsqu’il fonctionne à l’échelle de DoorDash : est-ce qu’il fonctionne bien ? Dans ce cas précis, très bien, en effet.  Sur les 50 derniers signalements de code obsolète traités par le système, celui-ci a généré une pull request prête à être fusionnée pour 45 d’entre eux, à un coût moyen de 4,79 $ et en 13,8 minutes par opération de nettoyage, au lieu des une à deux heures qu’aurait nécessité un nettoyage manuel. Le système n’a introduit aucun bug ni aucune régression au cours du processus.

Comment nous l'avons évalué

Nous avons évalué le système sur les 50 DV obsolètes les plus récents qu'il a traités dans plusieurs dépôts Kotlin. Nous avons classé chacun d'entre eux selon leur niveau de complexité : 

  • C'est simple : une seule vérification booléenne dans un ou deux fichiers ; n = 6
  • Moyen — Utilisations multiples sur trois à cinq fichiers avec quelques modifications à titre d'essai ; n = 18
  • Complexe — conditions imbriquées, dépendances entre fichiers, types non booléens ou mises à jour importantes des tests ; n = 26 

Par type, 41 étaient de type booléen, six de type chaîne de caractères et trois de type entier/long. Chaque PR a été évaluée selon trois critères : la réussite des tests d'intégration (y compris le seuil de couverture de 95 % des correctifs), l'exactitude du code et l'avis des développeurs. 

Résultats en fonction du degré de complexité

Figure 2 : Résultats du nettoyage en fonction du niveau de complexité. Les variables dépendantes simples sont acceptées à 100 % dès le premier essai ; celles de complexité moyenne atteignent un taux de nettoyage de 94 % ; les variables dépendantes complexes nécessitent davantage de révisions et enregistrent davantage d'échecs, mais atteignent tout de même un taux de 85 %.

Comme le montre la figure 2, 31 des 50 opérations de nettoyage ont été menées à bien dès le premier essai, 14 ont nécessité une révision mineure et cinq ont nécessité l'intervention d'un ingénieur. La répartition par niveau de complexité met en évidence les limites de fiabilité du système :

  • DV simples : nettoyées à 100 % en un seul passage.
  • DV moyennes : 94 % (17/18) : 14 ont été réalisées en une seule fois, trois après une révision et une a nécessité l'intervention d'un ingénieur.
  • DV complexes : 85 % (22/26), même avec une suppression en cascade des paramètres sur plus de cinq couches de méthodes, des conditions imbriquées et une refactorisation intensive des tests.

Les 14 révisions étaient mineures et se corrigeaient d’elles-mêmes ; six d’entre elles présentaient une couverture insuffisante du correctif, nécessitant une itération supplémentaire pour ajouter des tests, et huit concernaient une suppression incomplète de code mort, avec une variable ou une référence résiduelle enfouie au cœur d’une chaîne d’appels. Toutefois, toutes ont été corrigées en une seule nouvelle requête adressée au LLM.

La conclusion la plus importante concernait la nature des problèmes rencontrés. Sur l’ensemble des 50 PR générés, nous n’avons observé aucun bug ni aucune régression introduits par le code modifié. Chacun des cinq cas nécessitant une intervention s'expliquait par la profondeur de la chaîne d'appels avec un enchaînement de paramètres entre interfaces ; l'agent a correctement supprimé la majeure partie du code mort, mais a manqué un ou deux maillons dans une longue chaîne impliquant plusieurs fichiers. En d'autres termes, la limite de fiabilité du système réside dans l'exhaustivité et non dans l'exactitude, ce qui atténue les craintes selon lesquelles le système pourrait créer des problèmes plus importants en cas d'échec.

Coût et durée

Les coûts et les délais varient en fonction de la complexité, mais restent clairement dans le cadre d’une « mise en œuvre à grande échelle », comme le montrent le tableau 1 et la figure 3. 

Le tableau 1 présente la répartition des indicateurs de performance moyens et médians, notamment en termes de temps et de coût, pour les 50 « feature flags » analysés, classés en fonction de leur complexité. Ces données montrent que, même si les « feature flags » plus complexes nécessitent naturellement davantage de temps et de budget, le système reste très efficace, même pour les tâches de nettoyage les plus difficiles.

ComplexiténDurée moyenneDurée médianeCoût moyenCoût médian
Simple67,5 min8,2 min$2.69$2.50
Moyen1810,4 min9,9 min$3.46$3.42
Complexe2617,7 min14,4 min$6.20$4.50

Tableau 1 : Indicateurs de performance par niveau de complexité pour les 50 « feature flags » évalués.

Figure 3 : Coût de l'API par rapport au temps réel pour chaque DV, classés par couleur en fonction de leur complexité. Les DV simples et moyennes se regroupent dans la zone à faible coût et à faible durée d'exécution. Les DV complexes sont plus dispersées, avec quelques valeurs aberrantes approchant les 40 minutes et les 19 $. Les DV ayant échoué (×) ont tendance à présenter des durées d'exécution plus longues.

Comme l’illustre la figure 3, les DV simples et moyennes se regroupent étroitement dans le coin « bon marché et rapide ». Les DV complexes sont plus dispersées, avec quelques valeurs aberrantes atteignant environ 40 minutes et environ 19 dollars. Même dans le haut de cette fourchette, l’agent permet une réduction spectaculaire de l’effort d’ingénierie et une baisse des coûts, même par rapport à une estimation prudente d’une heure nécessaire pour supprimer manuellement une seule DV.

Ce que nous avons appris

  • La spécialisation des modèles par rôle s'avère payante. Nous utilisons Claude Sonnet pour l'orchestration, notamment pour la prise en compte du contexte global, la collecte de métadonnées légères et la planification, et Claude Opus pour les agents de suppression, notamment pour le raisonnement approfondi sur la sémantique du code, les chaînes d'appels et les dépendances de test. Le fait de réserver le modèle le plus coûteux à la phase d'édition, qui est très exigeante sur le plan cognitif, nous a permis de réduire le coût global de notre API sans nuire à la qualité du nettoyage.
  • L'isolation de l'arborescence de travail est indispensable pour les agents parallèles. Nos premières expériences, menées sans isolation de l'arborescence de travail, ont donné lieu à des conditions de concurrence dans lesquelles des agents simultanés modifiaient les mêmes fichiers, générant ainsi des différences corrompues. Une arborescence de travail dédiée par agent, associée à Gradle avec l'option –no-daemon, a permis de sécuriser le nettoyage parallèle et de rendre les échecs isolés.
  • L'intégration de l'expérimentation en temps réel a été la décision la plus déterminante. Sans l'état actuel du déploiement, toute approche automatisée — qu'elle soit basée sur des règles ou sur un modèle de langage grand (LLM) — risque de coder en dur une valeur erronée et de perpétuer discrètement un comportement incorrect. La lecture via MCP n'a ajouté qu'une latence négligeable et a éliminé toute une catégorie d'erreurs liées aux valeurs cibles.

Toute équipe envisageant une automatisation similaire doit garder à l'esprit que nos résultats mettent en évidence trois conditions préalables concrètes : 

  • Une source fiable d'informations sur l'État du pavillon des navires en service
  • Isolation de l'arborescence de travail pour les modifications en parallèle, et 
  • Des étapes de validation déterministes avant même l'ouverture d'un ticket PR.

Prochaines étapes

Nous étendons le système selon deux axes :

  1. Un score de confiance de phase 1 permettant d'approuver automatiquement les corrections à faible risque. Par exemple, une variable dépendante booléenne dont le déploiement est à 100 % et dont le code par défaut correspond déjà à celui de l'environnement de production, ce qui permet de réserver le contrôle humain aux cas réellement ambigus.
  2. Une vérification de la qualité du code après le nettoyage. Cela permettrait de détecter des problèmes sémantiques, tels que des noms de variables qui ne reflètent plus leur fonction une fois qu'un indicateur a été supprimé.

Le nettoyage des « feature flags » est le genre de tâche qui, prise individuellement, est minime, mais qui, prise dans son ensemble, représente un travail colossal, en particulier à l’échelle de DoorDash, et qui est sans cesse reportée. En combinant un orchestrateur, des agents de suppression parallèles dans des arborescences de travail isolées, des requêtes en temps réel sur la plateforme d’expérimentation et des contrôles de validation stricts, notre système de nettoyage des feature flags transforme un retard accumulé de plusieurs milliers d’heures en un processus d’arrière-plan peu coûteux et fiable qui génère des pull requests prêtes à être fusionnées pour 90 % des flags traités, à un coût d’environ 4,79 $ et 14 minutes par flag, sans aucune régression. La seule limite restante concerne l’exhaustivité des chaînes d’appels les plus profondes, et c’est précisément là-dessus que nous allons concentrer nos efforts prochainement.

Vous souhaitez développer des systèmes comme celui-ci ? L'équipe d'ingénierie de DoorDash recrute : venez nous aider à développer nos solutions à grande échelle.

Flux est la plateforme d’agents basée sur le cloud de DoorDash destinée aux ingénieurs. En un seul mois de 2026, nous avons utilisé Flux pour automatiser 130 000 tâches d’ingénierie. Connaissant une expansion rapide depuis son lancement au premier trimestre 2026, Flux prend déjà en charge des flux de travail en arrière-plan à haut volume au sein de DoorDash, notamment plus de 25 000 revues de code automatisées chaque semaine, ainsi que plus de 300 playbooks uniques et plus de 10 000 invocations utilisées chaque semaine. Ces flux de travail peuvent s'exécuter de manière autonome, en parallèle et 24 heures sur 24.

Dans cet article de blog, nous allons passer en revue les contraintes qui nous ont poussés à aller au-delà des charges de travail des agents locaux exécutés sur des ordinateurs portables, expliquer pourquoi nous avons choisi de développer Flux en interne plutôt que de nous appuyer uniquement sur des agents de codage hébergés, et présenter les éléments fondamentaux de la plateforme, tels que les environnements de test des agents, la passerelle MCP, les playbooks et les interfaces d’invocation, qui permettent de rendre la délégation des agents reproductible et sécurisée.

Cas d'utilisation du workflow de fond de Flux

Tableau 1 : Aperçu de l'utilisation de Flux au sein de DoorDash sur un mois, couvrant les revues de code automatisées, l'exécution des playbooks et la réalisation des tâches en arrière-plan

Nos débuts

Au cours de l'année écoulée, les utilisateurs exécutant des charges de travail de type « agentic » sur leurs ordinateurs portables se sont rapidement heurtés à certaines limites :

  • Ressources et disponibilité. Un ordinateur portable dispose d’un nombre fixe de cœurs de processeur, d’une mémoire limitée et d’une batterie, tous ces éléments étant partagés avec toutes les applications installées. Les workflows d’Agentic doivent souvent exécuter en parallèle des tâches gourmandes en ressources de calcul, telles que des compilations, des tests et des recherches à grande échelle, ce qui épuise rapidement les capacités des ordinateurs portables. Les workflows dépendent également du fait que l’appareil soit allumé, connecté et disponible ; le travail s’interrompt lorsqu’un ingénieur ferme son ordinateur portable, perd la connexion ou s’absente.
  • Contrôles de sécurité. Les ordinateurs portables disposent généralement d’un large accès à des identifiants et systèmes sensibles, notamment des clés SSH, des sessions VPN et des outils nécessitant une authentification. Accorder à un agent autonome ce même niveau d’accès engendre un risque inutile et un champ d’impact potentiellement important. Les environnements locaux compliquent également la définition précise de ce à quoi un agent peut accéder et pendant combien de temps.
  • Visibilité et traçabilité. Lorsque les charges de travail s'exécutent sur des ordinateurs portables individuels, leur exécution est fragmentée et difficile à surveiller. Il devient alors plus compliqué de savoir ce qui s'exécute, où cela s'exécute, pour le compte de qui, et quels systèmes ou fichiers ont été concernés.

Notre approche pour résoudre ces problèmes est simple : 

Confier des tâches à des agents de codage autonomes et sécurisés afin que les ingénieurs puissent consacrer davantage d'énergie à l'innovation, à la réflexion critique et à la résolution de problèmes complexes.

Pourquoi nous avons développé Flux en interne

Les agents de codage hébergés sont utiles, mais ils imposent un compromis difficile : soit envoyer du code sensible et le contexte d’exécution à un tiers, soit ouvrir une voie permettant à ce tiers d’accéder aux systèmes internes. Pour DoorDash, le plus grand défi ne consistait pas simplement à faire en sorte qu’un agent écrive du code ; ce problème est en grande partie résolu. Il s’agissait plutôt de fournir à cet agent l’environnement, les outils, les autorisations, les intégrations et les contraintes appropriés.

Notre stratégie consiste à contrôler les éléments fondamentaux qui entourent l'agent, notamment l'orchestration, les environnements de test, les workflows, les autorisations, les intégrations, ainsi que les agents contextuels spécifiques à DoorDash nécessaires à un fonctionnement efficace. Nous avons également conçu ces éléments fondamentaux de manière modulaire, ce qui nous offre la flexibilité d'utiliser le meilleur outil tiers pour chaque tâche ou de développer en interne lorsque des exigences plus poussées en matière de sécurité, d'intégration, de performances ou de maîtrise de l'expérience utilisateur sont requises. 

Ces primitives démocratisent la création de workflows et rendent les systèmes plus adaptables aux futurs cas d'utilisation. Comme elles peuvent être combinées de différentes manières, les équipes peuvent créer de nouveaux workflows d'agents sans avoir à remanier l'infrastructure sous-jacente ni à imposer à chaque ingénieur la manière dont il doit structurer son workflow. Par exemple, nous exécutons les évaluations liées à notre révision de code sur l'infrastructure Flux.

Des éléments primitifs, pas des flux de travail

Comme le montre la figure 1, Flux s'articule autour de quatre éléments fondamentaux de la plateforme : les sandbox, la passerelle MCP (Model Context Protocol), les playbooks et les interfaces d'invocation. Ensemble, ils permettent de rendre la délégation des agents reproductible. Un playbook définit la tâche à effectuer. Une sandbox cloud offre à l'agent un environnement réel pour l'exécuter. Une passerelle d'agent contrôle les systèmes auxquels l'agent peut accéder. Enfin, les interfaces d'invocation permettent aux ingénieurs de lancer et de recevoir des tâches depuis les outils qu'ils utilisent déjà.

Figure 1 : Les quatre éléments fondamentaux de la plateforme Flux — les environnements de test, la passerelle MCP, les guides opérationnels et les interfaces d’invocation — ainsi que la manière dont ils s’articulent pour transformer une tâche en un travail qu’un agent peut effectuer en toute sécurité

Les « sandboxes » fournissent l'environnement d'exécution

Les agents locaux sont efficaces pour le développement interactif, mais ils ne conviennent pas aux flux de travail sans surveillance. Ils dépendent des ordinateurs portables de chaque ingénieur, se disputent les ressources locales, sont difficiles à contrôler et ne s'adaptent pas efficacement à la multiplication des tâches en parallèle.

Flux transfère l'exécution vers des environnements de test isolés dans le cloud, s'appuyant sur des micro-machines virtuelles (microVM) Firecracker pour assurer une isolation au niveau matériel. Chaque environnement de test est provisionné avec les référentiels, les outils de développement, les secrets et les dépendances d'exécution requis par la tâche, offrant ainsi aux agents un espace de travail d'ingénierie complet tout en fournissant à DoorDash un modèle cohérent en matière d'exécution, de sécurité et d'observabilité.

La maîtrise de cette couche nous permet de prendre en charge de véritables flux de travail d'ingénierie, notamment les modifications portant sur plusieurs dépôts et les demandes de fusion multiples à partir d'une seule session. Flux vise un objectif de niveau de service (95e centile) inférieur à cinq secondes pour la configuration complète de bout en bout — du démarrage de la micro-VM au clonage des dépôts requis, en passant par l'installation des outils de build et la configuration des harnais d'agents de codage pris en charge.

La passerelle MCP offre un accès contrôlé

Les agents doivent pouvoir accéder aux systèmes que les ingénieurs utilisent au quotidien, notamment les systèmes d'intégration continue (CI), les plateformes d'observabilité, les outils de suivi des incidents, les outils de déploiement, les outils de recherche de code, la documentation et les métadonnées des services. Cependant, l'octroi d'un accès étendu et sans restriction ne doit pas être la règle par défaut.

Flux relie les agents aux systèmes internes via une passerelle MCP développée en interne, appelée « Agent Gateway ». Chaque playbook spécifie les outils dont il a besoin, et Flux n'accorde que les autorisations limitées nécessaires à cette tâche. Chaque action est consignée, ce qui permet de disposer d'une piste d'audit claire.

Cette architecture de passerelle nous offre un point de contrôle centralisé pour l'authentification, l'autorisation, la surveillance, le suivi de l'utilisation et l'application des politiques, ce qui rend l'accès aux agents à la fois plus sûr et plus facile à gérer à grande échelle.

Les guides opérationnels définissent le travail

Un « playbook » est une unité réutilisable de travail automatisé — l’équivalent d’un conteneur Docker pour les compétences et les tâches automatisées sur la plateforme Flux. Défini dans un seul fichier YAML de balisage, il regroupe la tâche, les données d’entrée, le contexte, les compétences, les outils, les autorisations, la validation, les résultats attendus et les limites de sécurité nécessaires à l’exécution cohérente du travail.

Les playbooks peuvent combiner des étapes « agentiques », qui offrent flexibilité et marge d'appréciation, avec des étapes déterministes, qui garantissent prévisibilité, coûts réduits et validation simplifiée. Cela permet aux équipes de faire évoluer la logique entre l'exécution pilotée par des agents et le code classique à mesure que les besoins évoluent, sans avoir à repenser le flux de travail.

Les interfaces d'invocation s'adaptent aux besoins des développeurs

Un même scénario peut être déclenché depuis Slack, GitHub, cron, l'interface en ligne de commande (CLI) ou une compétence conversationnelle. Cela signifie que les équipes peuvent définir un workflow une seule fois et l'exécuter depuis l'interface la mieux adaptée à la situation :

  • Slack pour la délégation collaborative
  • GitHub pour l'automatisation des pull requests et de l'intégration continue
  • Cron pour la maintenance périodique
  • Interface en ligne de commande (CLI) permettant un contrôle direct par le développeur, ou pouvant être lancée via une skill

C'est ce qui rend Flux si facile à adopter.

Enseignements tirés

La mise en place de Flux nous a autant appris sur l'adoption d'un produit que sur l'infrastructure, notamment :

  • Commencez modestement pour gagner la confiance de vos collaborateurs. Nous avons commencé par la révision automatisée du code, plutôt que d’essayer d’automatiser l’intégralité du cycle de vie du développement logiciel. La révision du code était fréquente, mesurable et facile à évaluer pour les ingénieurs. Cela nous a permis de mettre en place un workflow de production dans lequel nous pouvions optimiser la qualité, la latence, les coûts et le comportement avant de passer au triage CI, aux tâches de permanence, aux guides de maintenance et au développement piloté par les tickets.
  • Rendre le travail visible. Notre première intégration à Slack créait des canaux privés pour chaque tâche confiée à un agent. Cela rendait Flux utile pour les individus, mais ne favorisait pas l’instauration d’habitudes au sein de l’équipe. Le fait de transférer le travail vers des fils de discussion publics a changé le mode d’adoption. Les ingénieurs pouvaient voir ce que les autres avaient délégué, suivre l’avancement de Flux, examiner les résultats et renforcer la confiance mutuelle.
  • Les guides opérationnels nécessitent une mise en œuvre. Les workflows réutilisables ne se créent pas d'eux-mêmes simplement parce que la plateforme existe. Des ateliers et des hackathons ont aidé les équipes à transformer les tâches opérationnelles récurrentes en guides opérationnels. Les éléments de base ont rendu l'automatisation possible ; la mise en œuvre a aidé les équipes à identifier les workflows qui méritaient d'être codés.

Prochaines étapes

Dans nos prochains articles, nous aborderons plus en détail les éléments fondamentaux de la plateforme qui permettent à Flux de fonctionner, ainsi que l'expérience développeur pour la création de nouveaux workflows. Nous évoquerons également les applications que nous avons développées à partir de cette plateforme, notamment Flux Responder, notre agent Slack interne.

Remerciements

Nous remercions Adam Rogal, Adam Yarger, Andy Fang, Ashwin Kachhara, Fan Xia, Ivan Rudovol, Jason Prasad, Jialu Deng, Justin Block, Justin Deocampo, Justin Fan, Keith Lyall, Praneet Singh, Sean Chen, Tyler Berrett et Volanda Zhu pour leurs contributions à la plateforme et à cet article.

Restez informé grâce aux mises à jour hebdomadaires hebdomadaires

Abonnez-vous à notre blog d'ingénierie pour obtenir des mises à jour régulières sur tous les projets les plus intéressants sur lesquels notre équipe travaille. projets les plus intéressants sur lesquels notre équipe travaille

Les agents IA ne sont utiles que lorsqu’ils peuvent intervenir au sein de systèmes réels. Chez DoorDash, cela implique d’accéder à des API internes, à des systèmes d’ingénierie, à des plateformes d’observabilité, à des systèmes de gestion des tickets, à des bases de connaissances et à des produits SaaS tiers. Le protocole MCP (Model Context Protocol) a facilité l’exposition de ces outils en offrant aux agents et aux serveurs un moyen commun de décrire, de découvrir et d’invoquer des fonctionnalités.

Lorsque les agents sont passés des phases d'expérimentation à des flux de travail réels, nous nous sommes heurtés à un autre problème. Si le MCP a permis de normaliser la structure d'un appel d'outil, il n'apportait toutefois pas de réponse aux questions essentielles liées à la production concernant cet appel :

  • Quel agent est autorisé à utiliser cet outil ?
  • Au nom de quel utilisateur, de quelle équipe ou de quel service agit-il ?
  • Quels identifiants faut-il utiliser ?
  • Quel sous-ensemble d'outils l'agent devrait-il même voir ?
  • Comment peut-on révoquer un accès ?
  • Comment savoir ce qui s'est passé après l'appel ?

Ces besoins se multiplient rapidement. Un agent de développement peut avoir besoin de GitHub, de Jira, d’un moteur de recherche de code, d’une intégration continue (CI), d’observabilité et de documentation. Un serveur MCP tiers peut exposer des centaines d’outils alors qu’un workflow n’en nécessite que cinq. Un agent en contact avec les utilisateurs peut avoir besoin d’une autorisation OAuth de l’utilisateur, tandis que l’automatisation d’équipe doit utiliser une entité de service non personnelle. Si chaque équipe résout ces problèmes de manière indépendante, chaque association agent-outil se retrouve avec son propre code d’authentification, son propre flux OAuth, sa propre gestion des secrets, son propre catalogue d’outils, ses propres limites de débit et ses propres journaux.

C’est pourquoi nous avons développé une passerelle pour agents (Agent Gateway) afin d’intégrer l’accès aux outils des agents au cœur de la plateforme. Il s’agit d’un point d’entrée unique et contrôlé à partir duquel les agents peuvent découvrir et invoquer des outils. La passerelle authentifie l’appelant, vérifie son autorisation, n’expose que les outils approuvés, injecte les identifiants appropriés, achemine la requête vers le serveur MCP en aval et enregistre un événement d’utilisation structuré pour chaque appel.

La passerelle n'est pas simplement un proxy. Il s'agit du plan de contrôle de l'écosystème des agents. Elle détermine qui peut accéder aux outils, sélectionne les outils visibles par les agents, regroupe ces derniers au sein d'interfaces axées sur les tâches, et offre à DoorDash un point central pour surveiller, limiter, révoquer et optimiser l'accès des agents aux outils.

Problème : l'accès aux outils comporte trois étapes

Si MCP aide les agents à utiliser des outils, le problème le plus complexe en environnement de production comporte trois aspects :

  1. Le premier point concerne l'accès. La plateforme doit savoir qui appelle, si cette personne est autorisée et quel modèle d'authentification s'applique : identité interne, jeton géré par la passerelle, OAuth par utilisateur ou accès via une entité de service.
  2. Le deuxième point concerne la sélection des outils. Les agents ne doivent pas recevoir une liste brute de tous les outils exposés par les serveurs MCP en aval. Des catalogues plus restreints, adaptés aux tâches à accomplir, améliorent la sécurité, réduisent la confusion des modèles et facilitent l'utilisation des agents.
  3. Le troisième volet concerne les opérations. Les appels aux outils doivent être soumis à des limites de débit, et faire l'objet de traces, de métriques, d'événements d'utilisation, d'une imputation des coûts, de métadonnées de propriété et d'une visibilité en production.

Notre Agent Gateway a été conçue parce que l'accès, la gestion et les opérations doivent être regroupés au sein d'une seule et même plateforme partagée, et non dupliqués sur chaque agent et chaque serveur MCP.

Architecture de passerelle

La passerelle comporte deux éléments principaux : un proxy et un registre, comme le montre la figure 1 ci-dessous. Le proxy constitue le plan de données ; il reçoit chaque requête MCP, authentifie l’appelant, autorise l’action, applique les limites de débit, injecte les identifiants, transfère la requête et génère des données d’observabilité. Le registre est la source de vérité du plan de contrôle ; il stocke les agents, les serveurs MCP, les propriétaires, les configurations de transport, les modes d’authentification, les politiques, les catalogues d’outils détectés et les configurations de surface d’outils.

Figure 1 : Architecture générale de la passerelle d'agents

Cette séparation confère à la passerelle certaines fonctionnalités difficiles à obtenir avec des intégrations point à point :

  • Les agents utilisent un modèle de point de terminaison MCP unique et cohérent, au lieu d'avoir à mémoriser les modèles d'authentification et de routage de chaque serveur en aval.
  • Les propriétaires d'outils enregistrent leurs capacités une seule fois, y associent les droits de propriété et la politique applicable, puis consultent les données d'utilisation en production.
  • Les équipes de sécurité disposent d'un point centralisé pour contrôler les accès, révoquer les autorisations et auditer les appels.
  • Les équipes chargées de la plateforme peuvent optimiser le parcours commun une seule fois, et tous les agents bénéficieront alors de cette amélioration.

Nous séparons également les périmètres de confiance internes et externes. Les flux de travail internes de DoorDash et les cas d’utilisation des agents en contact avec l’extérieur s’exécutent via des plans de proxy distincts, avec des bibliothèques partagées et des concepts de registre communs. Cela permet de limiter la portée des incidents liés à Internet et de laisser chaque modèle d’authentification évoluer de manière indépendante.

Gestion centralisée des identités, des autorisations et des secrets

Chaque requête adressée à la passerelle est associée à un appelant : un utilisateur, un service ou un agent agissant avec un contexte utilisateur délégué. Cette identité est conservée tout au long des processus d'autorisation, d'injection d'identifiants, de routage et d'observabilité, afin que chaque action en aval puisse être attribuée à son auteur.

La passerelle vérifie l'autorisation de manière centralisée. Les critères de politique ne se limitent pas à déterminer si cet appelant peut accéder à ce serveur. Ils peuvent également porter sur :

  • Cet agent peut-il accéder à ce serveur ?
  • Cet utilisateur peut-il lancer cet outil via cet agent ?
  • Cet outil peut-il être exposé dans cet environnement ?
  • Cet appelant peut-il utiliser une variante en écriture, ou uniquement une variante en lecture seule ?
  • Ce workflow peut-il utiliser un identifiant de service d'équipe, ou nécessite-t-il une authentification OAuth par utilisateur ?

Comme la politique de sécurité est gérée au niveau de la passerelle, DoorDash dispose d’un point centralisé pour modifier et révoquer les droits d’accès. Les responsables des outils n’ont ainsi pas à reconfigurer les autorisations pour chaque serveur, et les développeurs d’agents n’ont pas à intégrer les décisions de sécurité dans les invites ou le code des applications.

La gestion des identifiants suit le même schéma, comme le montre le tableau 1 :

Mode d'authentificationQui détient ce titre ?Fonctionnement de la passerelle
Identifiant de service interneInfrastructure de DoorDashTransmet le contexte de l'appelant vérifié aux services internes
jeton détenu par GatewayStockage sécurisé des clés de passerelleInjecte un jeton de fournisseur ou de service sans l'exposer à l'agent
OAuth par utilisateurBase de données des autorisations chiffrée par utilisateurInsère et actualise le jeton de l'utilisateur pour les actions relevant de la portée de l'utilisateur
Service principalDirecteur d'écurie géré par GatewayLes « mints » ou les courtiers émettent des identifiants non personnels à durée de vie limitée

Les agents ne doivent pas détenir d'identifiants bruts, tels que des clés API de fournisseurs, des jetons de rafraîchissement OAuth ou des autorisations humaines empruntées pour les automatisations d'équipe. La passerelle garantit que ces limites restent explicites et vérifiables.

OAuth par utilisateur sans interrompre le tour de l'agent

Les outils tiers fonctionnant au niveau de l'utilisateur nécessitent l'autorisation de ce dernier. Pour consulter la documentation d'un utilisateur, créer un ticket en son nom ou mettre à jour des données SaaS pour son compte, il n'est pas possible d'utiliser une clé partagée en toute sécurité. Avant le déploiement de la passerelle, chaque équipe avait tendance à mettre en place son propre flux OAuth, son propre système de stockage des jetons et sa propre expérience « connexion en premier ».

La passerelle centralise la procédure d'autorisation OAuth. L'accès est limité à l'agent, à l'utilisateur et au serveur. Lors du premier appel nécessitant une autorisation, la passerelle lance le flux OAuth du fournisseur, stocke les jetons d'accès et de rafraîchissement chiffrés ainsi obtenus, puis injecte le jeton de l'utilisateur lors des appels suivants. Les agents n'ont jamais accès au jeton brut.

Pour les clients prenant en charge la procédure d’authentification MCP, la passerelle peut suspendre l’appel de l’outil, demander au client d’afficher une invite de connexion, puis reprendre l’appel initial une fois que l’utilisateur l’a autorisé. Pour les clients ne prenant pas en charge cette procédure, elle renvoie une réponse structurée indiquant qu’une autorisation est requise, accompagnée d’une URL de connexion, comme illustré à la figure 2. Cela permet de transformer une connexion ayant échoué en une étape récupérable de l’appel de l’outil.

Figure 2 : Procédure d'échange d'informations préliminaires

Surfaces d'outils sélectionnées : ensembles et filtrage

Les développeurs ne raisonnent pas naturellement en termes de serveurs MCP. Ils raisonnent en termes de tâches. Un développeur ne souhaite pas que GitHub, Jira, l'observabilité et la documentation constituent des étapes de configuration distinctes ; il souhaite disposer de l'ensemble d'outils nécessaires pour analyser un problème, modifier le code, ouvrir une pull request, inspecter l'intégration continue (CI) et comprendre le comportement en production.

Parallèlement, la plupart des serveurs MCP en aval exposent bien plus d’outils qu’un agent ne devrait en utiliser. Les serveurs tiers peuvent publier des centaines d’opérations, notamment des actions d’administration, des actions destructrices, des API de facturation et des fonctionnalités spécifiques à certains fournisseurs. La plupart des workflows de DoorDash n’ont besoin que d’un petit sous-ensemble approuvé de celles-ci.

La passerelle résout ces deux problèmes grâce à des interfaces d'outils sélectionnées avec soin, comme le montre la figure 3. Les ensembles regroupent des outils provenant de plusieurs serveurs MCP au sein d'un point de terminaison MCP logique unique. Des filtres déterminent quels outils de chaque serveur sont mis à disposition pour un ensemble, un agent, un groupe d'utilisateurs, un environnement ou un public donné.

Par exemple, un ensemble d'outils de développement peut comprendre :

  • une sélection d'outils GitHub pour les workflows liés aux dépôts et aux pull requests ;
  • certaines fonctionnalités de Jira permettant de rechercher et de mettre à jour les tickets ;
  • des outils d'observabilité sélectionnés pour les journaux, les métriques et les traces ;
  • certains outils de recherche de code et de documentation ; et
  • certains outils de déploiement ou de gestion des indicateurs de fonctionnalité.

Figure 3 : Expérience utilisateur avec le pack MCP intégré

L'agent se connecte à une URL de passerelle, telle qu'un point de terminaison des outils de développement. Derrière cette URL, la passerelle répartit la liste des outils (tools/list) entre les serveurs du groupe, applique les filtres d'autorisation et les filtres d'outils, attribue des espaces de noms ou des alias aux noms d'outils si nécessaire, puis renvoie un catalogue cohérent. Lorsque l'agent appelle l'interface tools/call, la passerelle applique à nouveau la politique, achemine l'appel vers le serveur en aval approprié et applique le modèle d'authentification de ce serveur.

Cela offre aux agents une interface de qualité, plutôt qu’un simple ensemble brut de fonctionnalités en aval, et inclut notamment des outils approuvés, des noms stables, des descriptions plus claires, des métadonnées de propriété et des ensembles adaptés à des publics spécifiques. Les équipes d’ingénierie, d’analyse de données, d’assistance et les ensembles externes peuvent toutes utiliser les mêmes primitives de passerelle tout en exposant différentes interfaces d’outils.

Les avantages sont concrets :

  • La configuration de l'agent est plus simple : un seul point de terminaison de passerelle au lieu de plusieurs points de terminaison de serveur.
  • La détection des outils est plus sûre : les agents ne voient que les outils situés à l'intérieur de la zone autorisée.
  • Le comportement des modèles s'améliore : des catalogues plus restreints limitent les choix non pertinents et la confusion liée aux outils.
  • L'autorisation reste centralisée : la détection et l'invocation appliquent la même politique.

C'est là que la passerelle devient bien plus qu'un simple proxy d'accès. Elle détermine ce que les agents peuvent découvrir, ce qu'ils peuvent appeler et la quantité de contexte non pertinent qu'ils transportent. Une interface d'outils soigneusement sélectionnés peut faire la différence entre un agent qui choisit le bon outil et un autre qui se perd dans un catalogue d'API trop vaste.

Observabilité, imputation des coûts et protection en aval

Comme chaque appel transite par la passerelle, chaque requête peut générer un événement structuré comprenant :

  • le serveur, l'outil, le bundle et l'équipe responsable ;
  • l'utilisateur, l'agent, le service et la plateforme impliqués dans l'appel ;
  • résultat de l'autorisation, code d'état, source de l'erreur et répartition des temps de latence ;
  • la taille des requêtes et des réponses ; et
  • les métadonnées relatives aux coûts communiquées en aval, lorsqu'elles sont disponibles.

La passerelle génère également des métriques concernant le volume de requêtes, la latence par outil, les décisions d'autorisation, les résultats des actualisations OAuth, les décisions de limitation de débit, les connexions de streaming et les défaillances en amont. La propagation des traces permet aux équipes de suivre un appel depuis l'agent jusqu'au serveur en aval, en passant par la passerelle.

Cela présente un intérêt opérationnel direct : les équipes de sécurité peuvent contrôler les accès, les équipes chargées des plateformes peuvent identifier les agents qui génèrent trop de données, les responsables des outils peuvent suivre leur adoption et repérer les erreurs, et les équipes chargées de l'infrastructure peuvent attribuer les coûts.

Ce même mécanisme protège les systèmes en aval. Les limites de débit peuvent être définies par serveur, outil, appelant, utilisateur, ensemble ou type d'appelant. Les nouvelles limites peuvent être testées en mode « shadow » avant leur mise en application, ce qui permet de voir ce qui aurait été rejeté sans perturber le trafic de production.

La passerelle transforme la gouvernance en données, au lieu de compter sur chaque équipe pour renseigner les champs appropriés et faire respecter les limites adéquates.

Intégration en libre-service

Une passerelle ne fonctionne que si les équipes préfèrent l'utiliser plutôt que de la contourner. L'enregistrement, la détection, le filtrage et la gestion des bundles doivent tous s'effectuer en libre-service.

Grâce à l'interface utilisateur et à l'API du plan de contrôle, les équipes peuvent enregistrer des serveurs et des agents MCP, configurer l'authentification, détecter des outils, attribuer des droits de propriété, définir des filtres, ajouter des outils à des ensembles et analyser l'utilisation en production.

Le processus d'intégration de la passerelle se déroule selon les étapes suivantes :

  1. Enregistrez le serveur MCP.
  2. Découvrez son catalogue d'outils bruts via tools/list.
  3. Sélectionnez et approuvez les outils que DoorDash souhaite mettre à disposition.
  4. Définir le mode d'authentification, le propriétaire et la politique.
  5. Ajoutez les outils approuvés à un ou plusieurs ensembles.
  6. Surveillez le trafic, la latence, les erreurs, les décisions d'autorisation et les coûts.

Une gouvernance qui nécessite des tickets n'est pas évolutive. La solution toute faite doit être plus simple que de copier une clé secrète dans un agent et de se connecter directement à un serveur.

Qu'est-ce qui a changé ?

La passerelle offre à chaque groupe un avantage différent :

  • Les développeurs d'agents bénéficient d'une intégration unique, d'un catalogue soigneusement sélectionné, et n'ont pas à se soucier de l'authentification en aval, de l'OAuth, de la gestion des secrets ni du routage par outil.
  • Les propriétaires d'outils bénéficient d'un parcours de distribution géré, comprenant un contrôle d'accès, une visibilité approuvée sur les outils et des données d'utilisation en production.
  • Les équipes de sécurité disposent d'une gestion centralisée des politiques, des secrets, des autorisations OAuth, des révocations et des pistes d'audit.
  • Les équipes chargées de la plateforme bénéficient d'un effet de levier ; l'identité, la limitation du débit, l'observabilité, la qualité des outils, l'imputation des coûts et l'expérience des développeurs s'en trouvent toutes améliorées, le tout depuis un seul et même endroit.
  • Les agents bénéficient de catalogues plus concis, de noms d'outils plus clairs, d'offres groupées axées sur les tâches, d'un choix réduit d'options non pertinentes et de flux de connexion récupérables.

Adoption

L'adoption est le véritable test de validité d'une plateforme : les équipes ne l'utilisent que si cela leur facilite la tâche par rapport à un accès direct. À cet égard, l'Agent Gateway est désormais la voie par défaut pour accéder aux outils des agents au sein de l'équipe d'ingénierie de DoorDash :

  • Plus de 200 serveurs MCP sont enregistrés derrière la passerelle ; ensemble, ils mettent à disposition des milliers d'outils regroupés en sous-ensembles approuvés et adaptés à des tâches spécifiques, plutôt que sous forme de catalogues bruts.
  • Plus de 30 agents et services, utilisés par des milliers de collaborateurs, accèdent à ces outils via la passerelle, et aucun d'entre eux ne traite d'identifiants bruts. 
  • Chaque semaine, des millions d'appels vers des outils transitent par l'Agent Gateway ; chacun d'entre eux est authentifié, autorisé et enregistré sous la forme d'un événement d'utilisation structuré.
  • La mise en service est rapide et s'effectue en libre-service. L'enregistrement d'un nouveau serveur MCP et de ses outils ne prend que quelques minutes, et l'association d'un agent à un outil déjà enregistré est encore plus rapide.

Enseignements tirés

La mise en place et le développement de la passerelle ont bouleversé notre façon d'envisager l'accès des agents aux outils. Plusieurs enseignements en ressortent, et pas seulement chez DoorDash :

  • MCP résout les problèmes d'appel, mais pas ceux de gouvernance. L'identité, les politiques, les secrets, la gestion, l'observabilité et la révocation constituent désormais les aspects les plus complexes.
  • Le catalogue d'outils découvert est une interface, ce qui signifie que les noms, les descriptions, les filtres, les regroupements et le public cible ont leur importance.
  • Les « bundles » constituent l'unité idéale pour les flux de travail. Les agents ont besoin de boîtes à outils axées sur les tâches, et non de listes de serveurs.
  • Le filtrage des outils améliore à la fois la sécurité et la qualité des agents en proposant un ensemble d'actions plus restreint et plus clair.
  • Les identifiants doivent être gérés au niveau de la passerelle, où ils peuvent faire l'objet d'une rotation, d'un audit et d'une révocation de manière centralisée.
  • L'absence d'autorisations OAuth correspond à des états normaux, et non à des exceptions, dans le cadre de la gestion de la connexion au sein du protocole.
  • Le parcours guidé doit être en libre-service, sinon les équipes passeront directement par d'autres canaux.

Prochaines étapes

Le prochain investissement majeur portera sur le renforcement de l'identité des agents et de la délégation des droits aux utilisateurs. Le modèle visé attribue à chaque agent une identité cryptographique et permet à la passerelle de générer des identifiants délégués à durée limitée, dont la portée est limitée à l'utilisateur, à l'agent, à la tâche et à l'outil cible. Cela permet à la piste d'audit de disposer de deux entités réelles : l'utilisateur et l'agent.

Nous investissons également dans les outils de développement, notamment les fiches d'évaluation de la qualité et de la sécurité, la vérification des descriptions d'outils présentant des risques, la détection d'informations confidentielles ou à caractère personnel dans les erreurs, la création de serveurs guidée, l'enregistrement automatique, la découverte dynamique d'outils, ainsi que les flux d'événements d'appel d'outils anonymisés à des fins d'analyse et de conformité.

La découverte dynamique s'inscrit dans la même logique. Au lieu de fournir à un agent l'ensemble des outils d'un ensemble, la passerelle peut s'appuyer sur le contexte de la tâche, la politique en vigueur et les signaux d'utilisation pour ne proposer que les outils susceptibles d'être utiles pour la tâche en cours.

Conclusion

La passerelle Agent Gateway transforme l'accès aux outils des agents, qui nécessitait auparavant un travail d'intégration répétitif, en une fonctionnalité de la plateforme partagée. Le MCP a simplifié la description et l'appel des outils ; la passerelle rend cet accès régulé, géré, observable et évolutif.

Remerciements

Introduction

Nous avons développé Ask DoorDash sur une plateforme commune qui permet aux équipes métier de créer et de faire évoluer leurs agents sans avoir à refondre les systèmes sous-jacents. Nous avons évalué cette plateforme à l'aune de deux critères concrets : la rapidité avec laquelle les équipes pouvaient ajouter des fonctionnalités et des domaines, et la rapidité avec laquelle elles pouvaient évaluer et déployer des améliorations en matière de coût, de qualité et de latence.

Nous avons lancé « Ask DoorDash » avec la prise en charge des restaurants et des épiceries en environ deux mois, et la plateforme a depuis traité plus de deux millions de conversations. L'ajout de « Réservations », notre troisième agent thématique, n'a pris qu'une semaine, soit environ dix fois plus vite que la création des premiers agents thématiques.

Notre infrastructure d’évaluation commune et nos contrôles de déploiement fournissent aux équipes des indicateurs de qualité clairs lorsqu’elles mettent en production les modifications. Moins d’une semaine après la sortie d’un nouveau LLM, nous l’avons évalué et déployé, réduisant ainsi la latence p50 de 35 % sans aucune baisse des scores de qualité. Une mise à niveau ultérieure du modèle a permis de réduire encore la latence p50 de 40 %.

Cet article explique comment nous avons déterminé les éléments à intégrer à la plateforme, comment ses composants s'articulent entre eux, ainsi que les compromis qui ont motivé ces choix. Il fait suite à notre présentation générale des aspects techniques, à notre analyse approfondie de l'intelligence et à notre analyse approfondie de l'évaluation. Une analyse approfondie de l'expérience utilisateur suivra.

Comment nous avons choisi les éléments à normaliser

Nous n'avons centralisé une fonctionnalité que lorsque plusieurs domaines en avaient besoin et que des implémentations distinctes auraient pu entraîner des problèmes de fiabilité ou d'exploitation. L'orchestration, la mémoire, l'accès aux modèles, le traçage, l'infrastructure d'évaluation et les contrôles de déploiement répondaient à ces critères. Les équipes métier ont conservé la maîtrise des instructions, des compétences, des outils, des critères d'évaluation et des choix de modèles qui définissent leurs agents.

Lorsqu'une norme industrielle adaptée existait, nous l'avons adoptée. Agent2Agent (A2A) définissait la manière dont les agents communiquaient, tandis que le Google Agent Development Kit (ADK) fournissait le cadre nécessaire à leur développement et à leur exécution. Ces normes ont parfois limité nos choix de conception, mais elles ont permis aux équipes de s'appuyer sur des contrats communs et nous ont évité de devoir développer des équivalents propriétaires.

Pour les problèmes récurrents pour lesquels il n'existait pas de norme adaptée, nous avons développé nous-mêmes les capacités nécessaires. Une régression dans le code partagé pouvant affecter plusieurs agents, des contrôles d'évaluation et de déploiement étaient indispensables. Chaque modification apportée au code partagé devait satisfaire à des contrôles de qualité communs et respecter les mesures de sécurité relatives à la mise en production.

Enquête sur l'architecture de DoorDash

Le choix architectural central consistait à déterminer ce que chaque agent devait partager et ce qui relevait de la responsabilité de chaque domaine. Nous avons mis en place un chemin d'exécution partagé autour d'agents spécialisés, propres à chaque domaine. Un agent unique à usage général serait devenu de plus en plus difficile à appréhender à mesure qu'il assumerait davantage de responsabilités, tandis que des piles d'agents totalement indépendantes auraient entraîné une duplication de l'infrastructure et généré des expériences incohérentes.

Comme le montre la figure 1, chaque requête passe par la passerelle. La passerelle authentifie l’utilisateur, assemble le contexte du point d’entrée et assure la traduction entre les interfaces HTTP et de streaming du client et le protocole A2A de la plateforme. Elle transmet ensuite la requête à l’orchestrateur, qui choisit l’agent de domaine pour chaque tour de parole tout en préservant la continuité à mesure que la conversation passe d’un domaine à l’autre. L’agent sélectionné charge les compétences appropriées et utilise les outils du Model Context Protocol (MCP) pour interagir avec les services DoorDash existants.

Au-delà de ce chemin de requête, la plateforme fournit l'état de session, la mémoire, les artefacts, l'accès aux modèles, le traçage, l'infrastructure d'évaluation et les contrôles de déploiement. Les agents de domaine utilisent ces services communs tout en conservant le contrôle de leurs instructions, de leurs compétences, de leurs outils et de leurs critères d'évaluation.

Figure 1 : Ask DoorDash distingue le comportement spécifique à chaque domaine des agents des capacités d'exécution et de production communes utilisées dans l'ensemble du système.

Une passerelle vers des expériences réactives et multimodales

Les passerelles client DoorDash ont été conçues pour des API qui renvoient rapidement une réponse complète. Les agents fonctionnent différemment. Le premier message peut être prêt alors que l’agent est encore en train d’appeler des outils, et la réponse finale peut combiner du texte avec des fiches interactives de magasins ou d’articles. Nous devions gérer ce flux de travail sans avoir à expliquer à chaque client comment les agents communiquent.

Le Gateway se charge de cette conversion. Il authentifie l'utilisateur, ajoute des informations contextuelles sur l'origine de la conversation et convertit les requêtes HTTP des clients en requêtes A2A. Lors du retour, il utilise le SDK Vercel AI pour envoyer des mises à jour textuelles et des données de widgets aux clients via le protocole SSE (Server-Sent Events). Les clients affichent ces mises à jour au fur et à mesure de leur réception, ce qui permet aux utilisateurs de suivre la progression pendant que l'agent termine son tour de conversation.

La mise en place et l'exploitation de la passerelle ont demandé du travail, mais elles ont permis de centraliser des tâches qui, sans cela, auraient fait l'objet de doublons chez chaque client de DoorDash. Les agents peuvent désormais évoluer dans le cadre d'un contrat client unique et stable, sans qu'il soit nécessaire de coordonner les mises à jour chez chaque client. Un nouvel agent de domaine réutilise le parcours d'authentification et de diffusion existant, au lieu de devoir s'intégrer séparément à chaque application.

Un assistant, des agents spécialisés dans différents domaines

Une conversation peut passer d'un domaine à l'autre sans préavis. Un utilisateur peut commencer par dire : « Je veux me faire livrer une tourte au poulet en moins de 30 minutes », puis décider : « En fait, je préfère la préparer chez moi. » La première demande relève du domaine « Restauration » et la seconde du domaine « Épicerie », mais l'expérience doit donner l'impression de ne former qu'une seule et même conversation.

Nous avons testé une architecture à agent unique, mais les domaines « Restaurant », « Épicerie » et « Réservations » s'appuient sur des outils, des politiques et des critères d'évaluation différents. Les regrouper rendrait l'agent plus difficile à tester et obligerait chaque domaine à suivre le même cycle de déploiement. Chaque domaine dispose donc de son propre agent spécialisé. L'Orchestrator analyse la conversation et achemine chaque tour de parole vers l'agent approprié via A2A.

Ce routage ajoute de la latence et des jetons d’entrée, même lorsque la conversation reste dans le même domaine. Pour réduire ce coût, l’Orchestrator attribue les tours de parole suivants à l’agent de domaine sélectionné. Cette attribution permet à cet agent de traiter directement les requêtes suivantes, sans étape de routage supplémentaire. Si la conversation change de direction, l’agent de domaine reconnaît le message hors champ et rend le contrôle à l’Orchestrator. L’Orchestrator réachemine la requête au cours du même tour, de manière transparente pour l’utilisateur.

Figure 2 : Le « pinning » évite un appel à l'Orchestrator lors des tours suivants, tout en permettant à la conversation de passer d'un domaine à l'autre.

Les compétences permettent de limiter le contexte et les coûts

Des agents spécialisés ont empêché les domaines « Restaurant », « Épicerie » et « Réservations » de partager une seule et même fenêtre de contexte gigantesque. Cependant, à mesure que chaque agent gagnait en capacités, le même problème de contexte est apparu au sein de chaque domaine. Les nouvelles fonctionnalités ajoutaient des instructions et des outils à chaque tour, même lorsque celui-ci ne les utilisait jamais. Cela a augmenté le coût en jetons de saisie, et les instructions qui se chevauchaient ou se contredisaient semaient la confusion chez l'agent.

Nous avons intégré la prise en charge des compétences à notre harnais afin de modulariser les instructions et les outils. À chaque tour, l’agent ne charge que ce qui est pertinent. Un utilisateur peut commencer par dire : « Trouve-moi un restaurant thaïlandais bien noté qui livre en moins de 30 minutes », puis ajouter : « Montre-moi le menu du premier et ajoute le pad see ew à mon panier. » Ces deux demandes sont traitées par l’agent « Restaurant », mais la première charge la compétence de recherche et de découverte, tandis que la seconde charge la compétence de commande via le panier, qui gère la consultation du menu et les actions liées au panier.

Le chargement dynamique pose un problème de sélection. Le sélecteur doit déterminer à quel moment une capacité est nécessaire sans charger trop souvent du contexte non pertinent ; c’est pourquoi nous évaluons la sélection des compétences dans le cadre du comportement de l’agent. Cette complexité supplémentaire en valait la peine, car elle a permis aux équipes d’ajouter des capacités sans avoir à élargir le contexte à chaque tour.

Pour mesurer cet effet, nous avons compté les tokens présents dans la base de l’agent et dans les instructions chargées pour les compétences. Nous avons exclu l’historique des conversations, les messages des consommateurs, les schémas d’outils et les résultats des outils. La réplique médiane relative à une compétence utilisait moins de la moitié du nombre de tokens d’instruction par rapport à la requête du système monolithique.

AgentInstructions de base et de compétences avec toutes les compétences chargéesNombre médian d'instructions liées aux compétences par tourRéduction
Restaurantenviron 42 000 jetons

environ 20 000 jetons50 % et plus
Épicerieenviron 25 000 jetonsenviron 10 000 jetons60%

Accès sécurisé et réutilisable aux services via MCP

Ask DoorDash a besoin d’accéder aux services DoorDash pour rechercher des magasins et des articles, consulter les menus, gérer les paniers et agir au nom de l’utilisateur. Les API qui sous-tendent ces opérations supposent un code d’application déterministe. Un modèle de langage de grande envergure (LLM) choisissant les opérations au moment de l’exécution a besoin d’une interface plus restreinte. Exposer directement les API obligerait le modèle à interpréter des interfaces de bas niveau. Encoder les autorisations et les règles métier uniquement dans la prompt ajouterait du contexte sans garantir leur application.

Nous avons mis en place une couche commune de protocole de contexte de modèle (MCP) entre les agents et les API DoorDash. Chaque outil MCP expose une opération spécifique comprenant les entrées et les sorties dont le modèle a besoin. C'est le modèle qui choisit l'outil à appeler. Un code déterministe valide chaque requête et applique les autorisations et les règles métier avant que l'appel n'atteigne le service sous-jacent.

Les invites aident le modèle à choisir l'opération appropriée, mais ces indications ont un caractère purement consultatif. La validation et l'application des règles s'effectuent au niveau du code de l'outil à chaque appel, ce qui garantit une limite de sécurité stable même lorsque les invites et les modèles évoluent.

La conception d’un outil requiert un certain équilibre. Un outil trop général peut proposer au modèle un choix trop vaste, tandis qu’un outil trop restreint peut transformer une tâche en une longue chaîne d’appels. Notre serveur MCP partagé propose désormais plus de 60 outils répartis entre les workflows publics et internes des agents. Un nouvel agent sélectionne les outils dont il a besoin dans cette bibliothèque, et les améliorations apportées à la validation, à la télémétrie ou à l’intégration des services profitent à tous les agents qui les utilisent.

Figure 3 : Les invites influencent ce que le modèle demande ; le code de l'outil MCP détermine ce qui peut être exécuté.

Prêt pour la production par défaut

ADK nous a fourni les éléments de base nécessaires à la création d'un agent, notamment des instructions, des outils, des callbacks, des sessions et le câblage des modèles. Les agents DoorDash avaient également besoin d'un traçage distribué, d'un accès aux modèles, d'un état durable et de contrôles de déploiement. Sans couche partagée, chaque équipe métier aurait dû prendre ces décisions et gérer l'infrastructure qui en résulte de manière indépendante.

Nous avons développé des modules réutilisables sur la base de l'ADK afin de garantir que certaines fonctionnalités fonctionnent de la même manière d'un agent à l'autre. Les équipes métier continuent de choisir leurs instructions, leurs outils et la configuration de leurs modèles. La plateforme relie ces choix aux systèmes nécessaires pour assurer un fonctionnement fiable des agents en production.

Le traçage en est un exemple. Une équipe l'active via la configuration, et le SDK partagé diffuse un identifiant de traçage à travers les appels A2A, les outils MCP et les services DoorDash en aval. Les ingénieurs peuvent suivre une requête à travers l’Orchestrator, les agents de domaine et les appels d’outils, au lieu de devoir rassembler manuellement les journaux provenant de systèmes distincts. Ces mêmes données de traçage facilitent le débogage et l’évaluation. La mise à disposition du traçage via la plateforme permet d’économiser environ un mois de travail d’observabilité pour chaque lancement d’un nouvel agent.

L'accès aux modèles repose sur le même principe. Les équipes métier choisissent les modèles utilisés par leurs agents, tandis que la plateforme uniformise la manière dont ces modèles sont invoqués, tracés et protégés par un comportement de secours. Les équipes peuvent évaluer et adopter de nouveaux modèles sans avoir à réécrire les intégrations spécifiques à chaque fournisseur. Cette approche commune a permis les mises à niveau rapides des modèles et les améliorations en matière de latence décrites précédemment.

Un état partagé pour des conversations fiables

Les premières tentatives de développement de produits basés sur des agents DoorDash en 2025 ont montré à quelle vitesse l'expérience se détériore lorsque l'état du système n'est pas fiable. Un agent pouvait répondre correctement à une requête, puis oublier ou mal utiliser les informations issues d'un échange précédent. Pour résoudre ce problème, il ne suffisait pas d'ajouter l'historique de la conversation à la consigne.

Ask DoorDash utilise trois types d'état. L'état de session permet de suivre la conversation en cours et les tâches déjà effectuées dans le cadre de celle-ci. La mémoire conserve les informations susceptibles d'être utiles lors de conversations ultérieures, telles que les préférences de l'utilisateur. Les artefacts contiennent les résultats structurés créés et mis à jour par les agents, notamment les listes de courses et les fiches interactives. Chacun d'entre eux présente une durée de vie et un mode d'accès différents.

Auparavant, les projets d’agents DoorDash mettaient en œuvre ces fonctionnalités de manière indépendante. Cela entraînait une duplication des tâches de persistance et faisait dépendre la fiabilité des choix de chaque équipe. Nous les avons centralisées au sein de Managed Agent Services, qui fournit des API compatibles avec l’ADK pour les sessions, la mémoire et les artefacts. Les agents de domaine utilisent les mêmes interfaces sans avoir à gérer leurs propres systèmes avec état.

La centralisation nécessite tout de même des règles distinctes en matière de stockage et de cycle de vie pour les données de session, la mémoire à long terme et les artefacts. Managed Agent Services regroupe ces délimitations en un seul et même endroit. Chaque domaine continue de décider quelles informations son expérience doit enregistrer et récupérer.

Les services d'agents gérés et l'architecture mémoire sont abordés plus en détail dans la deuxième partie.

Restez informé grâce aux mises à jour hebdomadaires hebdomadaires

Abonnez-vous à notre blog d'ingénierie pour obtenir des mises à jour régulières sur tous les projets les plus intéressants sur lesquels notre équipe travaille. projets les plus intéressants sur lesquels notre équipe travaille

Ce que la plateforme a changé

La fonctionnalité « Réservations » a constitué un test concret de la plateforme. L'équipe a réutilisé le processus de production déjà en place pour les secteurs « Restaurant » et « Épicerie » et a lancé la prise en charge des « Réservations » en une semaine, soit environ 10 fois plus rapidement que si elle avait dû développer les agents de domaine initiaux.

Les équipes métier restent responsables du comportement des agents et des critères de qualité. La plateforme fournit l'infrastructure d'évaluation, les outils MCP, le traçage, l'accès aux modèles et les contrôles de déploiement, de sorte que les améliorations apportées à ces composants profitent à tous les agents qui les utilisent.

Le partage des infrastructures amplifie la portée des erreurs. Un défaut peut affecter plusieurs agents, et une abstraction prématurée peut contraindre différents produits à adopter le même modèle. Nous ne normalisons une fonctionnalité qu’après que plusieurs domaines en aient exprimé le besoin et que des implémentations distinctes risqueraient d’entraîner des problèmes de fiabilité ou d’exploitation.

Rejoignez-nous

Si la création à grande échelle de plateformes d'agents et d'IA destinées aux utilisateurs vous intéresse, consultez nos offres d'emploi en ingénierie sur careersatdoordash.com.

Comment nous avons mis au point une couche d'évaluation qui nous permet de déterminer où, pourquoi et dans quelle mesure nous pouvons faire confiance à un réviseur de code autonome, et pourquoi un indicateur unique n'aurait jamais pu y parvenir.

En bref

DoorDash a récemment expliqué comment nous avons développé un agent de révision de code en production auquel les ingénieurs prêtent réellement attention. Cet article abordait les choix en matière de produit et d’architecture qui sous-tendent cet agent : un « lead scout », des réviseurs approfondis, un contexte ciblé, une approche privilégiant la précision plutôt que le rappel, ainsi qu’une conception indépendante du modèle. DashBench est la couche de mesure qui sous-tend ce système. DashBench repasse l’historique des PR et évalue si les systèmes mettent en évidence des résultats concrets, exploitables par l’humain, au lieu de se contenter de produire des commentaires plausibles. Cette distinction est importante car des indicateurs pratiques tels que le taux d’acceptation, les retours positifs ou un simple score global peuvent donner l’impression qu’un réviseur est efficace tout en masquant ses lacunes : passer à côté de problèmes importants, se concentrer excessivement sur les commentaires faciles, ou privilégier la précision au détriment de la couverture, d’une manière que les métriques produit ne révèlent pas.

Résultat phare : sur l’ensemble des 105 cas étudiés, le réviseur de code de production de DoorDash (Claude Sonnet 4.6 « high scout » + Claude Opus 4.8 « high reviewer ») a identifié 504 anomalies réelles avec un rappel pondéré de 53,6 %, contre 164 anomalies réelles et un rappel pondéré de 30,7 % pour une référence GPT 5.5 « high » sans scout. L'analyse globale des combinaisons de modèles est plus intéressante que la simple désignation d'un vainqueur unique : la combinaison Kimi K2.6 (scout) + Claude Fable 5 (reviewer) a obtenu les meilleurs résultats en termes de rappel pondéré et d'indice F1, tandis que la combinaison Composer 2.5 (scout) + GPT 5.5 medium (reviewer) a obtenu les meilleurs résultats en termes de précision pondérée ; les modèles de référence à passage unique sont quant à eux restés nettement moins coûteux.

Figure 1 : Compromis entre précision et rappel pondérés
Figure 2 : Compromis entre le F1 pondéré et le coût.

Remarque : les indicateurs pondérés accordent plus d'importance aux problèmes présentant un niveau de gravité plus élevé (critique = 4, élevé = 2, moyen = 1, faible = 0,5).

Pourquoi les signaux évidents sont trompeurs

La méthode la plus tentante pour évaluer un réviseur de code – et le principal indicateur utilisé aujourd’hui par la plupart des grands outils de révision de code – consiste à observer ce qui se passe en production : les auteurs acceptent-ils ses commentaires, en tiennent-ils compte ? Ce signal est bien réel. Mais il repose sur des hypothèses fragiles et des informations incomplètes. Dans le langage des matrices de confusion, l’acceptation ne remplit jamais que deux des quatre cellules : un commentaire accepté par l’auteur est enregistré comme un « vrai positif » (TP), tandis qu’un commentaire rejeté est considéré comme un « faux positif » (FP). Ces deux classifications partent du principe que la décision humaine est la vérité absolue – que les ingénieurs sont infaillibles, ou du moins qu’ils se trompent si rarement et de manière suffisamment aléatoire pour que ces erreurs n’aient pas d’importance dans l’ensemble. Comme nous le verrons, c’est un pari risqué. 

Les deux autres cellules restent vides, et c’est là que réside le véritable coût. L’acceptation ne peut pas enregistrer les faux négatifs (les bogues que le réviseur a manqués) ni les vrais négatifs (le code propre pour lequel le silence était la bonne réponse). Elle vous indique qu’il s’est passé quelque chose ; elle ne vous dit pas si la révision était correcte, si le silence était justifié, ni ce qui a été manqué. DashBench gère cela différemment : après évaluation, les groupes de problèmes réels manqués comptent comme des faux négatifs pour le score de référence, même si la télémétrie d’acceptation en production ne peut pas les observer.

L’autre problème évoqué plus haut est que l’acceptation humaine est un signal utile, mais pas une vérité absolue. Les auteurs acceptent ou rejettent des commentaires pour des raisons liées au produit et au flux de travail : le moment choisi, l’urgence en matière de relations publiques, le contexte de responsabilité, le degré d’intrusion de la correction, ou encore le fait que le problème ait déjà été traité d’une autre manière. Cela fait de l’acceptation une télémétrie produit précieuse, mais un indicateur de référence peu fiable en soi. Lors des audits de désaccord, l’examen humain et la vérification par un agent ont tous deux mis en évidence des erreurs ; l’objectif n’était pas de donner raison à l’une des parties, mais de distinguer ce qui « a été accepté » de ce qui « était réel ». L’indicateur le plus accessible peut tout de même pointer vers la mauvaise cible.

C’est la raison pour laquelle nous avons développé DashBench. Si nous nous appuyions sur un indicateur pratique pour concevoir et améliorer un système dont dépendent désormais nos ingénieurs, nous optimiserions en toute confiance un objectif fondamentalement erroné. La solution ne réside pas dans un indicateur unique plus performant, mais dans un outil d’évaluation qui recoupe plusieurs signaux imparfaits sans jamais considérer aucun d’entre eux comme la vérité absolue. 

D'une architecture de production à un environnement épuré propice aux mesures

Notre outil de révision de production utilise une architecture par étapes qui sépare la détection des anomalies de leur vérification. Un « scout » principal examine la modification et signale les zones suspectes. Des réviseurs spécialisés se chargent ensuite d’étudier les pistes les plus prometteuses, de confirmer si chaque préoccupation est fondée et d’écarter les signalements qui ne résistent pas à un examen approfondi. Ce processus reflète la manière dont travaillent les réviseurs humains expérimentés : ils se fient à leur intuition, concentrent leur attention sur les parties à risque de la comparaison, puis valident leurs conclusions avant de demander quoi que ce soit à l’auteur.

Cette conception nous offre justement une surface d’évaluation claire. Le workflow étant indépendant du modèle, chaque composant est une variable que nous pouvons maintenir fixe ou modifier individuellement : le modèle d’exploration, le modèle de révision, la politique de contexte, la politique d’outil, le budget d’exécution. Nous pouvons rejouer les mêmes PR figées via l’agent de production, via des variantes par étapes avec différentes affectations de modèles, et via de simples réviseurs en un seul passage, et l’élément mesuré reste constant.

La question pertinente n'est donc plus « quel est le meilleur modèle ? », mais plutôt : pour une architecture, un contexte, une politique d'outils, un budget d'exécution et une combinaison de modèles donnés, quel compromis entre couverture, précision, coût et latence obtenons-nous réellement, et où ce compromis présente-t-il des failles ? Le terme « meilleur » n'a de sens que si l'on précise « meilleur en quoi », « dans quels cas » et « à quel coût ».

Comment nous avons élaboré cet indice de référence

Figure 3 : Pipeline de tests de performance DashBench

Ce qui importe, c’est la boucle, et non pas une case en particulier. Les nouveaux cas et les changements de modèle continuent d’entrer dans le même cycle de relecture et d’évaluation, tandis que l’examen des désaccords garantit l’intégrité du benchmark lorsque les retours humains, le comportement en production et le jugement de l’agent ne concordent pas. Au cours de cette étape, nous inspectons manuellement les preuves, déterminons si le résultat est valide, puis réintégrons les cas résolus dans l’étalonnage du juge. Le juge lui-même est basé sur un LLM, mais DashBench le traite comme un signal étalonné, et non comme une vérité absolue.

Les cas.

DashBench est parti d'un échantillon d'environ 1 000 candidatures de PR brutes et a été sélectionné pour mettre en évidence des cas illustrant différents comportements de révision, tels que des diffs complexes, des historiques de révision chargés et des résultats variés en termes de gravité. L'article utilise le rapport valide de 105 cas pour analyser les systèmes par étapes par rapport aux systèmes en un seul passage, les choix de modèles « scout/reviewer » et la qualité pondérée en fonction de la gravité sur un échantillon d'évaluation cohérent.

  • Communiqués de presse historiques accompagnés des conclusions réelles de l'évaluation et d'un contexte suffisamment complet pour permettre de retracer fidèlement le déroulement de celle-ci.
  • Les PR « bénins » ne présentant pratiquement aucun élément concret permettent au benchmark de mesurer la modération et de détecter les faux positifs ; un réviseur qui insiste trop sur la qualité du code constitue en soi un facteur d'échec.
  • Les PR qui ont ensuite été annulées ou corrigées par un correctif d'urgence, afin que le test de performance puisse vérifier si un système détecte les véritables régressions au niveau du code avant la fusion.

Les étiquettes. 

C’est là que la plupart des tests de performance ont pris un raccourci que nous avons refusé de prendre. La préparation des étiquettes ne s’est pas limitée à l’importation des retours humains. Nous avons demandé aux ingénieurs auteurs des PR d’annoter les résultats potentiels, puis nous avons comparé trois sources entre elles : les annotations humaines, les résultats potentiels d’origine et un juge automatisé. Les retours humains étaient précieux mais souvent erronés : les réviseurs passaient à côté de problèmes valides, acceptaient des affirmations peu fondées ou interprétaient le contexte d’un PR historique différemment du réviseur suivant. Lorsque les trois sources divergeaient, nous réexaminions manuellement les preuves et résolvions le litige ; ces cas résolus servaient ensuite de données d’étalonnage pour le juge.

Il en résulte un critère de référence dont la vérité de référence ne repose pas sur une seule source susceptible d'erreur. Le jugement humain, les retours d'expérience liés à la production et l'évaluation par les agents y contribuent chacun à leur manière ; aucun n'est considéré comme infaillible. 

L'environnement d'exécution. 

Dans le cadre d'une comparaison, chaque configuration est exécutée en utilisant le même ensemble de cas figés, la même piste contextuelle, la même sélection de invites et de packs de compétences, le même contrat de sortie normalisé et le même pipeline d'évaluation. Le harnais fait partie intégrante de ce que nous mesurons : les profils diffèrent par le type de runner, les modèles de scout/réviseur, l’interface des outils, les mécanismes contextuels et les limites des fournisseurs. Certains s’exécutent à partir d’un dépôt préparé avec des outils de type « read/grep », d’autres reçoivent un contexte de dépôt délimité dans l’invite, et d’autres encore désactivent les étapes de recherche ou d’évaluation lors de l’exécution de révision. Le coût, la latence, les délais d’expiration, les nouvelles tentatives et les échecs sont consignés dans les artefacts d’exécution et les résumés de la suite de tests, plutôt que d’être traités comme des constantes contrôlées. L’objectif est la reproductibilité : lorsqu’un chiffre varie, nous voulons savoir si ce changement est dû à une modification du modèle ou du harnais, et non à une dérive de l’évaluation.

Le rapport : ce que DashBench montre réellement

Nous utilisons DashBench pour répondre à quatre questions auxquelles la télémétrie de production ne permet pas à elle seule de répondre. Premièrement, la mise en place d’étapes intermédiaires permet-elle réellement d’améliorer la couverture ? Deuxièmement, quels choix de modèles d’évaluateurs/relecteurs modifient la frontière entre coût, précision et rappel ? Troisièmement, les classements généraux restent-ils valables lorsque l’on ventile les résultats par niveau de gravité ? Quatrièmement, dans quels cas les configurations par étapes d’évaluateurs/relecteurs surpassent-elles des relecteurs plus performants travaillant en un seul passage, et quels compromis doivent-elles accepter pour y parvenir ?

Avantages de la mise en scène

La mise en scène garantit une couverture, et son coût est visible. Sur le rapport portant sur 105 cas, le réviseur de code de production de DoorDash (Claude Sonnet 4.6 « high scout » + Claude Opus 4.8 « high reviewer ») a identifié 504 résultats réels avec un rappel pondéré de 53,6 %, contre 164 résultats réels et un rappel pondéré de 30,7 % pour la référence GPT 5.5 « high » sans scout. La précision pondérée est restée du même ordre de grandeur, à 87,0 % contre 84,1 %, mais le réviseur de production a coûté plus cher par PR et a pris plus de temps. C’est exactement le genre de résultat que nous voulons que DashBench mette en évidence : non pas un trophée, mais un compromis mesuré.

SystèmeConclusions réellesPrécision pondéréeRappel pondéréCoût / Relations publiques
Réviseur de code de production chez DoorDash50487.0%53.6%$3.91
Pas de scout + Évaluateur GPT 5,5 (niveau élevé)16484.1%30.7%$0.75
Pas de scout + Claude Opus 4.8, critique très bien noté11589.8%20.2%$0.65

Choix du modèle de scout/évaluateur

C’est dans la comparaison entre différentes combinaisons de modèles que le benchmark révèle toute son utilité : nous inversons les rôles des modèles (l’un en tant que « scout » et l’autre en tant que « reviewer ») tout en conservant le benchmark inchangé. Aucune configuration ne domine sur tous les axes. La combinaison Kimi K2.6 (scout) + Claude Fable 5 (reviewer) a obtenu les meilleurs résultats en termes de rappel pondéré et d’indice F1 sur le sous-ensemble valide de 105 cas, avec respectivement 65,2 % de rappel pondéré et 75,3 % d’indice F1 pondéré. La combinaison Composer 2.5 (modèle de repérage) + GPT 5.5 medium (modèle d’évaluation) a affiché la précision pondérée la plus élevée, à 92,2 %, mais avec un rappel nettement inférieur. Les modèles de référence sans modèle de repérage étaient moins coûteux, tandis que la combinaison Kimi K2.6 (modèle de repérage) + Claude Opus 4.8 high (modèle d’évaluation) constituait une alternative par étapes moins coûteuse, mais avec un rappel nettement inférieur.

ConfigurationConclusions réellesPrécision pondéréeRappel pondéréF1 pondéréeCoût / Relations publiques
Kimi K2.6 (testeur) + Claude Fable 5 (critique)53789.2%65.2%75.3%$3.81
Claude Sonnet 4,6 – scout de haut niveau + Claude Opus 4,8 – critique de haut niveau50487.0%53.6%66.3%$3.91
Kimi K2.6 « scout » + Claude Opus 4.8 « high reviewer »39682.3%45.8%58.9%$2.35
Claude Sonnet 5 : scout de haut niveau + Claude Sonnet 5 : critique de haut niveau32177.3%40.1%52.8%$6.55
Claude Sonnet : note de 5 sur 5 en tant qu'explorateur + Claude Opus : note de 4,8 sur 5 en tant que critique22680.8%32.9%46.8%$5.06
GPT 5.5 « scout » de niveau intermédiaire + GPT 5.5 « réviseur » de niveau avancé27691.5%19.9%32.6%$5.95
Composer 2,5 (scout) + GPT 5,5 (évaluateur de haut niveau)26791.1%19.6%32.2%$4.68
Composer 2,5 (scout) + GPT 5,5 (évaluateur de niveau intermédiaire)24692.2%18.0%30.1%$3.53
Pas de scout + Évaluateur GPT 5,5 (niveau élevé)16484.1%30.7%45.0%$0.75
Pas de scout + Claude Opus 4.8, critique très bien noté11589.8%20.2%33.0%$0.65

Incidence de la gravité des résultats sur l'évaluation

Le niveau de gravité change encore la donne. Sur le sous-ensemble valide de 105 cas, l’ensemble des résultats réels validés comprend 40 clusters critiques, 136 clusters de gravité élevée, 271 clusters de gravité moyenne et 385 clusters de faible gravité. La combinaison Kimi K2.6 (scout) + Claude Fable 5 (réviseur) s’est montrée la plus performante pour la couverture des clusters critiques, de gravité élevée et de gravité moyenne, tandis que la combinaison Claude Sonnet 4.6 (scout) + Claude Opus 4.8 (réviseur) a couvert un peu plus la partie des clusters de faible gravité. La configuration « GPT 5.5 high » sans scout était peu coûteuse et restait utile pour les problèmes de gravité élevée, mais moins performante en termes de couverture globale. C’est la raison d’être du score pondéré : il accorde plus d’importance aux omissions critiques et de gravité élevée qu’aux omissions de faible gravité, tout en nous permettant d’examiner la répartition complète par niveau de gravité.

Figure 4 : Taux de rappel en fonction de la gravité.

La vue « Gravité » montre pourquoi un seul score ne suffit pas : des configurations qui semblent similaires dans l'ensemble peuvent présenter des problèmes de nature très différente.

Influence de la qualité et de la taille du modèle sur les résultats des tests comparatifs

Les données issues du « model-mix » confirment cette conclusion en chiffres : la combinaison Kimi K2.6 (scout) + Claude Fable 5 (reviewer) domine en termes de rappel pondéré et de F1 ; la combinaison Composer 2.5 (scout) + GPT 5.5 (reviewer) moyen domine en termes de précision pondérée ; enfin, les modèles de référence à passage unique sans scout restent moins coûteux, mais au détriment de la couverture. Une configuration sur mesure ne transforme pas comme par magie un modèle plus faible en un modèle performant dans tous les domaines ; elle modifie simplement la nature du compromis. Les « scouts » améliorent l’étendue lorsque le « reviewer » peut effectuer une vérification rigoureuse. Des configurations de « reviewer » plus strictes améliorent la précision lorsque l’objectif métier est de réduire le bruit. Les modèles de référence à passage unique sans « scout » constituent un seuil utile et peu coûteux, mais ils laissent de la couverture sur la table. Le résultat n’est pas qu’un modèle l’emporte. Le résultat est qu’aucune configuration unique ne domine, et c’est précisément cela qui importe.

Enseignements tirés de l'analyse comparative des rapports de relations publiques

La confiance se mérite. Aucun signal pris isolément ne constitue une source fiable de vérité pour une évaluation comparative dans le monde réel. Les étiquettes humaines, l’acceptation en production et le jugement des agents ont chacun apporté leur contribution, et chacun s’est trompé suffisamment souvent pour que le fait de considérer l’un d’entre eux comme la « vérité absolue » aurait discrètement faussé les résultats. La qualité d’une évaluation comparative réside précisément dans son refus de se fier uniquement à une donnée faillible.

Les humains ne sont pas capables de traiter des volumes importants. L'attention qu'ils peuvent consacrer à l'étiquetage diminue rapidement lorsqu'il s'agit de tâches complexes, et ce d'autant plus lorsqu'il y en a plusieurs, même lorsque la personne chargée de l'étiquetage est le même ingénieur qui a rédigé ou révisé la pull request d'origine. C'est grâce à l'évaluation par l'IA que l'étiquetage à cette échelle devient gérable, mais uniquement dans le respect des contraintes abordées dans les deux leçons suivantes.

La variance est une caractéristique, pas un bug. Les LLM sont non déterministes ; ainsi, plusieurs exécutions d’un même agent font émerger des résultats valides supplémentaires, ce qui signifie qu’une seule exécution sous-estime la couverture réelle d’un agent, et qu’il faut effectuer plusieurs exécutions et agréger les résultats pour l’évaluer de manière honnête. Cette même stochasticité s’applique au juge : l’appariement déterministe est stable mais ne tient pas compte de l’équivalence sémantique, tandis que les juges basés sur les LLM raisonnent de manière plus nuancée mais nécessitent un calibrage, des ensembles d’audit et des grilles d’évaluation stables pour rester fiables. L’ensemble du processus de mesure est stochastique ; le travail consiste à concevoir en tenant compte de cette réalité plutôt que de faire comme si elle n’existait pas.

Lorsqu’un indicateur ne suffit pas, il faut en prendre plusieurs en compte. Un score unique est, par nature, trompeur. La précision pondérée, le rappel pondéré, l’indice F1 pondéré, les résultats non réels, le rappel élevé/critique, la latence et le coût évoluent tous de manière indépendante ; un système peut exceller dans un domaine et échouer dans un autre au cours d’un même test. Par conséquent, le terme « meilleur » n’a de sens que si l’on précise « meilleur en quoi ».

Restez informé grâce aux mises à jour hebdomadaires hebdomadaires

Abonnez-vous à notre blog d'ingénierie pour obtenir des mises à jour régulières sur tous les projets les plus intéressants sur lesquels notre équipe travaille. projets les plus intéressants sur lesquels notre équipe travaille

Prochaines étapes

DashBench a été créé pour nous permettre de continuer à améliorer le système de révision en nous appuyant sur des données concrètes plutôt que sur des anecdotes. L’objectif n’a jamais été d’établir un classement statique ; il s’agit d’une boucle de rétroaction dans laquelle chaque modification significative apportée au modèle, à la consigne, au contexte, à l’utilisation des outils, au flux de travail ou au budget d’exécution est testée sur les mêmes cas réels de révision de PR avant d’ être transmise à un ingénieur.

La prochaine étape consiste en une analyse comparative continue :

  • Les nouveaux modèles et harnais sont rapidement intégrés à la base de référence dès leur mise sur le marché, y compris les harnais d'agents supplémentaires que nous avons prévus pour cette phase.
  • Les cas de relations publiques obsolètes sont retirés de l'ensemble de données tandis que de nouveaux cas y sont ajoutés en continu ; ainsi, le benchmark suit l'évolution du code source au lieu de s'en éloigner.
  • Nous passons d'un juge unique à un jury agentique, afin d'atténuer les biais entre les différents modèles de juges.
  • Nous comparons nos solutions à des solutions externes de révision de code, et pas seulement à des variantes internes.

Et nous commençons à évaluer les agents de codage, et pas seulement les relecteurs, sur une base de code d'entreprise réelle, avec des tâches et des fonctionnalités concrètes. Nous y reviendrons plus tard.

En résumé, voici la vérité : rendre un système d’IA utile ne représente que la moitié du travail. Le plus difficile est de savoir où il échoue, pourquoi il échoue, et si la modification suivante l’a amélioré ou simplement rendu différent. La plupart des acteurs du secteur continuent de mesurer la mauvaise unité avec le mauvais chiffre. DashBench est notre tentative de mesurer ce qui compte vraiment : des résultats concrets, sur de véritables PR, en laissant apparaître les compromis. Si c’est le genre de problème sur lequel vous souhaitez travailler, nous serions ravis d’en discuter avec vous.


Annexe

Une section dédiée aux détails qui vient étayer l'article sans alourdir le texte. La section « Rapport » permet au lecteur de rester concentré sur les compromis ; cette section conserve la piste d'audit.

A. Environnement d'exécution (spécifications complètes). Dans le cadre d'une comparaison, chaque configuration s'exécute sur le même sous-ensemble de données et produit le même contrat de résultats structuré : gravité, preuves et repères de fichiers. Le harnais lui-même fait partie de ce que mesure DashBench ; les profils peuvent donc différer en termes de type de moteur d’exécution, de répartition entre « scout » et « réviseur », de choix de modèle, d’interface utilisateur, de mécanismes contextuels et de limites imposées par les fournisseurs. Le calcul du score utilise ensuite le même chemin de correspondance pour cette comparaison, avec une correspondance déterministe lorsque cela est possible et une évaluation subjective lorsque la correspondance sémantique est nécessaire. L’objectif n’est pas d’effacer les différences entre les systèmes, mais de rendre ces différences suffisamment explicites pour que les variations de coût, de latence et de qualité s’expliquent par des raisons interprétables.

B. Taille de l’ensemble de données et de l’ensemble d’évaluation. DashBench a commencé avec environ 1 000 candidatures PR brutes, puis a restreint son champ d’analyse aux cas pouvant être reproduits et évalués. Cet article utilise le rapport valide portant sur 105 cas pour l’analyse principale portant sur la combinaison de modèles et la comparaison entre l’approche par étapes et l’approche en un seul passage. Le dénominateur de gravité dans cette sélection de 105 cas correspond à l’union des groupes de résultats réels : 40 cas critiques, 136 cas élevés, 271 cas moyens et 385 cas faibles. Il s’agit de groupes de résultats, et non du nombre de PR ; un même PR peut contribuer à plusieurs groupes.

C. Indicateurs de configuration complets. Le corps du texte présente la version abrégée, car c'est celle qui est la plus lisible. Les indicateurs complets sont indiqués ci-dessous à des fins d'auditabilité ; ils ont été fractionnés afin de tenir sur la page.

MétriqueRéviseur de code de production chez DoorDashPas de scout + Évaluateur GPT 5,5 (niveau élevé)
ConfigurationClaude Sonnet 4,6 – scout de haut niveau + Claude Opus 4,8 – critique de haut niveauUn seul évaluateur
Résultats bruts611200
Conclusions réelles504164
Précision pondérée87.0%84.1%
Rappel pondéré53.6%30.7%
F1 pondérée66.3%45.0%
Rappel élevé/critique52.8%51.7%
Coût / Relations publiques$3.91$0.75
Coût / constat réel$0.82$0.48
Latence de révision / PR725.0s170.3s
AvertissementCoût et latence plus élevés, mais couverture plus étendueCoût et latence réduits, mais un taux de rappel global nettement inférieur

ConfigurationConclusionsQualitéCoût / latence
Kimi K2.6 (testeur) + Claude Fable 5 (critique)669 bruts
537 réels
132 non réels
Précision de 89,2 %
Rappel de 65,2 %
F1 de 75,3 %
3,81 $ / PR
0,75 $ / réel
589,3 s / PR
Meilleur rappel pondéré/F1
Claude Sonnet 4,6 – scout de haut niveau + Claude Opus 4,8 – critique de haut niveau611 bruts
504 réels
107 non réels
Précision de 87,0 %
Rappel de 53,6 %
F1 de 66,3 %
3,91 $ / PR
0,82 $ / réel
725,0 s / PR
Kimi K2.6 « scout » + Claude Opus 4.8 « high reviewer »574 bruts
396 réels
178 non réels
Précision de 82,3 %
Rappel de 45,8 %
F1 de 58,9 %
2,35 $ / PR
0,62 $ / réel
263,9 s / PR
Temps le plus rapide mesuré en configuration par étapes
Claude Sonnet 5 : scout de haut niveau + Claude Sonnet 5 : critique de haut niveau509 brut
321 réel
187 non réel
1 indéterminé
77,3 % de précision
40,1 % de rappel
52,8 % de F1
6,55 $ / PR
2,14 $ / réel
657,9 s / PR
Claude Sonnet : note de 5 sur 5 en tant qu'explorateur + Claude Opus : note de 4,8 sur 5 en tant que critique327 brut
226 réel
100 non réel
1 indéterminé
Précision de 80,8 %
Rappel de 32,9 %
F1 de 46,8 %
5,06 $ / PR
2,35 $ / réel
565,3 s / PR
GPT 5.5 « scout » de niveau intermédiaire + GPT 5.5 « réviseur » de niveau avancé332 bruts
276 réels
56 non réels
Précision de 91,5 %
Rappel de 19,9 %
F1 de 32,6 %
5,95 $ / PR
2,26 $ / réel
619,5 s / PR
Composer 2,5 (scout) + GPT 5,5 (évaluateur de haut niveau)324 bruts
267 réels
57 non réels
Précision de 91,1 %
Rappel de 19,6 %
F1 de 32,2 %
4,68 $ / PR
1,84 $ / réel
539,2 s / PR
Composer 2,5 (scout) + GPT 5,5 (évaluateur de niveau intermédiaire)285 bruts
246 réels
39 non réels
Précision de 92,2 %
Rappel de 18,0 %
F1 de 30,1 %
3,53 $ / PR
1,50 $ / réel
429,4 s / PR
Meilleure précision pondérée
Pas de scout + Évaluateur GPT 5,5 (niveau élevé)200 bruts
164 réels
36 non réels
Précision de 84,1 %
Rappel de 30,7 %
F1 de 45,0 %
0,75 $ / PR
0,48 $ / réel
170,3 s / PR
Pas de scout + Claude Opus 4.8, critique très bien noté134 bruts
115 réels
19 non réels
Précision de 89,8 %
Rappel de 20,2 %
F1 de 33,0 %
0,65 $ / PR
0,60 $ / réel
112,8 s / PR

D. Répartition par niveau de gravité (complète). La vue par niveau de gravité est la raison la plus évidente pour laquelle l'indicateur global pondéré existe. Les écarts « critiques » et « élevés » ne sont pas comparables aux écarts de faible gravité, et les configurations évoluent différemment selon le niveau.

Rappel par niveau de gravité

GravitéGrappes réelles d'unionKimi K2.6 + Fable 5Sonnet 4.6 haut + Opus 4.8 hautKimi K2.6 + Opus 4.8 hautGPT 5.5 haut
Critique4080.0%62.5%72.5%37.5%
Élevé13677.9%50.0%46.3%55.9%
Moyen27156.8%53.5%43.2%20.3%
Faible38546.8%51.4%26.8%4.2%

Précision en fonction du degré de gravité

GravitéKimi K2.6 + Fable 5Sonnet 4.6 haut + Opus 4.8 hautKimi K2.6+ Opus 4.8 hautGPT 5.5 haut
Critique100.0%100.0%100.0%n/a
Élevé96.0%95.6%94.9%86.9%
Moyen94.2%87.8%86.8%80.9%
Faible65.3%76.8%49.7%70.4%

Les intentions de tâches comportent plusieurs étiquettes ; c'est pourquoi le total des comptes dépasse 105.

Intentions de tâche

Objectif de la tâcheComtePourcentage de cas
tests9085.7%
config_build8581.0%
fonctionnalité4139.0%
api_schema3331.4%
docs_kb2019.0%
outils_de_gestion_des_dépendances1918.1%
correction de bug1817.1%
modèle_de_données1514.3%
refactor_cleanup1514.3%
ui109.5%
rollout_guard98.6%
sécurité_confidentialité87.6%
observabilité76.7%
performance65.7%
changement_de_comportement11.0%

Modifier la taille du communiqué de presse

Classe de taille PRComtePourcentage de cas
grand3634.3%
moyen3634.3%
petit3331.4%

Vérifiabilité

VérifiabilitéComtePourcentage de cas
moyen5451.4%
strong3836.2%
faible1312.4%

Domaines de produits

Les 20 domaines les plus populaires sont affichés.

Domaine de produitsComtePourcentage de cas
flux_consommateurs1817.1%
fournitures_de_campagne65.7%
logistique_main-d'œuvre54.8%
panier_commande54.8%
support_entonnoir_de_support43.8%
risque_de_fraude_principal32.9%
logistique_exécution des commandes32.9%
argent_versé32.9%
contenu_découverte_consommateurs21.9%
hélios21.9%
merchant_mdh21.9%
commandes_des_commerçants21.9%
assistance_aux_commerçants21.9%
service_de_gestion_des_commerçants21.9%
platform_pretzel21.9%
public21.9%
repo_config21.9%
support_automatisation_agent_IA21.9%
support_voice21.9%
outils21.9%

Impact

Les étiquettes « Impact » sont des étiquettes multiples ; leur nombre total dépasse donc 105.

ImpactComtePourcentage de cas
en contact avec la clientèle3432.4%
merchant_ops3331.4%
fiabilité_des_infrastructures3028.6%
logistique2221.0%
intégrité des données1817.1%
connaissances_des_développeurs1615.2%
inconnu1615.2%
qualité_du_test98.6%
sécurité_confidentialité87.6%
argent76.7%

DoorDash dessert un ensemble vaste et diversifié de commerçants, chaque restaurant, menu et plat étant présenté de manière unique. Un catalogue de plats de haute qualité constitue la base des recherches des clients et de l’expérience de personnalisation, et représente un facteur clé du succès des restaurants. Contrairement aux catalogues standardisés, la gastronomie est profondément contextuelle, riche sur le plan culturel et très peu standardisée ; un même plat peut être décrit de multiples façons, tandis que des plats totalement différents peuvent partager des noms, des descriptions ou des images similaires.  À cela s’ajoute le fait qu’à l’échelle de DoorDash, il existe des millions de produits uniques et les menus sont constamment mis à jour. Cette variabilité et ce volume rendent extrêmement difficile la génération de métadonnées fiables à l’aide des approches traditionnelles. 

Pour y remédier, nous avons développé une plateforme de métadonnées pour les restaurants basée sur l’IA. Notre plateforme déduit des attributs au niveau des plats et des établissements — par exemple, si un plat est épicé ou si la cuisine d’un restaurant est chinoise — à partir de signaux multimodaux issus de textes, d’images et de recherches Web à grande échelle. Afin de créer des métadonnées fiables et précises à grande échelle, nous avons mis au point plusieurs innovations clés au sein du système complexe de DoorDash, notamment : 

  • Un système de notation basé sur un modèle linguistique à grande échelle (LLM) permettant une évaluation de haute qualité, qui a permis d'augmenter la précision de l'annotation d'environ 20 % par rapport à celle obtenue par des évaluateurs humains classiques.
  • Des agents d'optimisation contextuelle permettant d'améliorer de manière itérative les prompts en quelques minutes, ce qui augmente la précision du modèle de plus de 20 % tout en évitant l'inefficacité des prompts créés manuellement et sous-optimaux. Ce cycle a multiplié par dix la vitesse de développement des prompts.
  • Le calcul distribué permet d'effectuer des inférences à grande échelle sur des modèles de langage de grande envergure (LLM), réduisant ainsi le temps de mise à jour de plus d'un mois à seulement quelques jours, ce qui rend opérationnellement viable la génération de millions d'éléments.
  • Une annotation pilotée par l'IA pour générer des données d'entraînement, ce qui a permis de débloquer le processus de réglage fin afin d'atteindre la qualité des grands modèles de langage (LLM) de pointe, pour un coût d'inférence réduit à 10 % et sans aucun effort d'annotation humaine.

Cette plateforme de métadonnées nous permet de déployer avec succès l'IA générative de manière fiable et rentable à grande échelle, ce qui améliore notre processus d'ingénierie ainsi que l'expérience client de DoorDash. 

Présentation générale du processus

Comme le montre la figure 1, notre processus commence par la collecte des mises à jour des menus et la déduplication afin de minimiser les coûts d’inférence. Nous transmettons ces éléments à des générateurs d’IA pour produire des métadonnées, qui font l’objet d’une validation structurelle immédiate visant à détecter les erreurs et à effectuer de nouvelles tentatives. Nous surveillons en permanence la qualité des prédictions générées via un « jury » de modèles de langage (LLM) ; le résultat de cette évaluation est également utilisé pour l’ingénierie contextuelle afin d’améliorer la qualité de la génération. De plus, nous proposons un mécanisme de « override » permettant aux commerçants de valider ou de corriger les attributs.

Figure 1 : Cet aperçu général du processus de génération des métadonnées alimentaires de DoorDash inclut la mise à jour des menus et la déduplication grâce à la génération par IA, à l'évaluation et aux modifications apportées par les commerçants.

Innovations techniques

Les méthodes traditionnelles de collecte, d’étiquetage, d’entraînement et d’évaluation des données sont d’une lenteur et d’un coût prohibitifs, ce qui rend difficile la génération et l’extraction de métadonnées de haute qualité à l’échelle requise par DoorDash. Pour remédier à cela, nous avons développé un système qui utilise à la fois des modèles linguistiques multimodaux et des petits modèles linguistiques (SLM) entraînés afin d’obtenir une génération de haute qualité et à faible latence, à un coût raisonnable. Notre système « LLM Jury », soigneusement conçu, permet une évaluation fiable à grande échelle, une optimisation continue du contexte à partir de signaux d’échec réels, ainsi que la génération automatisée de données annotées de haute qualité afin d’accélérer l’amélioration des modèles et l’entraînement en interne.

Des évaluations efficaces et de grande qualité grâce à des jurys composés de modèles de langage de grande capacité (LLM)

La validation des balises générées par un étiquetage humain n’est pas envisageable dans le cadre d’une production à grande échelle. Seul un petit groupe d’experts du domaine est capable d’appliquer des balises d’une manière qui corresponde à la façon dont les clients prennent réellement leurs décisions d’achat, et encore moins d’experts comprennent de manière fiable les nuances entre les différentes cuisines et le langage des menus — par exemple, distinguer les plats népalais de ceux du nord de l’Inde, ou déterminer si l’expression « à la manière du Sichuan » implique un profil de piquant. Par conséquent, étendre la validation humaine à des millions d’articles devient d’un coût prohibitif et irréalisable sur le plan opérationnel ; l’évaluation traditionnelle ne convient tout simplement pas à la génération continue de métadonnées à grand volume.

Notre système automatisé d’évaluation consensuelle basé sur les modèles de langage de grande envergure (LLM) — les « jurys LLM » — remplace la validation humaine, qui est lente, coûteuse et incohérente. Comme le montre la figure 2, il comprend les étapes suivantes :

  • Évaluation consensuelle des grands modèles de langage (LLM) : plusieurs évaluateurs LLM performants jugent de manière indépendante chaque balise proposée, au lieu de s'appuyer sur un seul modèle ou un seul étiqueteur humain.
  • Vote et synthèse : chaque évaluateur émet un verdict et en expose les motifs ; les votes sont synthétisés pour aboutir à une décision consensuelle unique.
  • Vérification au niveau des balises : les évaluateurs vérifient chaque balise individuellement — par exemple, « protéine », « préparation » ou « santé » — plutôt que d'évaluer l'élément dans son ensemble. Les balises vérifiées sont enregistrées et utilisées dans la base de données.

Figure 2 : Le système d'évaluation par un jury de modèles de langage (LLM) fait appel à plusieurs évaluateurs LLM performants pour juger de manière indépendante les balises proposées. Nous regroupons les votes et vérifions les balises une par une.

Nous avons constaté que les balises LLM issues du consensus étaient environ 20 % plus précises que les balises classiques annotées par des humains. Le succès de notre cadre d'évaluation automatisé a joué un rôle fondamental dans l'automatisation de l'ensemble du système de génération de métadonnées.

Optimisation automatique du contexte inspirée de l'apprentissage par renforcement 

Notre système utilise des modèles de langage basés sur la vision pour améliorer la qualité et l’efficacité de la génération de métadonnées alimentaires. S’il peut être facile de fournir un contexte à une instruction pour générer certaines balises, la génération de balises hautement précises à grande échelle est bien plus difficile et ne peut être réalisée en une seule instruction. Même avec des ingénieurs hautement qualifiés, l’ingénierie manuelle du contexte est lente, fragile et imprévisible. De légères modifications de formulation, qui semblent équivalentes pour les humains, peuvent entraîner des comportements très différents du modèle, et la gestion des cas limites nécessite des essais et des erreurs répétés. À mesure que de nouveaux modèles d’éléments et des cas limites apparaissent, le maintien de la qualité des invites devient un effort manuel continu et impossible à faire évoluer.

Figure 3 : La boucle d'optimisation contextuelle utilise les signaux d'échec provenant d'ensembles de données d'évaluation de haute qualité pour proposer et tester des modifications des prompts, améliorant ainsi la qualité du modèle de manière itérative.

En nous inspirant de l’apprentissage par renforcement, nous avons développé une boucle autonome, illustrée à la figure 3, qui a permis de multiplier par dix la vitesse de développement du contexte des prompts. Nous définissons la récompense de la tâche en fonction des performances du modèle sur un ensemble de données d’évaluation de haute qualité. Un agent de réglage identifie les points faibles de la prompt actuelle et utilise ces signaux d’échec pour proposer un meilleur contexte au modèle. À chaque étape, des métriques sont générées à partir d’un ensemble de données d’évaluation de haute qualité. Celles-ci font office de garde-fous, garantissant que le système améliore en permanence la précision et le rappel globaux. Nous optimisons la prompt elle-même, plutôt que de mettre à jour les poids du modèle, ce qui rend la boucle beaucoup plus rapide et moins coûteuse à exécuter.

Nous avons privilégié cette approche axée sur les signaux d'échec plutôt que les méthodes évolutives basées sur une population, telles que l'algorithme GEPA, qui gèrent une population de prompts candidats et s'appuient sur des opérateurs de mutation et de croisement pour explorer l'espace des prompts. Plutôt que d'évaluer aveuglément de nombreuses variantes de prompts à chaque génération, notre agent analyse directement les cas d'échec et propose des modifications ciblées des règles. Cela confère à chaque itération un objectif précis plutôt qu'un caractère probabiliste, et réduit le nombre de cycles d'évaluation nécessaires, sans nécessiter de réglage des hyperparamètres de la population.

Voici quelques enseignements importants que nous aimerions partager :

  • La qualité des données est essentielle pour cette tâche : l'ensemble de données d'évaluation utilisé pour noter les propositions de prompts détermine directement la direction de l'optimisation. Des exemples de mauvaise qualité ou mal étiquetés poussent l'agent à se concentrer sur le bruit plutôt que sur le signal réel, ce qui se traduit par des prompts de qualité inférieure ou instables.
  • Les cas d'échec sont plus révélateurs que les cas de réussite : tout au long du développement, nous avons testé différentes combinaisons de cas d'échec et de réussite. Nous avons constaté que le fait d'accorder davantage d'importance aux cas d'échec s'avérait la solution la plus efficace pour notre cas d'utilisation.
  • Optimisation des prompts et des poids du modèle : l'optimisation des prompts suit la même dynamique de convergence que l'entraînement du modèle ; une IA peut accomplir en quelques heures ce qui prendrait des jours, voire des semaines, à un être humain. 

Cette approche transforme l'ingénierie contextuelle, qui était jusqu'alors une tâche ponctuelle et reposant sur l'intervention humaine, en un processus d'optimisation évolutif et mesurable, capable de s'adapter à l'évolution des données et des cas d'utilisation. Nous avons constaté une augmentation de la précision de plus de 20 % dans nos cas d'étude, sur un ensemble de données d'évaluation de validation.

Annotation de données pilotée par l'IA pour accélérer la collecte de données d'entraînement

La génération de métadonnées à l’échelle de DoorDash exige à la fois précision et efficacité. Les grands modèles de langage (LLM) disponibles sur le marché sont souvent soit imprécis, soit d’un coût prohibitif ; par conséquent, une partie de notre système d’IA repose sur des modèles hautement spécialisés et finement ajustés. L’entraînement de ces modèles nécessite toutefois l’annotation de milliers de balises sur des milliards d’entités du catalogue, ce qui fait de l’étiquetage des données l’une des principales contraintes du cycle de développement. Dans un flux de travail traditionnel, la production d’un tel volume de données d’entraînement et d’évaluation repose largement sur l’annotation humaine, ce qui rend l’itération des modèles lente, coûteuse et difficile à faire évoluer.

Figure 4 : Notre système d'annotation basé sur l'IA utilise des agents dédiés à la génération et à l'évaluation afin de créer et de valider efficacement des étiquettes de haute qualité destinées à l'entraînement de modèles spécialisés et finement ajustés.

Pour remédier à cela, nous avons mis au point un système d’annotation de données basé sur l’IA, présenté à la figure 4, qui génère et valide des étiquettes de haute qualité. Nous avons également développé un ensemble similaire d’agents chargés de l’optimisation automatique du contexte, de la génération et de l’évaluation, spécialement conçus pour les tâches d’annotation. Nos petits modèles finement ajustés ont permis de réduire les coûts d’inférence d’environ 90 % par rapport aux grands modèles de langage (LLM), tout en offrant des performances équivalentes.

Optimisation de l'inférence à grande échelle des modèles LLM

Compte tenu de l'ampleur des activités de DoorDash, les millions de plats uniques, les milliards d'options de menu et les centaines de milliers de mises à jour quotidiennes nécessitent un rafraîchissement continu des métadonnées. S'appuyer sur des appels API synchrones, effectués élément par élément, pour effectuer un rattrapage complet prendrait des semaines, ce qui rendrait les mises à jour quotidiennes irréalisables, tout en augmentant les coûts liés à l'infrastructure et aux modèles. 

Figure 5 : Notre pipeline d'inférence LLM distribué utilise la déduplication, la distribution Spark, le traitement par lots et le remappage des résultats pour transformer la génération à grande échelle, qui constituait auparavant un goulot d'étranglement, en un processus efficace et à haut débit.

Pour relever ces défis, nous avons conçu un pipeline d'inférence LLM distribué, illustré à la figure 5, afin d'éliminer les calculs redondants et d'optimiser le débit. Notre approche repose sur quatre mécanismes clés :

  • Déduplication : de nombreux commerçants utilisent des noms et des descriptions d'articles identiques ; un traitement simpliste enverrait à plusieurs reprises les mêmes données au modèle. Nous procédons à la déduplication en nous basant sur des correspondances exactes entre les caractéristiques, ce qui permet d'éviter les appels redondants au modèle.
  • Répartition dans Spark : nous découpons les données uniques restantes en lots et les répartissons entre les nœuds de travail du cluster Spark afin de les traiter en parallèle.
  • Traitement par lots : nous utilisons des API LLM par lots pour envoyer des charges utiles regroupées au modèle, ce qui nous permet d’optimiser le débit et la rentabilité. Pour les modèles entraînés, nous fragmentons les données, ce qui nous permet de relancer le traitement sur plusieurs GPU.
  • Remappage des résultats : après traitement, nous remappons les résultats du modèle vers les entités d'origine afin de préserver l'intégrité des données.

Ensemble, ces optimisations transforment la génération de métadonnées à grande échelle, qui constituait auparavant un goulot d'étranglement lent et coûteux, en un pipeline hautement efficace, évolutif et rentable, réduisant ainsi le délai de rattrapage de plus d'un mois à seulement quelques jours.

Pour une meilleure expérience client 

Nos métadonnées constituent la base de nombreuses applications en aval sur la plateforme DoorDash. En transformant le texte non structuré des menus en attributs précis et structurés, nous ouvrons la voie à de nombreuses nouvelles fonctionnalités, notamment l'amélioration de la recherche et de la découverte par les clients, le filtrage des plats pertinents, la personnalisation et la génération d'indicateurs à des fins d'analyse, comme le montre la figure 6 :

Figure 6 : Les métadonnées structurées constituent la couche de base des applications DoorDash en aval, permettant la personnalisation pour les clients, le filtrage et une recherche améliorée.

Conclusion

La plateforme d’IA dédiée aux métadonnées marque un tournant fondamental dans la manière dont DoorDash appréhende les produits et les commerces présents sur sa plateforme. Grâce à l’IA, nous avons développé une compréhension sémantique approfondie de chaque produit et de chaque commerce, transformant ainsi des données d’entrée incohérentes et non structurées en attributs riches, précis et structurés. En combinant un pipeline de génération de données distribué à grande échelle avec une évaluation automatisée rigoureuse et des mécanismes de sécurité impliquant une intervention humaine, nous avons démontré que l’IA générative peut être déployée de manière fiable et rentable dans des environnements de production à haut volume. L’infrastructure de métadonnées qui en résulte ne se contente pas d’alimenter les fonctionnalités actuelles de recherche et de découverte ; elle établit une base de données robuste et de haute qualité qui ouvrira la voie à la prochaine génération d’expériences personnalisées, tant pour les commerçants que pour les consommateurs.

Dans la suite de notre précédente présentation technique d'Ask DoorDash, ce troisième article de la série se penche en détail sur le cadre d'évaluation qui sous-tend le système. Des analyses approfondies de la plateforme et de l'expérience utilisateur suivront.


Introduction

Il est difficile de développer un agent d’IA performant lorsque la qualité ne peut être évaluée qu’à partir de rapports épars et de vérifications manuelles. C’est le problème auquel nous avons été confrontés avec Ask DoorDash, notre nouvelle expérience de commande automatisée récemment lancée. Au début du développement, l’évaluation reposait principalement sur les retours des employés et sur des tests manuels. Ces indicateurs étaient utiles, mais ils étaient rares et biaisés en faveur des scénarios que nous savions déjà devoir surveiller.

Nous avons mis au point un dispositif d’évaluation permettant de mesurer la qualité des agents à grande échelle. L’indicateur de qualité est ainsi passé d’une moyenne d’un commentaire soumis par un employé à 2 000 sessions notées automatiquement par jour. Cet indicateur plus complet nous a permis de détecter plus rapidement les défaillances des agents susceptibles de nuire à la confiance des clients et de hiérarchiser les modes de défaillance récurrents ; grâce à ces mesures, nous avons amélioré de 8 points les scores de qualité des agents avant même le lancement national, réduisant ainsi les taux d’erreur de près de moitié et atteignant notre objectif de lancement en production. Le dispositif d’évaluation a également considérablement accéléré la validation avant la mise en production : un test de régression complet qui prenait auparavant plus de 6 heures à réaliser manuellement s’exécute désormais en environ 20 minutes, ce qui permet d’évaluer de manière pratique des changements aussi importants qu’une migration vers un modèle de base qui a réduit la latence de 35 % tout en préservant la qualité.

Cet article explique comment nous avons développé cet ensemble d'outils d'évaluation : les grilles d'évaluation qui définissent les critères de réussite, l'outil de création de transcriptions qui reconstitue les sessions, le simulateur qui génère des exécutions hors ligne reproductibles, ainsi que le juge LLM calibré qui rend l'évaluation au niveau des sessions évolutive.

Les défis fondamentaux

Ask DoorDash aide les utilisateurs à découvrir des restaurants ou à faire leurs courses grâce à des conversations à plusieurs échanges. En coulisses, l’agent utilise des outils pour interagir avec le système DoorDash et agir au nom de l’utilisateur. Cela signifie que la qualité du travail de l’agent doit être évaluée sur l’ensemble de l’interaction, et non sur la base d’une seule réponse. L’évaluation doit prendre en compte à la fois les messages destinés à l’utilisateur et les appels aux outils qui ne sont pas visibles.

Figure 1 : L'évaluation doit tenir compte à la fois de la conversation visible et de la trajectoire cachée de l'outil.

La mise au point d'un cadre d'évaluation pour Ask DoorDash nécessite de transformer le problème ouvert de la qualité des agents en exigences système concrètes. Le tableau ci-dessous résume les principaux défis que nous avons dû relever et explique comment chacun d'entre eux a influencé la conception.

DéfiExigence
Objectifs ouverts. Un objectif utilisateur peut avoir plusieurs résultats acceptables ; il n'y a que rarement une seule bonne réponse. Formulez chaque objectif sous forme de grille d'évaluation : des critères suffisamment précis pour permettre une évaluation cohérente à chaque fois, mais suffisamment larges pour prendre en compte les différentes approches valables qu'une tâche peut permettre.
Visibilité sur l'exécution. La qualité d'un agent ne peut être évaluée uniquement à partir de la conversation. L'évaluateur doit également voir ce que l'agent a fait en coulisses. Selon les critères, différentes parties de ce compte rendu d'exécution sont nécessaires.Enregistrez le déroulement de la session et reconstituez-la sous forme de vues spécifiques à chaque critère, afin que chaque évaluateur puisse consulter les détails de la conversation et de l'exécution nécessaires à l'évaluation de ce critère, sans être gêné par des informations superflues issues de l'enregistrement.
Il n'y a pas de simulation sans risque. Une modification ne peut pas être testée sur de vrais utilisateurs, et une session passée ne peut pas être rejouée face à un agent modifié. Génération de sessions à la demande : un utilisateur simulé guide l'agent à travers un scénario choisi, les données contextuelles restant fixes afin que le même scénario se déroule de la même manière à chaque fois.
Évaluation à grande échelle. Déterminer si une session a été utile à l’utilisateur nécessite un jugement de niveau humain, mais l’évaluation humaine n’est pas adaptable à grande échelle.Un juge automatisé qui remplace un évaluateur humain : un modèle de langage de grande envergure (LLM) calibré à partir de sessions annotées par des humains, afin que les verdicts soient fiables.
Deux contextes. La qualité au stade du développement et celle au stade de la production peuvent diverger, et nous avons besoin des deux.Évaluez les sessions réelles et simulées à l'aide de la même grille d'évaluation et du même évaluateur, afin qu'un résultat obtenu hors ligne soit transposable en environnement de production et qu'un échec en production puisse être reproduit hors ligne.

Ce que nous évaluons, et où

Ask DoorDash utilise une architecture multi-agents. Le message d’un utilisateur parvient d’abord à l’agent Orchestrator, qui achemine la requête vers un agent de domaine spécialisé (par exemple, l’agent de recherche de restaurants). L’agent de domaine sélectionné gère alors la conversation directement ou renvoie le contrôle à l’Orchestrator lorsque la requête doit être réacheminée. 

Le système d'évaluation reflète cette structure. Étant donné que les défaillances surviennent à différents stades du pipeline, nous évaluons chaque couche là où elle peut être mesurée le plus directement : le routage au niveau de l'Orchestrator, les mesures de sécurité tout au long du flux, et les capacités spécifiques à chaque tâche au niveau de chaque agent de domaine.

Figure 2 : L'évaluation reflète l'architecture d'exécution de l'agent.

À l'intérieur du faisceau de câbles d'évaluation

Derrière ces évaluations se cache un ensemble unique dont les différents éléments correspondent aux exigences décrites précédemment. Une grille d’évaluation définit les critères d’une bonne session, et un générateur de transcriptions transforme une session brute en un document lisible par un évaluateur. Un modèle de langage de grande capacité (LLM), calibré par rapport à des évaluateurs humains, note cette transcription en fonction de la grille d’évaluation. Un simulateur génère ensuite des sessions à la demande pour des exécutions hors ligne. Comme ces éléments s’appuient les uns sur les autres, nous les abordons dans cet ordre précis, en commençant par la grille d’évaluation.

Grille d'évaluation

Une grille d’évaluation définit les critères utilisés pour évaluer une session. Pour rédiger une bonne grille d’évaluation, il faut trouver le juste équilibre entre précision et généralité. Les critères doivent être suffisamment précis pour garantir une notation cohérente, mais suffisamment généraux pour accepter toutes les réponses valables. Étant donné que de nombreuses tâches n’ont pas de « bonne réponse » unique, la grille d’évaluation doit décrire à quoi ressemble une session réussie plutôt que de prescrire un résultat précis. Le tableau ci-dessous présente des exemples de dimensions et de critères issus de chaque évaluation. Chaque critère fait l’objet d’une notation binaire (oui/non), et les notes individuelles sont agrégées pour former une note finale au niveau de la session.

ÉvaluationAgentDimensionExemple de critère
Glissière de sécuritéTous les agentsQualité de la communicationL'agent a donné une réponse concise et n'a pas expliqué son raisonnement à l'utilisateur.
Confiance et intégritéL'assistant n'a pas fourni d'informations manifestement fausses ni fait d'affirmations qui contredisent catégoriquement ce qui avait été montré au client
CapacitéÀ la découverte des restaurantsSatisfaction des contraintesLes restaurants recommandés répondent aux critères explicites de la demande en matière de délai de livraison, de budget et de régime alimentaire.
Diversité des résultatsCette collection offre une véritable diversité plutôt que des pièces presque identiques
CoursesExécution des achatsLes demandes explicites de l'utilisateur visant à modifier la liste de courses sont prises en compte.
Pertinence des articlesLes éléments sélectionnés correspondent aux objectifs de l'utilisateur dans cet État.

Ce qui constitue un bon parcours dépend de l'intention de l'utilisateur. Une commande de produits d'épicerie, une recherche de recette ou la recherche d'un restaurant : chacun de ces cas définit la réussite différemment. Certains parcours s'appuient également sur des outils ou des compétences spécialement conçus à cet effet ; tous les critères ne s'appliquent donc pas à chaque session.

Chaque critère comporte une étape de vérification de l'éligibilité. Avant de l'évaluer, le juge détermine d'abord si le critère s'applique à la session. Si ce n'est pas le cas, il passe cette étape. Ainsi, le déroulement d'une recette n'est pas pénalisé s'il manque une étape spécifique à la réorganisation. Les grilles d'évaluation « garde-fou » et « capacité » fonctionnent de cette manière, tant pour les sessions de production réelles en ligne que pour les sessions simulées hors ligne.

Hors ligne, nous utilisons également une grille d'évaluation sous forme de liste de contrôle : il s'agit d'une liste spécifique à chaque scénario qui détaille ce qu'une exécution correcte doit produire. Pour une session intitulée « Liste de courses pour des tacos végétariens pour deux personnes à moins de 60 $ », la liste de contrôle vérifie que chaque article est un ingrédient pertinent pour la préparation de tacos végétariens et que le sous-total est égal ou inférieur à 60 $.

Ces vérifications confèrent à l'évaluation hors ligne un signal à variance plus faible que les critères généraux et larges utilisés seuls. Elles nous permettent de tester directement des comportements spécifiques, ce qui rend un petit échantillon plus utile et favorise des itérations plus rapides. Les modalités de mise en place du dispositif de test sont abordées dans la section consacrée au simulateur de conversation ci-dessous.

Chaque critère est rédigé de manière à pouvoir être vérifié à partir de la session elle-même. Cela permet d'utiliser la même grille d'évaluation à la fois par un évaluateur humain lors de l'étalonnage et par le juge LLM à grande échelle. Les deux ont besoin que la session soit disponible sous une forme lisible, ce que permet justement le générateur de transcriptions.

Générateur de transcriptions

Nous instrumentons chaque session d’agent à l’aide d’OpenTelemetry. Chaque session devient une trace, que nous stockons dans une instance interne de ClickHouse. Ses « spans » enregistrent les étapes suivies par l’agent, notamment les entrées de l’utilisateur, les sorties du modèle, les appels et réponses des outils, ainsi que les widgets affichés à l’utilisateur. Cet enregistrement complet constitue la matière première à partir de laquelle chaque évaluation est lancée.

La trace brute est complète, mais difficile à noter directement. Certaines réponses des outils sont très volumineuses, et une grande partie du contenu contribue à la bonne structure de l'enregistrement, comme la mise en place du schéma et les champs qui ne fournissent aucune indication sur la qualité de l'agent. Les éléments justifiant un critère peuvent également être répartis sur plusieurs segments ou tours de parole ; ils doivent donc être réassemblés avant qu'un juge puisse les utiliser.

Le générateur de transcriptions est un ensemble de scripts Python qui traite les traces stockées avant leur évaluation. Il réassemble les segments dispersés, supprime les tokens ne fournissant aucune indication sur la qualité et réduit les charges utiles trop volumineuses. Il en résulte une vue synthétique de la conversation et du travail qui se cache derrière. Un évaluateur peut noter cette vue de manière plus cohérente que la trace brute.

Tous les critères ne nécessitent pas une vue d’ensemble. Chaque critère précise les éléments probants dont il dépend, et le concepteur ne fournit au juge que cette partie spécifique. Un contrôle de fond prend en compte l’affirmation de l’agent et les résultats de l’outil sur lesquels elle s’appuie. Un contrôle de diversité examine les recommandations et la demande initiale. Un contrôle narratif analyse le texte que l’agent a généré en continu pendant son travail. Le fait de circonscrire les éléments probants permet de maintenir chaque jugement ciblé.

Figure 3 : L'outil de création de transcriptions transforme les traces brutes en vues spécifiques à chaque critère, permettant ainsi une évaluation plus ciblée et plus cohérente.

Simulateur de conversation

Le simulateur de conversation nous permet d’évaluer un agent candidat avant qu’il n’entre en contact avec de vrais utilisateurs, en générant des sessions réalistes avec un utilisateur simulé – un modèle de langage de grande capacité (LLM) jouant le rôle du client. Chaque exécution part d’un scénario qui définit la demande initiale, l’objectif de l’utilisateur et la manière dont l’agent doit réagir aux questions ou aux résultats, produisant ainsi des conversations comparables à plusieurs échanges. Lorsqu’un scénario dépend d’un état externe, tel que des commandes Safeway antérieures, un panier en cours de remplissage ou le stock du magasin, le harnais utilise des « fixtures » : des données d’outil enregistrées renvoyées à la place d’appels en temps réel. Cela permet de maintenir chaque exécution dans le même état, évitant ainsi toute dérive due aux modifications du catalogue, à la disponibilité en magasin ou à l’historique du compte de test. Par exemple, le scénario de réapprovisionnement s’ouvre sur « Réapprovisionner mes produits habituels » et fixe la fonction get_reorder_items à un historique de commande enregistré.

// simulating reorder scenario
{
  "evalId": "mt-reorder",
  "sessionInput": {
    "state": {
      "__tool_fixture_pack_names": [
        "reorder_history_v1"
      ],
      "max_turns": 3
    }
  },
  "conversationScenario": {
    "startingPrompt": "Reorder my usuals",
    "conversationPlan": "You want to reorder your usual groceries.."
  }
// ...
}// reorder_history_v1 — returned in place of the live get_reorder_items call
{
  "success": true,
  "orders": [
    { "store_name": "Albertsons", "order_date": "2026-04-22T18:30:00Z",
      "items": [
        {"name": "Meadow Gold Whole Milk Jug (1 gal)",     "quantity": 2},
        {"name": "Oroweat 100% Whole Wheat Bread (24 oz)", "quantity": 1},
        {"name": "Signature Select Hass Avocados (5 ct)",  "quantity": 1},
        {"name": "Ben & Jerry's Half Baked Ice Cream",     "quantity": 1}
        // ... more items
      ] }
    // ... more historical orders
  ]
}

À chaque exécution de ce scénario, on retrouve exactement ces commandes. Le résultat attendu est donc prévisible : la liste sélectionnée par l’agent doit être tirée de l’historique fixe des commandes, et le juge peut l’évaluer à chaque fois par rapport à ce même ensemble d’éléments.

La simulation de conversations a rendu possible l’évaluation hors ligne du côté des entrées. Pour Ask DoorDash, un cycle complet type couvre 50 scénarios comprenant chacun 8 essais, ce qui génère 400 conversations. Sans simulation, la génération de cette suite obligerait un développeur à dialoguer localement avec l’agent, une conversation à la fois. À raison d’environ 1 minute par conversation, cela prendrait plus de 6 heures. Le simulateur réduit cette étape de génération à environ 20 minutes.

LLM en tant que juge

Dans la pratique, l'évaluation des agents est un problème de mesure caractérisé par une boucle de rétroaction serrée. Nous avons besoin d'un nombre suffisant d'échantillons pour estimer quotidiennement la qualité de la production, et de scores suffisamment rapides pour détecter les régressions avant qu'elles ne se propagent. La même contrainte s'applique avant le lancement : chaque modification envisagée génère ses propres sessions, et l'évaluation doit fournir des résultats suffisamment rapidement pour rester intégrée à la boucle de développement, plutôt que de devenir un goulot d'étranglement pour la mise en production.

Cette échelle exclut l'évaluation humaine comme méthode de notation par défaut. Un évaluateur peut prendre la bonne décision sur une transcription complexe, mais l'examen de chaque session est coûteux. Une session peut comporter de nombreux tours de parole, appels d'outils, résultats de modèles et widgets rendus, et l'évaluateur doit relier les affirmations de l'agent aux preuves contenues dans la trace.

L'intervention humaine reste indispensable, mais nous l'utilisons là où elle est la plus efficace : pour définir les grilles d'évaluation, étiqueter les ensembles d'étalonnage et contrôler le comportement des évaluateurs. La majeure partie de la notation est confiée à un évaluateur LLM, qui lit la transcription préparée et l'évalue en fonction de la grille d'évaluation.

Un juge LLM n'est utile que s'il est en accord avec les évaluateurs humains.

Nous avons utilisé l'optimisation GEPA des consignes pour affiner les limites de décision du juge. L'algorithme propose de manière itérative des modifications à la consigne du juge et conserve celles qui améliorent la concordance avec les étiquettes humaines sur un ensemble de validation. Cet étalonnage est continu : à mesure que les grilles d'évaluation évoluent, par exemple lorsque de nouvelles capacités sont ajoutées, nous collectons de nouvelles étiquettes et réétalonnons le juge.

Le juge note chaque critère de manière indépendante. Il reçoit le critère et les éléments de preuve sur lesquels celui-ci repose, puis rend un verdict accompagné d'une brève justification. Grâce à ce dispositif, nous avons étendu le contrôle qualité, passant d'environ un commentaire soumis par un employé à 2 000 sessions notées automatiquement par jour.

Service d'évaluation

La mise en place d’un cadre d’évaluation évolutif repose en grande partie sur une infrastructure de plateforme robuste. Le stockage des traces, l’exécution des évaluations en temps réel, le développement de juges via l’interface utilisateur et les workflows d’annotation ont un coût. Nous avons commencé par mettre en place une équipe dédiée à la plateforme embarquée, qui a développé ces éléments en parallèle du cadre d’évaluation Ask DoorDash, créant ainsi une boucle de rétroaction étroite entre le développement de la plateforme et la conception de l’évaluation. Cela a permis à l’infrastructure et au cadre d’évolution de progresser de concert. Cette étroite collaboration a accéléré le développement de l’évaluation pour Ask DoorDash et donne aujourd’hui naissance à un service d’évaluation partagé – une voie toute tracée que les autres équipes de DoorDash peuvent adopter rapidement tout en l’adaptant à leurs propres cas d’utilisation.

La boucle de rétroaction

Le dispositif d'évaluation nous offre un moyen évolutif de mesurer la qualité des agents. La question suivante est de savoir comment cette mesure modifie le cycle de développement.

Regroupement des défaillances par thèmes

Une évaluation quotidienne peut mettre en évidence de nombreuses défaillances, mais les examiner une à une rend le cycle d'itération trop axé sur des cas isolés. La défaillance la plus récente ou la plus surprenante peut prendre le dessus lors de la correction, même si ce n'est pas le mode de défaillance le plus courant. Nous devons identifier les tendances générales qui se cachent derrière ces défaillances et déterminer leur fréquence d'apparition, afin de pouvoir hiérarchiser les problèmes ayant le plus grand impact sur l'ensemble des sessions.

La grille d'évaluation nous offre un point de départ utile. Chaque session jugée insatisfaisante comporte déjà le critère qu'elle n'a pas respecté ; les critères de la grille d'évaluation constituent donc naturellement des groupes de problèmes. Une erreur de base, une substitution manquante et une narration insuffisante sont des problèmes distincts qui doivent généralement être analysés séparément. Regrouper les erreurs de cette manière permet de transformer une longue liste d'exemples en un ensemble de thèmes classés par ordre d'importance.

De la détection à la résolution

Le regroupement par clusters nous indique ce qui tombe le plus souvent en panne, mais pour déterminer pourquoi cela se produit et comment y remédier, il faut tenir compte du contexte de mise en œuvre. À mesure que le développement piloté par l’IA accélère la production de code et de modifications, le goulot d’étranglement se déplace vers le choix de la bonne modification et la validation de son impact. À partir d’un cluster de défaillances, un agent de codage peut inspecter les traces d’échec, les chemins de code concernés, les modifications récentes et les analyses précédentes. Lorsque la solution est claire, il peut directement rédiger une pull request ; pour les modifications nécessitant un apprentissage rapide ou en contexte, il établit un diagnostic et propose une modification qui sera soumise à la validation humaine.

Compétences des agents

Nous regroupons ces workflows de développement axés sur l'évaluation sous la forme de « compétences d'agent ». Chaque compétence définit une tâche reproductible, telle que le regroupement des défaillances ou l'analyse d'un mode de défaillance. Cela facilite l'adoption du workflow par d'autres équipes et nous permet de l'améliorer plus facilement au fil du temps. Lorsqu'une compétence classe un cluster de manière erronée ou propose une solution insuffisante, nous mettons à jour la compétence plutôt que de corriger une exécution ponctuelle, et cette amélioration est répercutée sur les invocations futures.

Le harnais d'évaluation fournit le signal, et les compétences des agents transforment ce signal en un flux de travail opérationnel. Ensemble, ils font de ce harnais bien plus qu'un simple tableau de bord : un plan de contrôle dédié à l'itération qui relie, au sein d'une boucle unique, la surveillance en production, le regroupement des défaillances, les investigations assistées par des agents et la validation avant mise en production.

Figure 4 : La boucle de rétroaction transforme « eval » en un processus continu allant des défaillances observées aux améliorations validées.

Du signal d'évaluation à l'impact sur la production

Réduire les fuites de raisonnement

Un problème récent concernant un agent chargé des courses illustre concrètement le cycle de développement axé sur l'évaluation. L'évaluation quotidienne en ligne a mis en évidence un pic de fuites de raisonnement. L'agent a mené à bien la tâche, mais ses explications destinées à l'utilisateur utilisaient parfois un langage propre au système, tel que « réorganiser la compétence », des noms d'outils ou des expressions propres au domaine logiciel comme « récupérer » et « en parallèle ». La réponse ressemblait davantage à celle d'un agent de programmation qu'à celle d'un assistant d'achats.

Le diagnostic a mis en évidence la nécessité d’une conception rapide ; nous avons donc harmonisé les règles de communication de l’agent et séparé les instructions techniques internes du langage destiné aux utilisateurs. Nous avons validé cette modification hors ligne à l’aide des scénarios les plus susceptibles de présenter ce mode de défaillance dans le trafic de production, et avons constaté une baisse du taux de fuite de 11 %. Nous avons également effectué une évaluation complète des scénarios clés et n’avons détecté aucune régression imputable à cette modification. Après le déploiement, la surveillance en ligne a confirmé une amélioration spectaculaire.

Figure 5 : Eval a permis d'identifier et de réduire les fuites de raisonnement dans les messages destinés aux utilisateurs.

Réduire les risques liés à la migration vers un modèle de base

Lorsque Gemini 3.5 Flash a été déployé à la mi-mai, nos agents fonctionnaient sous Claude Sonnet 4.6. Les tests de performance de Flash ont mis en évidence une opportunité : réduire la latence d’« Ask DoorDash » afin que l’agent donne l’impression d’être réactif plutôt que bloqué. Cependant, le remplacement du modèle de base comporte des risques, car il peut modifier le comportement des agents à l’échelle du système. Nous devions nous assurer que la qualité resterait au rendez-vous avant d’exposer les utilisateurs à cette nouvelle version.

Nous avons testé Flash à l’aide du harnais hors ligne, et les scores ont chuté brutalement. L’évaluation a mis en évidence des schémas d’échec concrets, et nous avons orienté les agents de codage IA vers ces schémas afin qu’ils formulent des hypothèses et mènent des expériences. Ce que nous avons découvert n’était pas un déficit de capacité, mais un problème de compatibilité. Flash formatait certains paramètres d’outils différemment de Sonnet et interprétait certaines parties d’une consigne système qui avaient été implicitement ajustées selon les conventions de Sonnet. Les faibles scores reflétaient un système adapté à Sonnet, et non un modèle moins performant.

That diagnosis pointed to a set of small, targeted fixes. Some were deterministic guards on tool inputs – Flash would occasionally pass a search query as a JSON object like {“dishes”: […]} where the tool’s signature declared a plain string, so we coerced these back into the expected shape instead of dropping the call. Others were prompt updates that stated explicitly what Sonnet had inferred on its own, such as using the exact store name from the data and not embellishing beyond what the tool results support. We were not changing the model; we were correcting the environment around it.

La réexécution de l'évaluation a permis à Flash de retrouver un niveau de qualité équivalent à celui de Sonnet, dans les limites du bruit du faisceau de câbles. Nous avons migré les agents de production et surveillé la qualité et l'engagement dans le trafic en production. Les résultats positifs se sont confirmés : une réduction de 35 % de la latence sans aucune perte au niveau des indicateurs de qualité.

Conjointement avec l'exemple de la fuite de raisonnement, cela démontre que le harnais fonctionne dans les deux sens : il transforme les signaux de qualité issus de la production en corrections et réduit les risques liés aux modifications délibérées du système avant leur déploiement.

Enseignements tirés

Les juges ont besoin de transcriptions adaptées à la question posée. La trace brute contient souvent plus d’informations que ne l’exige le critère. Certains segments n’ont aucun rapport avec le critère. Même les segments utiles peuvent inclure des éléments de structure de schéma, des champs répétés et des tokens de charge utile qui n’existent que pour garantir la validité syntaxique des données. Nous obtenons de meilleurs résultats en supprimant ce qui n’est pas pertinent et en ne conservant que les éléments sur lesquels le critère repose réellement.

L'environnement doit être contrôlé, sinon le score mesure du bruit. La dérive en amont et le non-déterminisme propre à l'agent peuvent tous deux influencer le résultat, ce qui rend difficile de déterminer si un changement a réellement été bénéfique. Les « fixtures » figent l'environnement en amont, et les scénarios fixes maintiennent les conditions de la tâche stables ; ainsi, l'évolution des métriques est plus susceptible de refléter l'agent lui-même plutôt que son environnement.

Un résultat hors ligne n'a d'importance que s'il se répercute en ligne. C'est pourquoi on utilise la même grille d'évaluation et le même juge calibré dans les deux contextes. Si l'on utilise des juges calibrés différemment pour l'évaluation en ligne et hors ligne, leurs notes peuvent diverger d'une manière difficile à concilier.

Le système d'évaluation génère également ses propres rapports de bogues. Une part importante des échecs signalés correspond en réalité à des défauts du harnais plutôt qu'à des problèmes liés aux agents ; il s'agit généralement d'un faux positif du juge ou d'une lacune dans le traçage qui fournit au juge une vue incomplète. Le fait de remédier à ces problèmes en parallèle permet de garantir la fiabilité du système d'évaluation.

Conclusion

Pour les agents de production, l'évaluation ne peut pas être une réflexion après coup. Elle doit faire partie intégrante du système dès le départ. Mais l'évaluation des sessions n'est qu'une première étape. Le plus difficile est de transformer ces notes en une amélioration du comportement des agents.

Ce dispositif comble le fossé entre la mesure et l’amélioration. Il a permis de faire passer le suivi quotidien de la qualité d’environ un commentaire soumis par un collaborateur à 2 000 sessions notées automatiquement, et de réduire la durée des tests de régression complets de plus de 6 heures (effectués manuellement) à environ 20 minutes. Ces gains nous ont permis de détecter plus tôt les problèmes de production, de hiérarchiser les modes de défaillance récurrents et de valider les changements majeurs avant qu’ils n’atteignent les utilisateurs. Il en a résulté un impact mesurable sur la production, notamment une amélioration de 8 points des scores de qualité des agents avant le lancement national et une migration validée vers un modèle de base qui a réduit la latence de 35 % tout en préservant la qualité.

Un cadre d'évaluation évolutif nécessite une infrastructure de plateforme robuste. Une étroite collaboration avec l'équipe chargée de la plateforme a permis d'accélérer le développement du cadre d'évaluation des agents pour Ask DoorDash. Ce travail de base est aujourd'hui à l'origine d'un service d'évaluation commun.

Voici la mise en garde obligatoire « L’IA évolue rapidement » : lorsque j’ai rédigé la première ébauche de cet article début mars, j’ai commencé par dire que « l’IA arrive » et que nous devrions nous y préparer. En relisant ces lignes à peine quelques mois plus tard, cette phrase me semble déjà remarquablement dépassée : l’IA n’est plus seulement en train d’arriver ; elle est déjà là. Les grandes entreprises technologiques génèrent désormais plus de 75 % de leur code grâce à l’IA, et les entreprises plus petites, moins à la pointe de la technologie, emboîtent rapidement le pas. Au moment où vous lirez cet article, cette affirmation vous paraîtra peut-être déjà aussi ridicule que la précédente !

Je pense toutefois que les enseignements tirés de cet article restent d’actualité. Ce que je présente ici, c’est mon parcours personnel vers la maîtrise de l’IA. Tout bien considéré, je me considère au mieux comme un utilisateur de niveau intermédiaire, mais pour en arriver là, il m’a fallu beaucoup d’expérimentation et bien plus d’écoutes de podcasts que je ne suis prêt à l’admettre. Ce qui reste encore un défi pour de nombreux développeurs, ce n’est pas d’utiliser l’IA pour la saisie semi-automatique ou les modifications rapides, mais de confier une tâche plus importante à un agent et d’obtenir en retour un résultat cohérent, vérifiable et utile. Si ce processus ne vous semble pas encore tout à fait naturel, cet article est fait pour vous.

Au fil des prochaines sections, je retracerai mon parcours : depuis le moment où Cursor était mon outil de prédilection pour la programmation d’IA, en passant par mes premières tentatives un peu hasardeuses avec Claude Code, puis les expériences menées avec des équipes d’agents qui ont surtout abouti à des résultats médiocres et coûteux, jusqu’à la boucle « recherche → planification → mise en œuvre » qui a finalement permis de rendre pratique l’utilisation d’agents fonctionnant sur le long terme.

Je vais ensuite vous présenter un petit projet que j’ai baptisé « Agentic Orchestrator », qui synthétise toutes ces leçons. Comme le montre la figure 1, il s’agit d’un orchestrateur assez basique, basé sur une machine à états, que j’ai créé pour permettre aux agents de mener à bien des projets ambitieux en une seule fois. Et le meilleur dans tout ça ? C’est open source

Figure 1 : Le tableau de bord principal d'Agentic Orchestrator, qui permet de suivre plusieurs fonctionnalités à différents stades de développement.

Mais commençons par le début.

Avant la boucle : ma phase de curseur

Cursor est un outil incroyable pour booster la productivité, qui allie la commodité d’un environnement de développement intégré complet à la puissance de l’IA agentique pour venir à bout de tâches présentant divers niveaux de complexité. Il ne cesse de s’améliorer au fil du temps, à mesure que les modèles et les outils gagnent en performances. Il est devenu mon outil de prédilection en 2025 et jusqu’en 2026. Je ne me souviens pas avoir écrit beaucoup de lignes de code à la main, à moins que l’utilisation incessante de la touche Tab ne compte.

Mais à mesure que ma curiosité grandissait quant aux limites du développement agentique, j’ai commencé à me heurter aux limites de Cursor et, plus généralement, du développement piloté par les onglets. Le principal problème était que je ne parvenais pas à le faire fonctionner de manière suffisamment fiable et durable pour développer des fonctionnalités complexes en toute autonomie. 

Grâce aux fonctionnalités révolutionnaires apportées par Opus d’Anthropic, le secteur parlait déjà des agents à exécution longue et de leur capacité à générer des caractéristiques en une seule fois, sans intervention humaine. Je voulais découvrir tout cela par moi-même ! Il était donc temps pour moi de me tourner vers la nouvelle tendance dont tout le monde parlait : Claude Code.

Ma relation complexe avec Claude Code

Imaginez un peu : vos amis et collègues se vantent de pouvoir automatiser tous les aspects de leur vie, tandis que vos tentatives pour automatiser ne serait-ce que les tâches les plus simples se soldent par un échec cuisant. C'était exactement mon cas en mars 2026.

Des personnes issues de différentes entreprises et ayant des parcours techniques variés tentaient de me convaincre que nous avions désormais atteint la singularité. Plus besoin d’intervention humaine, disaient-ils ; l’IA incarnerait chacun de vos désirs et les concrétiserait, qu’il s’agisse de se faire passer pour vous sur Slack, de répondre à des appels Zoom, de préparer un espresso d’exception et, bien sûr, d’écrire du code. Il me suffisait, m’assuraient-ils, de passer à Claude Code avec Opus 4.6 pour voir mon univers bouleversé. C’est donc ce que j’ai fait ! Pour moi, cependant, cela s’est avéré pire que d’utiliser des modèles équivalents avec Cursor.

J'apprécie autant que n'importe quel autre développeur un bon outil en ligne de commande, mais avec Claude Code, j'ai perdu la possibilité d'interagir directement pour vérifier chaque modification individuellement. En théorie, cela aurait dû être compensé par une meilleure orchestration et par les outils fournis par le « harness », mais au final, le modèle restait le même. Et ce n'est pas comme si Cursor ne disposait pas d'outils ou d'un mode « plan ». Alors, qu'est-ce qui cloche ?

Il s'avère que Claude Code s'accompagne d'un véritable changement de mentalité : vous n'avez plus besoin d'interagir directement avec votre code. Vous devez modifier votre processus de développement, faire confiance à l'agent et mener vos itérations entièrement par le biais de prompts. 

Il était temps de se lancer dans des essais et de tenter de réaliser cet espresso de rêve préparé avec la machine Opus.

Lancement des expériences : des agents, des agents, encore des agents !

N'oubliez pas mon objectif principal : amener l'IA à créer de manière autonome une fonctionnalité complexe.

En parcourant la documentation de Claude Code, je suis tombé sur une fonctionnalité expérimentale appelée « équipes d’agents ». Celle-ci permet aux utilisateurs de faire appel à une équipe d’agents dotés de personnalités et de rôles différents pour « pirater » diverses parties du code, puis de fusionner leurs résultats en un produit final fonctionnel — du moins, en théorie.

En pratique, mon expérience avec cet essaim d’agents a mal tourné pour des raisons auxquelles je ne m’attendais pas. Au départ, je pensais que le plus difficile serait la formulation des consignes, alors je me suis concentré là-dessus. J’ai créé des personas détaillés, j’ai dit à un agent qu’il était « le meilleur développeur Go qui ait jamais existé », j’en ai présenté un autre comme un « réviseur senior méticuleux », et j’ai orchestré la manière dont ils se passeraient le relais. J’étais, en substance, un influenceur motivateur pour les modèles linguistiques. Mais rien de tout cela n’a fait bouger les choses. La flatterie n’ajoute pas de capacités ! Le modèle ne garde pas ses véritables compétences en réserve jusqu’à ce que vous lui disiez qu’il est brillant. Le personnage n’était que de la mise en scène ; derrière cette mise en scène, le résultat était exactement aussi bon ou aussi mauvais qu’il aurait pu l’être.

Mon fiasco avec les personas était globalement sans gravité. Au pire, cela représentait un contexte gaspillé. Le véritable problème était que le parallélisme multiplie les divergences. Chaque agent travaillait à partir de sa propre interprétation d’un cahier des charges ambigu, formulait ses propres hypothèses indépendantes et inventait sa propre version de l’interface là où deux éléments étaient censés se rejoindre. Chaque agent était plausible localement. Mais rien n’était cohérent à l’échelle globale. Ainsi, au moment de la fusion, les joints ne s’emboîtaient pas : il y avait des contrats incompatibles, des logiques redondantes et deux moitiés d’une fonctionnalité où chacune supposait que l’autre fonctionnait différemment.

Résultat final ? Pour les fonctionnalités simples, les équipes d’agents fonctionnaient correctement. En revanche, pour tout ce qui aurait pu justifier la complexité d’une configuration multi-agents, les résultats médiocres issus de ces sessions étaient totalement inutilisables. Je me suis retrouvé à abandonner des pull requests entières qui avaient nécessité des heures de traitement, précisément pour cette raison.

J’ai donc pris un peu de recul. La leçon à retenir n’était pas que les agents ne peuvent pas gérer la complexité, ni que multiplier les agents est pire que d’en avoir un seul. C’était plutôt que je me posais la mauvaise question. Je n’arrêtais pas d’essayer d’amener les agents à collaborer, à se coordonner, à se mettre d’accord, à fusionner leur travail, alors que le véritable problème était qu’il n’y avait rien autour de quoi ils pouvaient se coordonner. Il n’y avait pas de source de vérité partagée. Aucun artefact n’était transmis d’une étape à l’autre. Il n’y avait pas de séparation entre la réflexion et l’action. Les agents n’étaient pas le problème. C’était l’espace vide entre eux. J’ai donc cessé de me demander comment amener une équipe d’agents à travailler ensemble et j’ai commencé à poser la question inverse : quelle structure permettrait de libérer chaque agent de l’obligation de collaborer, afin qu’il puisse se consacrer à une seule tâche précise et bien définie, en s’appuyant sur un élément déjà produit ? Il s’agissait moins de « jeter des agents sur le problème » que de construire les rails et de laisser les agents y circuler, un tronçon à la fois. 

C'est cette nouvelle approche qui m'a finalement conduit à emprunter une voie très différente.

Ma percée : recherche, entretiens, planification, itération et critiques hostiles

Après la débâcle de l’équipe d’agents, j’ai pris une mesure radicale : j’ai parlé à des gens — de vrais êtres humains, en somme. J’ai recherché des ingénieurs qui avaient, d’une manière ou d’une autre, trouvé la clé pour amener Claude à effectuer un travail utile de manière autonome. Ce que j’ai appris a complètement bouleversé ma vision des choses.

La première révélation m’est venue d’une conférence inspirante donnée par Humanlayer sur ce qu’ils appellent le cadre RPI — recherche, planification, mise en œuvre. L’idée est d’une simplicité trompeuse : au lieu de confier un cahier des charges complet à un agent en espérant que tout se passe bien, on décompose le travail en phases cognitives distinctes. Tout d’abord, l’agent explore la base de code en lisant les fichiers pertinents, en comprenant les schémas et en cartographiant les dépendances. Il élabore ensuite un plan — un document de mise en œuvre détaillé et par étapes, comprenant des chemins d’accès spécifiques aux fichiers, des extraits de code et des critères de réussite. Ce n’est qu’une fois cette étape terminée qu’il procède à la mise en œuvre, en s’appuyant sur son propre plan plutôt que sur une compréhension vague d’un cahier des charges qu’il a à moitié oublié il y a 30 000 tokens.

Il s’agissait d’une philosophie fondamentalement différente de l’approche « voici le cahier des charges, allez le développer » que j’utilisais jusqu’alors. Chaque phase a un objectif précis et bien défini. Chaque phase produit un artefact concret qui sert de base à la suivante. Et surtout, chaque phase s’inscrit naturellement dans une fenêtre contextuelle, car elle ne cherche pas à garder l’ensemble du problème en tête d’un seul coup. La recherche n’a pas besoin de se préoccuper des détails d’implémentation. La planification n’a pas besoin d’écrire de code. L’implémentation n’a pas besoin de redécouvrir la base de code, car le plan lui indique déjà exactement où chercher.

La deuxième révélation était encore plus simple, grâce à la technique « grill-me » de Matt Pocock: laissez l’agent vous interroger. Au lieu de passer des heures à rédiger la consigne parfaite qui anticipe tous les cas limites et clarifie toutes les ambiguïtés, vous laissez l’agent vous poser des questions ! Il s’avère qu’un échange avec un agent qui vient de terminer l’analyse de votre base de code vaut bien plus que des heures passées à élaborer une consigne en amont. L’agent sait ce dont il a besoin ; il suffit de lui donner la permission de poser des questions. Vous seriez surpris de voir à quel point ces questions sont parfois pertinentes. En fin de compte, c’est tout à fait logique : l’une des choses que l’on apprend en gravissant les échelons de sa carrière, c’est de déléguer. Lorsque vous le faites, la plupart du temps, les ingénieurs à qui vous avez délégué une tâche reviennent vers vous avec des questions. Ils ne veulent pas faire de suppositions erronées sur ce que vous aviez en tête. Pourquoi les agents seraient-ils différents ?

Mais la troisième leçon, et la plus importante, que j’ai apprise concernait ce que le secteur appelle communément « la boucle ».

Vous voyez, même si vous implémentez le RPI, vous êtes toujours confronté à deux problèmes. Le premier est le problème de la fenêtre de contexte : une implémentation complexe peut nécessiter plus de tokens qu’une seule session ne peut en contenir. L’agent peut avoir exécuté 80 % du plan, puis commencer à perdre sa cohérence. Ou bien il peut rencontrer une erreur de compilation inattendue qui le fait partir en vrille. Le deuxième problème est plus subtil : l’auto-révision est pratiquement inutile. Un agent qui vient de passer une demi-heure à implémenter une fonctionnalité ne devrait pas avoir à décider ensuite si cette implémentation est réellement bonne.

La boucle résout le premier problème. Un réviseur antagoniste résout le second. Et le plus beau dans tout ça ? Le tout est d’une simplicité déconcertante. À la base, il s’agit toujours essentiellement d’une boucle « while » en Bash, simplement dotée d’un contrôle de révision, comme illustré ici :

while [ iteration < max_iterations ]; do
      run the implementation agent with the objective and current progress
      check if the agent reported SUCCESS or RETRY
      if RETRY → feed the progress back in and go again
      if SUCCESS → run an antagonistic reviewer in a fresh context window
          if APPROVED → done
          if CHANGES_REQUESTED → feed the review back in and go again
  done

C’est tout. C’est ça, le secret. Vous lancez l’agent, il effectue certaines tâches, il met à jour un fichier de progression indiquant « voici ce que j’ai fait, voici ce qu’il reste à faire », puis il émet un signal : AGENT_LOOP_STATUS : SUCCESS ou AGENT_LOOP_STATUS : RETRY. S’il indique RETRY, vous démarrez une nouvelle session avec une fenêtre de contexte vierge, mais vous lui transmettez le fichier de progression afin qu’il sache où il s’était arrêté. S’il indique SUCCESS, vous ne lui faites pas confiance. Vous faites appel à l’antagoniste.

Par « réviseur antagoniste », je ne fais pas référence à un agent impoli ou qui prend plaisir à contredire pour le plaisir (même si c’est vraiment amusant à regarder ; vous devriez essayer !). Je parle d’un agent distinct dont le rôle est d’adopter une attitude professionnellement sceptique. Il lit le plan, examine les modifications, vérifie les tests, recherche les exigences manquantes, met en évidence les hypothèses fragiles et pose la question que l’agent chargé de la mise en œuvre est le moins enclin à poser : « Est-ce que c’est vraiment terminé ? »

La fenêtre de contexte distincte est la clé. Si le même agent révise son propre travail, il conserve toutes les hypothèses, les raccourcis et la dynamique narrative de la mise en œuvre. Il se souvient des raisons qui l’ont poussé à prendre une décision, et est donc plus enclin à la défendre. Un réviseur extérieur n’a pas ce genre d’attachement. Il examine l’état du référentiel, l’objectif défini et les artefacts de mise en œuvre. Cette séparation crée juste assez de pression critique pour détecter le type d’erreurs qui, sans cela, subsisteraient jusqu’à la révision humaine.

Cela permet de résoudre ces deux types de défaillance de la manière la plus pragmatique qui soit. Au lieu d’essayer de faire en sorte qu’une seule session dure pendant toute la durée de la mise en œuvre, on accepte que les sessions soient éphémères et on adapte la conception en conséquence. Chaque itération dispose d’une fenêtre de contexte vierge. Chaque itération lit le fichier de progression, identifie ce qui a été réalisé et se concentre sur la suite. Le fichier de progression devient la mémoire à long terme de l’agent, tandis que le réviseur fait office de système immunitaire qui empêche un travail de mauvaise qualité de se déclarer discrètement comme terminé.

Les plus avertis d'entre vous auront peut-être déjà reconnu qu'il s'agit d'une boucle de Ralph. Le critique acerbe a apporté la pièce manquante : il ne s'agit pas de « continuer jusqu'à ce que ce soit terminé », mais de « continuer jusqu'à ce qu'un regard neuf confirme que “terminé” signifie bien “terminé” ».

La combinaison des phases RPI, de la boucle et de la révision antagoniste a constitué un véritable tournant. Pour la première fois, j’ai pu confier à Claude une fonctionnalité moyennement complexe — quelque chose qui aurait nécessité plusieurs milliers de lignes de code réparties sur plusieurs fichiers — et retrouver une implémentation fonctionnelle. Cela ne fonctionnait pas à chaque fois, mais suffisamment souvent pour que je cesse de considérer le codage autonome comme un simple tour de passe-passe et que je commence à le voir comme un outil.

L'idée centrale était solide : diviser le travail en phases, faire en sorte que chaque phase produise des artefacts, passer d'un contexte à l'autre, et ne jamais laisser l'agent qui a écrit le code être le seul à juger de sa qualité.

Un espace de travail en désordre

Même si j’étais ravi de pouvoir créer des éléments complexes avec très peu de réglages à la fin de cette avancée décisive, je me suis laissé emporter. Mon espace de travail a fini par ressembler à la figure 2, mais multiplié par cinq ou six — un pour chaque élément sur lequel je travaillais simultanément.

Figure 2 : Mon espace de travail traditionnel : plusieurs sessions Claude et volets de terminal, chacun suivant une phase différente de la boucle RPI.

Le workflow fonctionnait, mais il était fastidieux à exécuter manuellement. Pour chaque fonctionnalité, je devais mener une session d’IA pour analyser le code source, une autre pour transformer ces résultats en plan de mise en œuvre, puis une boucle où je répondais aux questions de clarification, revoyais le plan et lançais la mise en œuvre. C’était gérable pour une seule fonctionnalité. Mais dès que j’ai essayé de gérer cinq ou six fonctionnalités en même temps, je me suis retrouvé à passer sans cesse d’un terminal à l’autre, d’un plan à l’autre, d’une branche à l’autre et de conversations inachevées à d’autres.

Ma première réaction a été de me tourner à nouveau vers les agents. J'ai demandé à un agent gestionnaire de mettre en œuvre le cadre « RPI-plus-loop » à l'aide de sous-agents, afin de pouvoir tout gérer à partir d'une seule instance de Claude. Mais, déjà marqué par mon expérience antérieure avec les équipes d'agents, j'ai décidé de créer un outil à la place. Je suis heureux d'annoncer que cet outil est désormais open source et accessible à tous.

Découvrez Agentic Orchestrator

C'est ainsi qu'est né Agentic Orchestrator. Il s'agit d'une interface utilisateur de terminal (TUI) qui reprend le cycle « recherche → planification → mise en œuvre », l'améliore et l'intègre dans une véritable ingénierie.

Le principe de base est simple : ne demandez pas à une IA de gérer d’autres IA. Créez plutôt une infrastructure qui pilote le cycle de vie des fonctionnalités, et laissez chaque session d’IA se concentrer sur une seule tâche, qu’elle accomplira parfaitement. L’orchestrateur gère les aspects techniques, notamment les transitions d’état, l’isolation de l’arborescence de travail, la gestion des sessions, le suivi de la progression et la reprise après incident. L’agent se charge de la réflexion. Il s’agit d’une orchestration prévisible qui s’appuie sur des agents imprévisibles.

Des boucles Bash à une véritable machine à états

Vous vous souvenez de la boucle « while » en Bash évoquée plus haut ? C’est celle où l’on lance l’agent, où l’on vérifie son état et où l’on renvoie des informations sur l’avancement. Agentic Orchestrator reprend ce même principe et lui donne une structure concrète. Chaque fonctionnalité évolue selon une machine à états bien définie, comme le montre la figure 3 : Base de connaissances → demande d’informations → recherche → conception → planification → mise en œuvre → révision → publication → terminé.

Figure 3 : Le nouveau cycle de développement amélioré.

RPI a désormais un aspect un peu différent de ce qu’il était auparavant. C’est le résultat de plusieurs semaines d’expérimentation, qui ont abouti à un processus de travail me permettant de produire en toute confiance des éléments très complexes au cours d’une seule session de longue durée. Non, je n’ai pas trouvé d’acronyme astucieux pour le désigner, mais le tableau 1 présente chaque étape et son rôle.

PhaseSortiePourquoi cela existe-t-il ?
Base de connaissancesUne carte réutilisable présentant l'architecture, les conventions, les API, les dépendances et les commandes de vérificationCela permet à chaque phase suivante de partir d'une base de référence réelle, au lieu de devoir redécouvrir le dépôt à partir de zéro.
Demande de renseignementsClarification des questions et des réponses des utilisateurs grâce au modèle « grill-me »Cela permet de mettre les inconnues au grand jour avant que les agents ne se mettent à émettre des hypothèses.
RechercheRecherche documentaire avec des références de dossiers précises et description du comportement actuelDécrit la réalité, et non la solution, sur laquelle reposera la planification fondée sur les faits. 
ConceptionDocument de conception : énoncé du problème, solution, scénarios utilisateurs, décisions, éléments explicitement exclus du périmètreConstitue la référence faisant autorité pour cette fonctionnalité, établie à l'issue d'une revue de conception ciblée menée avec l'agent.
Feuille de routeListe ordonnée de fines tranches verticales s'enchaînant bout à boutPermet aux agents d'exécuter des segments délimités et vérifiables plutôt que des plans tentaculaires qui touchent toutes les couches à la fois.
Planification par phasesPlan de phase approuvé comprenant les tâches, les critères d'acceptation et les attentes en matière de vérificationCela rend le projet suffisamment concret pour pouvoir être mis en œuvre ; les critiques facultatives formulées au stade de la planification permettent de repérer les faiblesses des plans avant que leur mise en œuvre ne devienne coûteuse.
Mise en œuvreCode, tests, fichier de suivi, rapport de vérificationPermet à une nouvelle fenêtre de contexte de reprendre là où la session précédente s'était arrêtée ; ce fichier de progression assure le bon déroulement de la boucle.
Révision finaleApprobation ou demandes de modification détailléesPermet à un réviseur qui n'a pas d'expérience en matière de mise en œuvre de déterminer si « terminé » signifie réellement « terminé ».

Tableau 1 : Explication de chaque étape du nouveau cycle de développement.

L'infrastructure de forage

 Les phases constituent la partie visible. En coulisses, l’orchestrateur se charge du travail ingrat qui transforme un flux de travail autrement manuel en un système capable de continuer à fonctionner même lorsque je ferme mon ordinateur portable. En arrière-plan, plusieurs composants assurent le bon fonctionnement du système :

  • Arbres de travail : chaque fonctionnalité s'exécute dans son propre arbre de travail Git, ce qui signifie que plusieurs fonctionnalités peuvent parcourir le pipeline en s'appuyant sur le même dépôt sans se marcher sur les pieds.
  • Le TUI : cette interface propose un tableau de bord qui permet de suivre l'ensemble des tâches en cours : celles qui font l'objet de recherches, celles qui sont bloquées, celles qui doivent être relues et celles qui sont prêtes à être publiées.
  • Récupération après un plantage et gestion des sessions : comme l'état de la machine à états est conservé, vous ne perdrez pas votre progression en cas de fermeture du terminal, de session bloquée ou de plantage complet.
  • Actions après publication : même une fois la pull request publiée, l’orchestrateur continue de vous accompagner. Vous pouvez effectuer un rebase par rapport à la branche « main », demander des refactorisations ciblées, revenir à un état antérieur, peaufiner l’implémentation ou répondre aux commentaires de révision sans quitter l’interface TUI.
  • Transitions de phase déterministes : le code décide de la suite des opérations, conserve les artefacts et applique les étapes de validation, tandis que l’agent se charge de la réflexion ; il ne peut pas inventer le flux de travail au fur et à mesure.

C'est précisément cet aspect qu'il ne faut pas laisser aux agents. Les agents fonctionnent selon un modèle probabiliste ; ils peuvent perdre la trace de l'état, sauter une étape ou poursuivre leur chemin en toute confiance à partir d'une hypothèse erronée. Plus la couche d'intelligence devient imprévisible, plus il est précieux de la faire fonctionner dans un cadre ennuyeux et déterministe.

Est-ce que ça marche ?

Les résultats peuvent varier d'une personne à l'autre. 

En mars dernier, Anthropic a publié cet article sur les agents à exécution prolongée, dans lequel l'entreprise présentait un défi accompagné de la consigne suivante :

Créer un outil de création de jeux rétro en 2D comprenant notamment un éditeur de niveaux, un éditeur de sprites, des comportements d'entités et un mode de test jouable.

J'ai essayé la même chose avec Agentic Orchestrator en mode entièrement autonome — autrement dit, sans aucune intervention humaine. Au bout de 12 heures, pour un coût de 250 $ et sans aucun réglage supplémentaire, voici le résultat :

Certes, cette version utilisait Opus 4.7 pour toutes les phases, contrairement à la version 4.5 utilisée par Anthropic dans sa démo. 

Je commence à avoir l'impression que ça ressemble beaucoup à un expresso préparé avec Opus.

Alors, avons-nous résolu le problème du génie logiciel ?

Pas encore, mais nous sommes sur le point de résoudre le problème de codage.

Les modèles s’améliorent à un rythme tel que la planification trimestrielle semble désormais appartenir à une autre époque. Des fonctionnalités qui nécessitent aujourd’hui cinq itérations de boucle pourraient demain être réalisées en une seule fois. Nous continuerons à repousser les limites de la fenêtre de contexte, remettant ainsi en question la nécessité même du modèle de boucle. Le contrôle de révision qui détecte les erreurs d’un agent perdra de son importance à mesure que les agents commettront moins d’erreurs. Avec un peu de chance, l’Orchestrateur d’agents lui-même deviendra obsolète d’ici quelques semaines, rendu inutile par des modèles capables de garder une fonctionnalité entière en « mémoire » sans avoir besoin d’un fichier de progression pour se souvenir de ce qu’ils étaient en train de faire. Honnêtement, ce serait le meilleur scénario possible. Je ne l’ai pas conçu parce que je voulais créer un orchestrateur. Je l’ai conçu parce que les agents non supervisés n’étaient pas assez performants pour fonctionner sans lui.

Mais même dans un monde où les modèles seraient dix fois plus performants qu’aujourd’hui, il y a une chose dont je suis de plus en plus convaincu qu’elle ne changera pas : les connaissances humaines dans un domaine donné et notre compréhension fondamentale des systèmes ne peuvent être remplacées.

Voici ce que j’ai appris après avoir utilisé ce flux de travail pendant plusieurs semaines : la qualité du résultat dépend toujours de la qualité des données d’entrée. Les agents ne sont pas magiques. Ce sont des amplificateurs. Si vous leur fournissez une consigne vague, vous obtiendrez une implémentation vague. Si vous ne maîtrisez pas suffisamment le code pour répondre à leurs questions, ils feront des suppositions, et ces suppositions seront erronées.

Le métier d'ingénieur logiciel évolue, il ne disparaît pas.

Lorsque votre langage de programmation est l’anglais, les compétences qui comptent évoluent. Vous devez toujours être un bon ingénieur, sans doute plus encore qu’auparavant, mais la nature du travail change. Il y a moins de saisie et plus de réflexion, moins de syntaxe et plus d’architecture. Nous cesserons de nous demander comment implémenter quelque chose pour commencer à nous demander ce que nous devrions implémenter et pourquoi. La valeur d’un ingénieur logiciel ne réside plus dans l’écriture de code. Elle réside dans sa capacité à savoir à quoi ressemble le bon code et à l’exprimer clairement afin qu’un agent puisse le produire.

Le modèle sur lequel je me suis arrêté ressemble à peu près à ça : pendant la journée, l’humain fait un travail d’humain. Vous rédigez des consignes. Vous répondez à des questions. Vous examinez des plans. Vous prenez des décisions concernant les cas limites et les compromis. Ensuite, les agents se mettent au travail et s’occupent de la mise en œuvre pendant des heures — pendant la nuit, pendant la pause déjeuner ou pendant que vous êtes en réunion. Lorsque vous revenez devant votre ordinateur, vous pouvez examiner la pull request, peaufiner ce qui doit l’être, publier, puis passer à autre chose. L’humain apporte l’intention et le jugement. La machine fournit le travail et l’endurance. C’est la délégation dans sa forme la plus pure et, comme toute délégation, cela exige que celui qui délègue sache réellement de quoi il parle.

Réflexion sur la charge de travail, la dépendance et la santé mentale

Je voudrais changer de ton ici, car cette partie est importante.

Dans le débat sur la productivité liée à l’IA, on entend souvent un argument qui va à peu près comme suit : « Les outils d’IA vous permettent de gagner du temps afin que vous puissiez vous concentrer sur le travail créatif et à forte valeur ajoutée. » Cela semble formidable. Mais d’après mon expérience, c’est en grande partie faux.

Une récente étude du BCG menée auprès de 1 488 salariés a mis en évidence un constat qui a profondément marqué les esprits : les auto-évaluations de productivité augmentaient lorsque les salariés utilisaient entre un et trois outils d’IA, mais chutaient brutalement à partir de quatre outils. Les salariés ont fait état d’un effort mental accru, d’une fatigue mentale plus importante et d’une surcharge d’informations plus forte lorsque l’IA nécessitait une supervision plus importante. L’auteure de l’étude, Julie Bedard du BCG, a expliqué que les salariés avaient le sentiment d’« atteindre les limites de leurs capacités intellectuelles ».

Et je l’ai ressenti, moi aussi. Voici ce qui s’est réellement passé lorsque j’ai réussi à faire fonctionner sans heurts le flux de travail « agentique ». Je n’ai pas travaillé moins d’heures. J’ai travaillé le même nombre d’heures, mais avec une intensité nettement plus élevée. Au lieu de passer huit heures à écrire du code, je passais huit heures d’affilée à prendre des décisions, à passer d’une fonctionnalité à l’autre, à vérifier les implémentations, à répondre aux questions des agents et à rédiger des invites. Les temps morts mécaniques, la saisie, le débogage, le répit dont je disposais pendant que j’écrivais le code standard et que je réfléchissais à ce que je faisais avaient disparu. Chaque minute était consacrée à la prise de décisions. Chaque minute augmentait ma charge cognitive.

Je me retrouvais plus fatigué à la fin de la journée, et non pas moins. J’étais plus épuisé, et non pas plus plein d’énergie. Et il y a une subtile dépendance à cela : quand on peut lancer une fonctionnalité avant d’aller se coucher et se réveiller avec un PR, la tentation d’en lancer trois avant d’aller se coucher est irrésistible. Les agents, eux, ne se fatiguent pas. Mais vous restez le goulot d’étranglement pour chaque question qu’ils posent et chaque projet qu’ils proposent. Le travail s’accumule, et si vous ne faites pas attention, il vous écrase avec lui.

Je n'ai pas de solution toute faite à ce problème. Nous traversons une période de transition délicate où les outils ont pris de l'avance sur notre capacité à les utiliser de manière durable. Nous sommes tous en train de découvrir les aspects pratiques d'une nouvelle façon de travailler, et prétendre que tout n'est que positif rend un mauvais service à tous ceux qui tentent de s'y adapter.

Faites des pauses. Fixez-vous des limites. N’oubliez pas que les agents seront toujours là demain matin. Et si vous vous surprenez à actualiser le tableau de bord à minuit pour voir si la validation a été effectuée, fermez votre ordinateur portable. Je parle d’expérience. Les relations publiques peuvent attendre.

Allez, lancez-vous dans un projet !

J'ai commencé par dire que j'étais, au mieux, un utilisateur de niveau intermédiaire. Même après avoir écrit tout cela, je continue de le penser. La différence, c'est que je connais désormais le tracé de la courbe d'apprentissage et que je peux vous dire : ça vaut le coup de la gravir.

Voici ce que je dirais à quelqu’un qui part de zéro aujourd’hui : n’essayez pas de faire la montagne en un jour. Commencez par une petite tâche sur Cursor ou Claude Code. Familiarisez-vous avec le rythme consistant à formuler des prompts, à réviser et à itérer. Ensuite, essayez le cadre RPI sur une seule fonctionnalité : commencez par la recherche, puis la planification, puis la mise en œuvre. Pas besoin de fioritures. Il suffit d’ouvrir trois sessions Claude, une pour chaque phase, et de transférer les éléments manuellement. Constatez à quel point c’est différent lorsque l’agent dispose d’un contexte issu de ses propres recherches plutôt que d’une instruction que vous avez rédigée à la hâte.

Une fois que vous aurez compris cela – et vous le comprendrez –, vous commencerez à repérer les lacunes qu’un meilleur outillage peut combler. Peut-être développerez-vous votre propre orchestrateur. Peut-être utiliserez-vous le mien. Peut-être qu’au moment où vous lirez ces lignes, il existera déjà une solution meilleure que les deux. Ce n’est pas grave. Les frameworks et les outils ne cesseront d’évoluer. Les modèles mentaux, eux, resteront les mêmes, du moins pas aussi rapidement. Faites des recherches avant de planifier. Planifiez avant de mettre en œuvre. Faites une boucle lorsque vous manquez de contexte. Laissez l’agent poser des questions. Et connaissez suffisamment bien votre base de code pour y répondre.

La machine à expresso n'est pas encore entièrement automatique. Mais ce n'est plus non plus une préparation manuelle. On se situe quelque part entre les deux, dans la catégorie des machines semi-automatiques, et la qualité des expressos s'améliore de semaine en semaine.

Maintenant, lancez-vous et faites des essais. Et quand vous trouverez une solution qui marche, partagez-la, car je vous garantis que quelqu’un d’autre se trouve exactement dans la même impasse que vous le mois dernier.

DoorDash s'appuie sur de nombreux microservices. Une seule requête utilisateur peut nécessiter des données provenant de plusieurs services, telles que les métadonnées des restaurants, les menus, les disponibilités, les tarifs et le contexte de traitement des commandes. Au début de l'histoire de l'entreprise, alors que le trafic de DoorDash augmentait, de nombreux services interrogeaient à plusieurs reprises les mêmes données peu sujettes aux modifications dans des intervalles de temps très courts.

Même lorsque les données n'avaient pas changé, chaque requête déclenchait tout de même des appels gRPC, des calculs répétés et, parfois, des lectures dans la base de données backend. À l'échelle de DoorDash, cela entraînait une consommation inutile de ressources du service, une charge plus importante sur la base de données et une détérioration des latences P90/P99.

Ces appels répétés ont également accru le risque lié à la fiabilité. Lorsqu’une dépendance devenait lente ou indisponible, les tentatives de réessai et la concurrence croissante pouvaient répartir la pression sur les services dépendants, transformant ainsi un petit problème en un incident de plus grande ampleur.

Les caches locaux se sont avérés utiles, mais ils étaient spécifiques à chaque service, manquant de cohérence et obligeaient chaque équipe à développer et à gérer sa propre logique de mise en cache. Nous avions besoin d'une couche de mise en cache partagée capable de réduire les tâches redondantes, de protéger les services en amont en cas de pannes partielles et pouvant être mise en place sans modification du code des applications.

C'est ce qui nous a poussés à créer Entity Cache.

Ce que nous avons réalisé

Entity Cache est un proxy de mise en cache HTTP/gRPC transparent fonctionnant au sein du maillage de services de DoorDash basé sur Envoy. Il s'intègre dans le chemin de requête et fournit les réponses les plus fréquemment consultées à partir d'un cache centralisé, sans que chaque service ait à implémenter sa propre logique de mise en cache.

Comme Entity Cache est intégré au maillage de services, les services continuent d'effectuer les mêmes appels HTTP ou gRPC qu'auparavant. L'intégration s'effectue via la configuration du maillage de services ; il n'est donc pas nécessaire de modifier le code des clients ni celui des services en amont.

Par exemple, un point de terminaison gRPC peut être acheminé via Entity Cache avec une configuration comme celle-ci :

- name: example-service/grpc/50051
  redirect:
    enabled: true
    entity_cache: true
    endpoints:
      - path: ^/example.v1.ExampleService/GetExample$
        headers:
          - name: :method
            string_match:
              exact: POST
        rollout: 1

En fournissant des réponses mises en cache avant même que les requêtes n'atteignent les services en amont, Entity Cache permet à DoorDash :

  • Réduire la latence en évitant les appels HTTP/gRPC répétés entre services.
  • Réduire la charge en amont en limitant les calculs répétés et les lectures de la base de données en arrière-plan.
  • Améliorer la fiabilité en fournissant des données mises en cache lorsque les services en amont sont lents ou indisponibles.
  • Réduction des coûts d'infrastructure grâce à la réduction de l'utilisation des ressources informatiques et des bases de données superflues.
  • Réduire la charge de travail technique en fournissant un cache centralisé au lieu d'obliger chaque service à créer sa propre couche de mise en cache.

Depuis son lancement, Entity Cache a été déployé sur de nombreux terminaux de niveau 0 et 1 à faible taux de mutation. Il traite désormais plus de 1,5 million de requêtes par seconde, atteint des taux de réussite de mise en cache supérieurs à 90 % sur de nombreux terminaux et est devenu une couche de performances et de fiabilité à l'échelle de la plateforme, reposant sur la structure de maillage de services de DoorDash.

Présentation générale de l'architecture de haut niveau

Entity Cache est déployé sous la forme d’un proxy de mise en cache dédié qui s’intègre à notre maillage de services basé sur Envoy. Les services clients n’ont pas à modifier leur code. Comme le montre la figure 1, le maillage de services achemine automatiquement les requêtes sortantes vers Entity Cache en premier lieu, le service en amont étant configuré comme solution de secours. Lorsqu’une requête arrive, Entity Cache vérifie s’il existe une réponse valide mise en cache. Si c’est le cas, cette réponse est renvoyée immédiatement ; en cas d’échec de mise en cache, la requête est transmise au service en amont, qui génère une réponse qui est ensuite mise en cache pour les requêtes futures.

Figure 1 : Entity Cache se situe entre le maillage de services et les services en amont ; il fournit les réponses mises en cache provenant de Valkey en cas de correspondance et transfère les requêtes sans correspondance vers l'amont afin d'obtenir des données actualisées. Les événements d'invalidation de Kafka et Cache Advisor fournissent tous deux des signaux relatifs à l'actualité des données et à leur intégration, afin que la mise en cache soit sécurisée, transparente et gérée de manière centralisée.
  • Chemin de la requête: Envoy achemine d’abord les requêtes vers Entity Cache, puis se rabat sur le service en amont si nécessaire. En cas de réussite de la mise en cache, Entity Cache renvoie la réponse directement depuis Valkey, en contournant les dépendances en amont. En cas d’échec, il transfère la requête au service en amont, stocke la réponse dans Valkey conformément à la politique de mise en cache, puis la renvoie au client.
  • Parcours d'invalidation du cache : Lorsque les données sous-jacentes changent, les services en amont émettent des événements de modification dans le cadre de leurs workflows d’écriture habituels. Ces événements transitent par Kafka vers Entity Cache. Au lieu de supprimer les entrées mises en cache sur toutes les instances, Entity Cache enregistre qu’une entité spécifique a été mise à jour et note l’heure de la mise à jour. À chaque requête, Entity Cache compare l’heure à laquelle la réponse a été mise en cache avec l’heure de mise à jour enregistrée. Si la réponse mise en cache est antérieure à l’heure de mise à jour, elle est considérée comme obsolète. Entity Cache récupère alors les données actualisées auprès du service en amont et met à jour le cache. Cette approche évite les opérations complexes de suppression distribuée tout en garantissant une mise à jour rapide des données.
  • Chemin d'analyse du trafic: Entity Cache vérifie en permanence l’exactitude des données à l’aide d’échantillons de trafic de production. Pour un sous-ensemble de requêtes, il compare les réponses mises en cache aux réponses en temps réel des serveurs en amont et calcule les écarts en arrière-plan. Ce mécanisme de validation garantit que la mise en cache reste précise au fil du temps et fournit une base de sécurité pour des déploiements contrôlés. Pour soutenir ce processus, nous avons développé Cache Advisor, un service distinct dédié à l’identification des points de terminaison présentant de faibles taux de mutation, qui constituent de bons candidats à la mise en cache. Il s’exécute de manière indépendante au niveau de l’ingress en amont en utilisant le filtre de traitement externe d’Envoy pour observer les modèles de requêtes et mettre en évidence ces opportunités.
  • Basculement automatique : Envoy bascule automatiquement vers le service en amont si Entity Cache devient indisponible.

Fonctionnalités mises en œuvre

Entity Cache n'est pas une simple optimisation, mais un ensemble de fonctionnalités axées sur la fiabilité et les performances, conçues pour fonctionner de concert à l'échelle de DoorDash. Cette section présente les mécanismes fondamentaux qui garantissent la sécurité, la prévisibilité et l'efficacité de la mise en cache en production.

1. Dégradation gracieuse avec deux TTL: comme le montre la figure 2, Entity Cache utilise deux seuils d’expiration : un délai de vie (TTL) « souple » qui définit la fraîcheur des données — par exemple, 60 secondes — et un TTL « strict » qui définit la durée de vie maximale absolue — par exemple, cinq minutes. Dans des conditions normales, les données mises en cache sont actualisées selon la définition du TTL souple. Lorsque les services en amont deviennent lents ou indisponibles, Entity Cache continue de fournir des données légèrement obsolètes à partir du cache plutôt que de propager des erreurs aux clients.

Figure 2 : Le cache d'entités utilise l'ancienneté des données pour déterminer s'il faut servir les données à partir du cache, les actualiser depuis la source ou demander des données à jour. Le point essentiel à retenir est que les TTL « souples » permettent de servir en douceur des données périmées lorsque la source est lente, tandis que les TTL « rigides » imposent une nouvelle récupération une fois l'ancienneté maximale atteinte.

Cette conception s'est avérée cruciale lors d'une panne en amont qui a duré plusieurs heures : alors que le cache d'entités a continué à fournir des données mises en cache obsolètes mais valides au lieu de tomber en panne, il a ainsi permis d'éviter un incident à l'échelle de la plateforme.

2. Invalidation du cache en temps réel à l’aide de listes d’exclusion : une suppression directe au sein d’un parc de proxys distribués nécessiterait de reconstruire les clés du cache, d’assurer la coordination entre les pods et de gérer les conditions de concurrence où des données obsolètes pourraient être réécrites immédiatement après leur suppression. Cela entraîne également une surcharge mémoire liée au suivi des associations de clés. L’approche par horodatage évite la coordination entre les pods et reste correcte même si les événements arrivent dans le désordre.

La mise en cache implique généralement un compromis entre des durées de vie (TTL) longues, qui favorisent les performances, et des durées de vie courtes, qui garantissent la cohérence. Entity Cache résout ce problème grâce à une invalidation pilotée par les événements, à l’aide d’une liste noire. Lorsque les données sous-jacentes changent, les services d’origine publient un événement d’invalidation vers Kafka. Un consommateur d’Entity Cache enregistre alors un horodatage d’invalidation dans Valkey pour l’entité et les identifiants concernés. Lors d’un accès au cache, Entity Cache vérifie cette entrée. Si l’objet mis en cache a été récupéré avant l’horodatage d’invalidation, il est considéré comme obsolète et actualisé depuis l’amont. Plutôt que de supprimer les clés du cache, le système s’appuie sur la comparaison des horodatages. Chaque objet mis en cache stocke son heure de récupération ; l’invalidation se contente d’enregistrer un horodatage plus récent. Cette conception permet aux points de terminaison de conserver des TTL souples de longue durée pour des taux de réussite élevés, tout en garantissant une cohérence quasi en temps réel, avec une latence d’invalidation P99 ciblée à environ une seconde.

3. Détection des valeurs aberrantes via Envoy — basculement automatique : Entity Cache s'intègre à notre maillage de services basé sur Envoy. Comme le montre la figure 3, Envoy surveille l'état de santé des pods de cache et exclut automatiquement les pods défaillants du pool d'équilibrage de charge. Si tous les pods de cache deviennent défaillants, Envoy achemine le trafic directement vers les services en amont, garantissant ainsi la disponibilité même en cas de défaillance totale du cache.

Figure 3 : Le service mesh achemine le trafic normal vers les pods Entity Cache opérationnels et exclut les pods défaillants du chemin de répartition de charge. Si le chemin du cache n'est pas disponible, le trafic est entièrement redirigé vers le service en amont, ce qui garantit la disponibilité.

Grâce à ce mécanisme de basculement automatique, Entity Cache renforce la résilience du système sans créer de nouveau point de défaillance unique. Lors de la mise en service, si une erreur de configuration provoque le plantage des pods de cache, le trafic est automatiquement redirigé vers les serveurs en amont. Les équipes peuvent ainsi activer la mise en cache en toute confiance.

Évolutivité jusqu’à plusieurs millions de requêtes par seconde

Le traitement de millions de requêtes par seconde a entraîné l'apparition de goulots d'étranglement au niveau du système, où de petites inefficacités se sont rapidement amplifiées. L'allocation de mémoire, la coordination des requêtes et le comportement du cache ont tous commencé à avoir un impact sur la latence en queue de file et la stabilité en amont. Pour maintenir les performances, nous nous sommes attachés à réduire les conflits d'accès, à lisser les flux de trafic et à éliminer les tâches redondantes sur les chemins les plus sollicités.

  • Gestion de la mémoire à grande échelle : les allocations fréquentes sur les chemins d'accès les plus sollicités ont accru la pression exercée sur le ramasse-miettes (GC) et provoqué des pics de latence. Nous avons mis en place un système personnalisé de mise en pool des tampons afin de réutiliser la mémoire et de réduire au minimum les allocations. Cela a permis de réduire la surcharge liée au GC, de stabiliser l'utilisation de la mémoire et de ramener la latence de surcharge des requêtes P99 à environ 2,1 ms, tout en maintenant un débit constant.
  • Ruée massive sur le cache : pour les entités très sollicitées, l'expiration basée sur le TTL concentrait le trafic de rafraîchissement à un moment précis, ce qui provoquait des pics de charge en amont. Nous avons mis en place un rafraîchissement anticipé probabiliste basé sur l'algorithme XFetch. À mesure que les entrées approchent de leur TTL souple, chaque requête a de plus en plus de chances de déclencher un rafraîchissement. Cela permet de répartir le trafic dans le temps, ce qui réduit les pics et stabilise la charge en amont.
  • Concurrence à l’échelle de millions de requêtes : les requêtes simultanées entraînaient souvent une duplication du travail, notamment lors de l’établissement de la connexion et en cas d’échec de mise en cache sur les clés les plus sollicitées. Nous avons mis en place un modèle de traitement unique utilisant des structures atomiques sans verrouillage. Le mécanisme « Compare and Swap » garantit qu’une seule goroutine effectue une connexion ou une récupération par clé, tandis que les autres réutilisent le résultat. La déduplication s’effectue au niveau de chaque pod, sans coordination entre les pods. Cela a permis d’éliminer le travail redondant et de réduire les conflits d’accès. Le débit par pod a été multiplié par cinq environ, les taux d’allocation ont diminué de 50 % à 60 %, et les pics de latence P99 ont été réduits jusqu’à 80 %.
  • Déterminer les éléments à mettre en cache : avec des milliers de points de terminaison, il n'était pas envisageable, d'un point de vue évolutif, d'identifier manuellement les éléments les plus susceptibles d'être mis en cache. 

Nous avons développé Cache Advisor afin d’analyser le trafic réel en production et de faciliter la mise en place de la mise en cache. Exécuté au niveau de l’ingress via le filtre de traitement externe d’Envoy, cet outil échantillonne les requêtes, surveille la stabilité des réponses et estime la fréquence de mise à jour. Il recommande des valeurs de TTL et met en avant les candidats les plus prometteurs. Cela a permis d’identifier plus de 130 opportunités de mise en place, favorisant ainsi une adoption plus large et plus sûre de la mise en cache.

Impact sur les utilisateurs et l'entreprise

Entity Cache est devenu un élément central de la fiabilité chez DoorDash, traitant désormais plus de 1,5 million de requêtes par seconde avec 99,99999 % . Plus de 100 points de terminaison répartis sur 50 services ont été intégrés, apportant des améliorations mesurables en termes de latence, d’évolutivité et de résilience face aux pannes.

Du point de vue des performances, Entity Cache n'ajoute qu'une surcharge minime tout en offrant des gains substantiels :

  • Latence liée à la gestion des requêtes P99 d'environ 2,1 ms
  • Taux de réussite du cache constamment supérieur à 90 %
  • Réduction de 60 % à 95 % des requêtes en amont en conditions normales de fonctionnement
  • Réduction de la latence pouvant atteindre 90 % pour les terminaux nouvellement intégrés
  • Même pour les services dotés de caches locaux, la latence de bout en bout est souvent réduite de près de moitié, car le nombre d'appels réseau entre services à la sortie du client est moindre.

Au-delà des performances, Entity Cache a considérablement renforcé la fiabilité de la plateforme. Lors de plusieurs incidents en production, il a empêché jusqu’à 90 % des requêtes d’atteindre des services en amont dont la disponibilité était dégradée. En fournissant, lorsque cela était nécessaire, des données obsolètes mais valides, il a protégé les services contre les pannes en amont, éliminé l’amplification due à l’afflux massif de requêtes grâce à un rafraîchissement probabiliste, et réduit l’ampleur des pics de trafic.

La surveillance continue des divergences permet une mise en œuvre en toute sécurité, ce qui donne aux équipes la confiance nécessaire pour activer la mise en cache sans compromettre l'exactitude des données. Si les économies réalisées sur les coûts d'infrastructure constituent un effet secondaire, la réduction de la charge en amont a également permis de diminuer les besoins en provisionnement et d'améliorer l'efficacité globale de la plateforme.

Prochaines étapes

  1. Répartition dynamique du trafic basée sur la comparaison des taux d'erreur : aujourd'hui, la répartition du trafic entre le cache d'entités et l'amont repose sur une configuration statique. Nous mettons en place un système de répartition dynamique du trafic qui ajuste le routage en fonction de comparaisons en temps réel des taux d'erreur, en acheminant davantage de trafic vers l'amont lorsque l'état du cache se détériore, puis en rééquilibrant le trafic à mesure que cet état s'améliore.
  2. Isolation des « voisins bruyants » par cluster de cache en amont : actuellement , tous les services partagent un seul cluster de cache. Nous nous orientons vers la mise en place de clusters de cache dédiés à chaque service à fort trafic afin d'établir des limites strictes en matière de ressources, ce qui empêchera les problèmes rencontrés par un service d'affecter les autres.
  3. Limitation adaptative de la concurrence : les limites de concurrence statiques ne sont pas optimales, car elles entraînent soit un gaspillage de capacité en période de faible charge, soit des pics de latence en période de forte charge. Nous mettons en place une limitation adaptative qui mesure en continu les performances du système et ajuste automatiquement la concurrence en fonction de la latence et du débit observés, afin d'optimiser la capacité tout en respectant les objectifs de latence.

Remerciements

La mise en place du « Building Entity Cache » a été le fruit d'un travail collaboratif entre de nombreuses équipes chez DoorDash. Nous tenons tout particulièrement à rendre hommage à l'équipe Core Infra : Dakota Baber, Hochuen Wong, Yifan Yang et Tejas Lodaya, dont les contributions ont été inestimables pour la mise en place, l'exploitation et la mise à l'échelle de la plateforme.

Nous tenons également à remercier Ivar Lazzaro, Muneeb Ansari, Karthik Katooru, Pushkar Raste, Matt Ranney, Thai Pham, Allen Meng, Madhav Gali, Jay Weinstein, Matt Zimmerman et Sebastian Yates pour leurs conseils, leurs commentaires et leur soutien tout au long de ce projet.