Revenir au contenu principal

coSTAR : Comment nous déployons rapidement des agents IA chez Databricks, sans rien casser

Comment nous sommes passés de révisions manuelles de deux semaines à des tests et raffinement automatisés en quelques heures

coSTAR

Publié: 20 mars 2026

AI19 min de lecture

Summary

  • Chez Databricks, nous construisons et déployons des agents en utilisant une méthodologie complète et automatisée de test et de raffinement appelée coSTAR (coupled Scenario, Trace, Assess, Refine) que nous avons développée à l'aide de MLflow. La méthodologie est structurée autour d'une analogie avec le développement logiciel traditionnel, utilisant des LLM comme suite de tests et un assistant de codage pour raffiner automatiquement l'implémentation de l'agent jusqu'à ce que les tests réussissent.
  • Cette méthodologie a éliminé la boucle de développement manuelle lente précédente "exécuter, réviser, corriger, répéter", qui était sujette aux régressions et manquait de confiance. coSTAR a réduit le temps de vérification des changements de deux semaines à quelques heures, permettant une vélocité de développement plus élevée.
  • Les mêmes tests sont exécutés en production pour détecter les problèmes sur le trafic utilisateur réel, et dans le cadre de nos pipelines CI/CD, nous aidant à signaler les régressions causées par des changements dans l'infrastructure dépendante.

Vous ne laisseriez jamais un assistant de codage refactoriser votre base de code sans une suite de tests. Sans tests, l'assistant navigue à l'aveugle. Il pourrait corriger une fonction et en casser silencieusement trois autres. Ce sont les tests qui bouclent la boucle : exécutez-les, observez les échecs, corrigez le code, exécutez-les à nouveau. Pas de tests, pas de confiance.

Chez Databricks, nous développons et déployons continuellement des agents qui couvrent un large éventail de fonctionnalités, des nouvelles fonctionnalités de la plateforme Databricks (par exemple, les capacités d'ingénierie des données, d'analyse de traces et d'apprentissage automatique dans Genie Code), aux projets OSS (par exemple, l'assistant MLflow), aux flux de travail d'ingénierie internes (par exemple, le support sur appel ou les réviseurs de code automatisés). Ces agents peuvent effectuer des tâches de longue durée, générer des milliers de lignes de code et créer de nouveaux actifs de données et d'IA, entre autres choses. Bien que nous ayons mis en place quelques vérifications de base dès le début, nous manquions du type de suite de tests complète et automatisée qui nous permettrait d'itérer en toute confiance. Ce billet décrit comment nous avons comblé cette lacune en utilisant MLflow et la méthodologie coSTAR (coupled Scenario, Trace, Assess, Refine) que nous avons développée autour. coSTAR exécute deux boucles couplées : une qui aligne les juges sur le jugement humain expert afin qu'ils puissent être fiables, et une qui utilise ces juges fiables pour affiner automatiquement l'agent jusqu'à ce qu'il réussisse tous les scénarios de test.

coSTAR

Figure : Le framework coSTAR exécute deux boucles STAR miroirs (Scenario → Trace → Assess → Refine). La boucle de l'agent (bleu) utilise des juges pour évaluer automatiquement les traces et affine l'agent pour l'aligner sur les juges. La boucle du juge (orange) utilise des experts humains pour évaluer les traces et affine les juges pour les aligner sur leurs évaluations. Les deux boucles partagent les mêmes scénarios et traces.

Le problème : coder sans tests

Au début, notre boucle de développement ressemblait à ceci : exécuter l'agent, examiner manuellement sa sortie, repérer un défaut, demander à un assistant de codage de le corriger. Répéter.

Si cela vous rappelle l'écriture de code sans tests et le contrôle qualité manuel de chaque modification, c'est exactement ce que c'était. Et cela a échoué exactement comme vous pourriez le prédire. La réaction évidente est « alors écrivez des tests ». Mais le test d'agent est structurellement différent du test d'une fonction déterministe, et plusieurs défis se cumulent à la fois :

  • Non-déterminisme. La même implémentation, la même entrée, peut produire des sorties différentes lors d'exécutions différentes. Les tests doivent évaluer les propriétés de la sortie plutôt qu'affirmer des sorties exactes.
  • Boucles de rétroaction lentes. Une seule exécution d'agent peut prendre des dizaines de minutes. Il n'y a pas d'itération comme le permet une suite de tests de moins d'une seconde. Chaque cycle d'évaluation est coûteux.
  • Erreurs en cascade. Une mauvaise décision à l'étape 3 provoque un échec à l'étape 7. Au moment où le symptôme apparaît, la cause première est enfouie plusieurs étapes en arrière dans l'exécution de l'agent.
  • Qualité subjective. Pour de nombreuses dimensions de test (ce code d'ingénierie de fonctionnalités est-il bon ? cette approche de nettoyage de données est-elle appropriée ?), il n'y a pas de vérité terrain. Le jugement de ces dimensions dépend de l'expertise du domaine.

Ces contraintes ont façonné chaque décision de conception qui suit. Elles expliquent également pourquoi ce problème est intéressant : nous ne construisons pas seulement un exécuteur de tests, nous construisons une méthodologie d'optimisation automatisée pour des processus stochastiques, de longue durée et multi-étapes où « correct » est une question de jugement.

L'analogie qui guide notre approche

Si vous plissez les yeux, le développement d'agents s'aligne parfaitement sur la boucle de développement que chaque ingénieur connaît déjà :

Logiciel traditionnelDéveloppement d'agents
Code sourceImplémentation de l'agent (y compris les invites, les choix de FMs, les outils)
Suite de testsJuges LLM
Fixtures de test (configuration, entrée, sortie attendue)Définitions de scénarios (état initial, invite, attentes)
Exécuteur de tests / harnaisLe harnais de test exécute l'agent sous test, produit des traces
Correction des tests (les tests vérifient-ils la bonne chose ?)Alignement des juges (le juge est-il d'accord avec les experts humains ?)
L'assistant de codage corrige le code jusqu'à ce que les tests réussissentL'assistant de codage affine l'implémentation jusqu'à ce que les juges réussissent
CI exécute tous les tests à chaque changementCI exécute les scénarios + les juges à chaque changement
Surveillance de la productionLes mêmes juges s'exécutent sur le trafic en direct

Cette analogie n'est pas seulement illustrative. C'est l'architecture littérale de notre système, que nous appelons coSTAR : deux boucles couplées qui utilisent des définitions de Scénarios comme fixtures de test, la capture de Traces comme harnais de test, l'Assessment avec des juges comme suite de tests, et le Raffinage comme boucle rouge-vert. Examinons chaque élément.

S - Définitions de scénarios

Dans les tests traditionnels, une fixture de test configure les préconditions : créer une base de données, l'amorcer avec des données, configurer l'environnement. Notre équivalent est une définition de scénario : une description structurée de l'état initial, de l'invite utilisateur et des résultats attendus.

Voici un scénario simplifié pour tester un agent Data Analyst sur un jeu de données désordonné :

Chaque scénario regroupe la configuration, l'entrée et les critères de succès en un seul endroit, tout comme une fixture de test. Nous en maintenons une suite pour différents agents, couvrant les cas courants, les cas limites et les échecs passés connus. La suite grandit avec le temps à mesure que nous découvrons de nouveaux modes d'échec : chaque bug que nous trouvons en production devient un nouveau scénario, de la même manière que chaque bug de production devrait devenir un test de régression.

Pourquoi s'embêter avec cette structure ? Parce que les exécutions d'agents sont coûteuses. Un seul scénario prend des minutes à exécuter. Nous devons être délibérés quant à ce que nous testons, et nous avons besoin que les définitions de scénarios soient portables : le même scénario peut être exécuté contre différentes implémentations d'agents ou différentes versions du même agent.

T - Capture de traces

Pour exécuter notre suite de tests, nous utilisons un harnais qui envoie l'invite de chaque scénario à l'agent sous test (AUT). Chaque exécution est capturée sous forme de trace MLflow : un journal structuré de chaque appel d'outil, de chaque sortie intermédiaire et de chaque artefact que l'agent produit. Pensez-y comme à un enregistreur de vol : il capture tout ce que l'agent a fait, dans l'ordre, afin que nous puissions inspecter n'importe quelle partie de l'exécution après coup.

Une décision architecturale clé : nous découplons l'exécution de l'évaluation. Le harnais de test produit des traces ; les juges (que nous présenterons ensuite) les évaluent. Ce sont des étapes distinctes. En persistant les traces, nous pouvons itérer sur les juges sans réexécuter les scénarios. Modifier un seuil ? Réévaluer les traces enregistrées en quelques secondes. Ajouter un nouveau juge ? L'exécuter sur toutes les traces que vous avez jamais collectées. Vous suspectez qu'un juge est erroné ? Comparez ses verdicts aux enregistrements et déboguez-le hors ligne. Une exécution coûteuse d'un agent produit des données qui sont réutilisées plusieurs fois, y compris comme candidats pour l'Ensemble Doré que nous utiliserons plus tard pour aligner les juges.

A - Évaluation avec des juges

Les juges opèrent sur des traces et raisonnent sur les propriétés de l'exécution : l'agent a-t-il produit du code valide ? La sortie a-t-elle atteint un seuil de qualité ? L'agent a-t-il suivi le bon processus ? Comme mentionné précédemment, cette évaluation est différente des tests unitaires traditionnels : la sortie de l'agent est non déterministe et riche, et donc l'affirmation de sorties exactes est essentiellement inutile.

L'approche standard pour implémenter ces juges est « LLM-en-tant-que-Juge » : alimentez la trace complète dans un modèle et demandez un score et, surtout, une justification de ce score. Cependant, c'est comme écrire un test qui déverse l'intégralité de l'état du programme dans une assertion. C'est coûteux, fragile et difficile à déboguer. Pour nos agents, une seule trace peut contenir des milliers de lignes. La faire entrer dans la fenêtre de contexte d'un juge dégrade la qualité du jugement.

Au lieu de cela, nous utilisons les juges agentiques de MLflow : des juges qui sont eux-mêmes des agents, équipés d'outils pour explorer la trace de manière sélective. Tout comme un test bien écrit appelle une fonction spécifique et vérifie une valeur de retour spécifique, un juge agentique appelle un outil spécifique sur la trace et vérifie une propriété spécifique.

Voici quelques exemples de juges que nous avons utilisés pour nos agents :

Le juge d'invocation de compétence explore la trace et identifie si l'agent a invoqué des compétences ciblées par le scénario (sinon, le but de la compétence n'est pas clair pour l'AUT) :

Le juge des meilleures pratiques vérifie si la sortie suit les meilleures pratiques selon la documentation officielle de Databricks :

Outcome Judge inspects the trace for output assets and asserts certain properties. Going back to the Data Analyst example, identify the part of the trace where engineering code was authored and evaluate whether the code is appropriate for the task at hand:

This judge is interesting because it tackles the subjective quality problem head-on: what counts as good feature engineering depends on domain expertise. An LLM judge can't get this right out of the box. It's tempting to try writing out the complete criteria in the judge's prompt: "prefer median imputation over mean for skewed distributions, always scale features before distance-based models, ..." But encoding a domain expert's full judgment into a prompt is laborious and brittle. It's much easier for humans to look at an example and say "this is good" or "this is bad" than to write out the complete spec. This is exactly why alignment works, as we'll cover shortly.

In general, our test suite for a single agent includes judges across several categories:

Deterministic checks, things we can verify mechanically, no LLM needed:

  • Syntax/linting on generated code
  • Output schema validation (do expected tables exist? are column types correct?)
  • Tool sequence linting (did the agent read the error logs before trying to fix the issue, or did it skip straight to editing code?)

LLM-based checks, judgment calls that require understanding context:

  • Code diff guidelines (did the agent change unrelated lines? did it introduce deprecated APIs?)
  • Best practice adherence (is the generated code following the conventions for this domain?)

Operational metrics, signals that don't pass/fail individually but track health over time:

  • Token usage (high token counts often signal the agent is struggling, retrying, backtracking, or going in circles)
  • Tool call counts and failure ratios (a spike in failed tool calls indicates something is wrong)
  • Latency (wall-clock time for the agent to complete the task)

The operational metrics deserve a note. They don't gate a release the way pass/fail judges do, but they're critical for cost management and early warning. If token usage doubles after a change, something went wrong even if all judges still pass; the agent is probably doing more work than it should. We track these over time and alert on anomalies.

Growing the test suite over time

Test suites don't get authored in one sitting. They evolve over time. They start with the simplest checks that give a signal: does the output exist? Does it parse? Then structural checks follow: does the output have the right schema, the right columns, the right types? Only later come end-to-end data validation judges: does the output actually produce correct results when you run it?

This mirrors how test suites mature in traditional software. Exhaustive integration tests don't come on day one. It starts with smoke tests, then unit tests as failure modes emerge, building toward end-to-end coverage over time. The key is that the infrastructure supports adding new judges cheaply, so the test suite grows alongside the agent.

Testing the Tests: Judge Alignment

Here's a problem every engineer knows: a flaky or wrong test suite that greenlights bad code ships bugs with confidence. Similarly, judges who approve poor outcomes give a false sense of security. This is where the second loop of the coSTAR framework comes in: the same scenarios and traces that drive agent refinement also drive judge refinement, with human expert scores as the ground truth. This matters because, unlike traditional testing where test correctness can be verified by inspection, LLM judges are stochastic and can drift in how they interpret natural-language criteria. So we need a way to verify them and keep them aligned with human experts.

To do this alignment, we first curate a Golden Set of typically dozens of examples of agent outputs that our engineers have manually assessed. This is the ground truth the judges must agree with. Then we leverage MLflow's alignment capabilities (powered by techniques like GEPA and MemAlign) to automatically refine the judge against the Golden Set. Notice this is structurally the same STAR loop we use to refine the AUT itself, but the assess step is performed by human experts and the refine step applies to the judge.

UN LEADER 5X

Gartner® : Databricks, leader des bases de données cloud

R - Refine

With judges that the judge loop has aligned against human expert judgment, we can now trust the agent loop. A coding assistant treats the agent as its codebase and the judges as its test suite. It reads failures, diagnoses root causes, patches the agent, and re-runs everything. The engineer is still the reviewer and final arbiter of the proposed changes to the agent, but this automated iteration saves considerable human effort in analyzing and improving the agent.

Here's what one iteration looked like for the Data Analyst agent:

Red. We ran the initial version of the agent against our scenario suite. The best-practices judge flagged a discrepancy: our agent was generating code for logical views that was different from our official recommendations/documentation. While this discrepancy would not affect correctness, it had implications on the maintenance and deployment of the generated code. This is an example of an insidious regression that would be hard to catch by manual investigation.

Green. The coding assistant analyzed the judge feedback and identified the gap: the agent was using a skill that was not prescriptive about the type of views that should be created (temporary vs permanent). After adding the relevant guidance to the skill, the tests passed successfully and the change was verified to not introduce other regression (based on other test scenarios).

Regression Tests for Infrastructure, Not Just the Agent

So far we've described judges as tests for the agent, catching regressions when the agent implementation changes. But in practice, the agent itself isn't the only thing that changes. The agent depends on external tools and infrastructure, and those change too.

Our agents call MCP tools, standardized interfaces for data access, code execution, environment setup, and more. These tools have their own development teams and release cycles. When a tool changes its implementation (say, a code execution tool starts returning stderr in a different format, or a data access tool changes how it handles null values) the agent hasn't changed at all, but the agent's behavior can break.

Because we run our judges on every nightly build, they act as regression tests against the full stack, not just the agent’s current implementation. When a tool team ships a change that causes an agent to start failing its judges then we catch the error immediately, before it reaches customers. More importantly, the judge's failure tells us what broke (the specific quality dimension that regressed), which makes it far easier to triage whether the root cause is in the agent or in a tool the agent depends on.

This is the same value that integration tests provide in traditional software: they guard the contract between the code and its dependencies. The only difference is that here, the "code" is an agent and the "dependencies" are MCP tools.

From Eval to Production Monitoring

There's one more extension of the testing analogy that turned out to be surprisingly valuable: running the same judges on production traffic.

In traditional software, testing doesn't stop at CI. Production gets monitored too: error rates, latency percentiles, business metrics on live traffic. The same test logic that validates code in dev often reappears as health checks and alerts in prod.

We do the same thing. The judges we built for eval are designed to score any agent conversation, not just eval scenarios. So we run them (or a sampled subset) on real production conversations. This gives us:

  • Early warning on drift. If judge's pass rate drops on production conversations, something changed. Maybe a model upgrade degraded quality, maybe user prompts shifted in a way the agent handles poorly. We see it in the judge scores before we see it in user complaints.
  • Real-world signal for the test suite. Production conversations that judges flag as failures become candidates for new eval scenarios. This is how the test suite grows organically: real failures feed back into eval, closing the loop between production and development.
  • Cost monitoring at the agent level. We track token usage and tool call counts on production conversations. A quality-neutral change that triples cost is still a regression.

L'idée clé est que la même infrastructure de scoring (juges, métriques, traces enregistrées) sert à deux fins. Construisez-la une fois pour l'évaluation, et la surveillance de la production en découle comme un effet secondaire.

Où nous en sommes

Nous avons adopté cette méthodologie pour plusieurs agents que nous avons publiés sur la plateforme Databricks (par exemple, les capacités d'ingénierie des données, d'apprentissage automatique et d'analyse des traces dans Genie), des agents internes pour la productivité des développeurs, ainsi que d'autres agents destinés aux clients (par exemple, AI Dev Kit, ou l'assistant MLflow OSS). Dans l'ensemble, nous avons constaté des avantages tangibles :

  • Comparées aux évaluations manuelles, les suites de tests automatisées ont réduit le temps de vérification des modifications de 2 semaines à quelques heures. En conséquence, cela a permis à nos équipes de déployer des améliorations avec une plus grande vélocité.
  • Plusieurs suites de tests comptent désormais des centaines de scénarios de test par agent, ce qui augmente notre confiance dans la détection des régressions.
  • Les tests d'intégration ont signalé des changements dans l'infrastructure dépendante, ce qui nous a permis d'éviter les régressions en production. Parmi ces changements, citons le comportement de gestion des TODO dans le modèle sous-jacent, les changements impactant la latence ou les modifications du modèle.

MLflow a également joué un rôle déterminant en tant que plateforme de test GenAI, aidant nos ingénieurs à standardiser la méthodologie, à accélérer le développement des tests et à partager les meilleures pratiques entre les équipes.

Ce qui ne fonctionne pas (encore)

L'analogie des tests est également utile ici. Nos limitations correspondent à des problèmes de test familiers :

La génération de scénarios est manuelle (l'écriture des cas de test est coûteuse). Nous avons automatisé le scoring, l'alignement et l'optimisation, mais la génération des scénarios eux-mêmes reste une tâche humaine. Chaque scénario nécessite de définir un état initial réaliste, une invite significative et des attentes correctes. C'est le goulot d'étranglement qui limite la taille de la suite de tests, et une suite de tests étroite mène directement au problème suivant. L'automatisation de la génération de scénarios (synthèse de cas de test diversifiés et réalistes à partir des modèles de trafic de production ou des spécifications de l'agent) est un domaine de travail actif pour nous.

L'assistant de codage peut sur-apprendre (la suite de tests est trop étroite). Si la suite de tests ne couvre pas suffisamment de cas, l'assistant de codage concevra une implémentation d'agent qui excelle sur ces entrées spécifiques mais échoue sur de nouvelles. C'est l'équivalent agent d'écrire du code qui réussit les tests unitaires mais échoue en production. Nous atténuons cela en réintégrant les échecs de production dans l'évaluation et en élargissant la couverture au fil du temps, mais tant que la génération de scénarios ne sera pas automatisée, la suite de tests se développera plus lentement que nous ne le souhaiterions.

L'alignement des juges est coûteux (la calibration des tests nécessite un travail humain). La construction de l'Ensemble d'Or nécessite que des experts du domaine notent manuellement les sorties, exactement le goulot d'étranglement que nous essayons d'éliminer. Et ce n'est pas un coût unique : à mesure que les agents évoluent, les juges nécessitent une recalibration. Nous étudions des moyens de rendre cela plus intelligent en mesurant l'incertitude du juge, en identifiant les exemples spécifiques où le juge est sous-spécifié et où une étiquette humaine résoudrait l'ambiguïté. L'objectif est l'apprentissage actif pour l'alignement des juges : au lieu de demander aux experts de noter un échantillon aléatoire, ne présenter que les exemples où le juge est incertain et où l'avis d'un expert du domaine affinerait le plus ses critères.

Les échecs en plusieurs étapes sont difficiles à attribuer (analyse des causes profondes). Lorsqu'un agent échoue à l'étape 7 d'un pipeline en 10 étapes, la cause profonde se situe-t-elle à l'étape 7 ou à l'étape 3 ? Nos juges détectent le symptôme, mais l'assistant de codage corrige parfois la mauvaise étape, comme corriger un échec de test en modifiant la mauvaise fonction. Une meilleure traçabilité causale est un domaine de travail actif.

Les nouveaux modes d'échec passent inaperçus (lacunes de couverture). coSTAR optimise dans les dimensions couvertes par les juges. Si une nouvelle classe d'échec émerge qu'aucun juge ne vérifie, elle est invisible, tout comme un bug dans le code qu'aucun test n'exerce. coSTAR s'améliore *dans* sa suite de tests, mais il ne peut pas l'étendre par lui-même. Les humains doivent encore remarquer les nouveaux modes d'échec et ajouter des juges.

Points clés à retenir

  1. Le développement d'agents pose un problème de test. Sans évaluation automatisée, vous codez sans tests, et vous subirez les régressions que vous méritez.
  2. Donnez des outils aux juges, pas des traces. Un juge agentique qui appelle des outils ciblés est comme un test unitaire focalisé. Fournir la trace complète à un juge revient à injecter l'état du programme dans une assertion. Cela ne fonctionne pas à grande échelle.
  3. Testez vos tests. Les juges LLM sont stochastiques. Alignez-les sur des ensembles d'or notés par des humains de la même manière que vous valideriez une suite de tests par rapport à une spécification.
  4. Boucle fermée. Le véritable avantage est la boucle coSTAR complète : scénarios fiables, traces enregistrées, juges alignés et un assistant de codage qui affine l'agent jusqu'à ce que les tests réussissent. L'évaluation sans raffinement automatisé n'est que la moitié de l'histoire.
  5. Construisez une fois, surveillez partout. Les mêmes juges qui valident lors de l'évaluation peuvent surveiller la production. Un investissement, deux retours.
  6. Le couplage est essentiel. Le raffinement de l'agent n'est fiable que dans la mesure où les juges qui le pilotent le sont. Les deux boucles couplées de coSTAR — l'une qui établit la confiance dans les juges, l'autre qui utilise cette confiance pour affiner l'agent — sont ce qui rend le raffinement automatisé significatif plutôt que simplement rapide.

Nous construisons coSTAR dans le cadre de MLflow. Si vous êtes confronté à des problèmes similaires, nous aimerions en discuter.

  • Essayez Genie Code pour voir les fonctionnalités que nous avons publiées en utilisant la méthodologie coSTAR.
  • Suivez les tutoriels sur MLflow pour commencer à définir et utiliser des juges LLM pour le raffinement itératif des agents.

(Cet article de blog a été traduit à l'aide d'outils basés sur l'intelligence artificielle) Article original

Ne manquez jamais un article Databricks

Abonnez-vous à notre blog et recevez les derniers articles dans votre boîte mail.