Article original : How to Test and Improve AI Applications with an Evaluation Flywheel

En programmation traditionnelle, les développeurs s'appuient sur des tests unitaires pour détecter les erreurs dans les applications. Mais lors de la création de produits d'IA, ce filet de sécurité n'existe pas. Les réponses peuvent varier en fonction des mises à jour de modèles, des changements de données et des subtiles fluctuations des prompts ou des résultats de récupération (retrieval). Les méthodes de test habituelles, comme les tests unitaires avec Pytest ou Jest, les tests d'intégration ou les pipelines CI, ne parviennent pas à détecter les baisses de précision, les hallucinations ou les régressions, et ces échecs silencieux peuvent devenir de réels risques en production.

Dans cet article, vous apprendrez pourquoi les méthodes de test traditionnelles sont insuffisantes pour les systèmes d'IA et comment un volant d'évaluation (evaluation flywheel) peut être utilisé comme approche pratique pour tester et améliorer les applications d'IA. Les sections ci-dessous décomposent le volant d'évaluation étape par étape, de l'identification du problème à la mise en œuvre d'une boucle d'évaluation reproductible.

Table des matières

Pourquoi les tests traditionnels échouent-ils pour les applications d'IA ?

En programmation standard, les tests supposent un comportement déterministe. Cela signifie qu'une même entrée est censée toujours produire la même sortie. Par exemple :

def authenticate_user_age(age: int) -> str:
    limit = 18

    if age >= limit:
        return "Access granted"
    else:
        return "User doesn't meet the age limit"

# Test 
assert authenticate_user_age(20) == "Access granted"
assert authenticate_user_age(16) == "User doesn't meet the age limit"

La réponse de cette fonction est toujours prévisible. Vous pouvez écrire des tests une seule fois et avoir l'assurance qu'ils détecteront les erreurs indéfiniment.

Cependant, les modèles d'IA ne se comportent pas de la même manière à chaque fois ; ils génèrent des sorties basées sur des probabilités. Une requête telle que « meilleures pratiques de programmation » peut produire des conseils pertinents un jour, et des conseils obsolètes ou incomplets le lendemain. Ce décalage peut survenir en raison de changements dans le modèle sous-jacent, de mises à jour des composants de récupération ou d'une dérive progressive des données (data drift). Sans un processus d'évaluation structuré, ces incohérences s'immiscent en production sans être remarquées et peuvent discrètement affaiblir les performances du système.

Qu'est-ce que le volant d'évaluation ?

Le volant d'évaluation est un système d'amélioration continue où des cas de test représentant le comportement réel des utilisateurs passent par plusieurs étapes d'évaluation pour évaluer la sortie des modèles d'IA. Les résultats ne vous disent pas seulement si le système a réussi ou échoué, ils alimentent directement le cycle d'amélioration suivant.

┌─────────────┐
│  Collecter  │
│ cas de test │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│  Exécuter   │
│ évaluations │
└──────┬──────┘
       │
       ▼
┌─────────────┐      ┌─────────────┐
│ Identifier  │─────▶│  Améliorer  │
│ les échecs  │      │ le système  │
└─────────────┘      └──────┬──────┘
                            │
                            ▼
                       ┌─────────────┐
                       │   Répéter   │
                       └─────────────┘

Voici comment cela fonctionne en pratique :

  • Collecter des cas de test — Rassemblez des exemples issus d'interactions réelles d'utilisateurs ou créez des scénarios synthétiques. Ceux-ci doivent refléter le type de tâches et d'entrées que votre système doit gérer.

  • Exécuter des évaluations — Passez chaque cas de test à travers une série de vérifications. La vérification peut être programmatique (métriques automatisées comme les scores de pertinence ou les détecteurs d'hallucinations) ou nécessiter une révision manuelle (comme la vérification de l'exactitude de conseils juridiques ou de la cohérence de la voix de la marque).

  • Identifier les échecs — Détectez les points où le modèle se trompe, ce qui peut inclure des hallucinations, des réponses non pertinentes ou des erreurs sur des cas limites (corner-cases).

  • Améliorer le système — Sur la base de ces échecs, affinez les prompts, améliorez les données d'entraînement ou de récupération, ou ajustez les composants architecturaux.

  • Répéter le cycle — Ré-exécutez le système mis à jour sur les cas existants et les nouveaux cas collectés. Au fil du temps, cela développe et renforce votre suite d'évaluation et augmente la fiabilité du système.

Parallèles avec les pratiques familières

Si vous avez déjà écrit du logiciel, le volant d'évaluation vous semblera familier. Il reflète des modèles déjà utilisés en ingénierie. Par exemple :

Tests unitaires → Jeux de données d'évaluation
Les tests unitaires confirment qu'une fonction renvoie la bonne sortie. Les jeux de données d'évaluation jouent le même rôle pour l'IA : ce sont des requêtes et des réponses de référence (ground-truth) qui protègent contre les régressions.

Test-driven development (TDD) → Evaluation-driven development (EDD)
En TDD, vous écrivez les tests avant le code. En EDD, vous écrivez les cas d'évaluation avant de déployer des prompts ou de mettre à jour des modèles. Cela remplace les suppositions par des résultats vérifiables.

Pipelines CI/CD → Pipelines d'évaluation continue
Le CI/CD exécute des vérifications automatiquement à chaque changement de code. L'évaluation continue fait de même pour les modèles : elle exécute des contrôles de qualité automatisés chaque fois que vous ajustez un prompt, ré-entraînez ou remplacez un composant.

La différence clé est subtile mais importante. Les tests logiciels traditionnels vérifient si une fonction renvoie la bonne valeur ou le bon type. Les tests d'évaluation d'IA vérifient si le système produit le bon sens. C'est plus difficile à mesurer, mais le principe est le même : construire un filet de sécurité qui se renforce à chaque cycle.

Pourquoi les échecs silencieux sont importants : un exemple concret

Les systèmes d'IA se comportent souvent différemment en production par rapport au développement. Un modèle qui semble solide lors des tests peut dériver, halluciner ou échouer silencieusement face à des entrées réelles.

Exemple concret : Un modèle de détection de fraude a passé toutes les métriques de surveillance mais a manqué un pic de fraude. Un ingénieur ML a partagé comment leurs tableaux de bord de surveillance de production suivaient la latence, le débit et les taux d'erreur, et tout était au vert. Pourtant, les transactions frauduleuses passaient à un taux deux fois supérieur à la normale. Personne ne l'a remarqué car les outils d'observabilité existants se concentraient sur la santé du pipeline, pas sur la qualité des prédictions.

Cet échec silencieux a coûté des pertes importantes à l'entreprise. Le système semblait correct selon les métriques traditionnelles. Il mesurait les performances du système — latence, débit, disponibilité — mais ignorait ce qui importait le plus : la précision des prédictions. À mesure que les fraudeurs adaptaient leurs tactiques, le modèle a dérivé, et sans boucles d'évaluation appropriées, la dégradation est passée inaperçue pendant des semaines.

Source : InsightFinder.

Pourquoi cet exemple est important

  • Les échecs silencieux ne sont pas toujours des bugs — Ils proviennent souvent de l'incapacité des modèles à s'adapter aux changements de schémas dans le monde réel.

  • L'évaluation statique ne suffit pas — Vous avez besoin de boucles de rétroaction continues et réelles pour détecter quand les hypothèses ne sont plus valables.

  • La dérive des données a un impact business — La dégradation du modèle n'est pas seulement technique, elle se traduit directement par des pertes de revenus, des failles de sécurité ou une perte de confiance des utilisateurs.

Comment créer un volant d'évaluation

Pour montrer comment construire un volant et comment il fonctionne, créons-en un pour un chatbot de support client qui répond aux questions sur un produit SaaS.

Étape 1 : Construire votre système d'IA

Créez votre produit initial : prompts, logique de récupération et intégrations. Pour notre chatbot :

def answer_support_question(question: str) -> str:
    # Retrieve relevant docs from knowledge base
    context = retrieve_docs(question, top_k=5)

    # Generate answer using LLM
    prompt = f"""You are a helpful customer support agent.

Context: {context}

Question: {question}

Provide a clear, accurate answer based on the context."""

    response = llm.generate(prompt)
    return response

Comment ça marche : Cette fonction définit la logique de chat principale, elle prend la question d'un client et renvoie une réponse générée par l'IA. D'abord, elle recherche dans votre base de connaissances pour trouver les cinq documents les plus pertinents via retrieve_docs(). Ces documents fournissent un contexte sur votre produit ou vos politiques. Ensuite, elle construit un prompt qui inclut ce contexte et la question de l'utilisateur, puis l'envoie à un modèle de langage. Le LLM lit le contexte et génère une réponse pertinente, que la fonction renvoie.

Étape 2 : Identifier les cas de test

Construisez un ensemble d'évaluation qui reflète le comportement réel des utilisateurs. Plus vos cas de test sont représentatifs, incluant des cas courants, des cas limites et des entrées ambiguës, mieux votre modèle pourra détecter les échecs avant qu'ils n'atteignent la production.

Sources pour les cas de test :

  • Tickets de support client précédents

  • Sujets de FAQ courants

  • Cas limites découverts lors des tests bêta

  • Scénarios synthétiques (requêtes hypothétiques mais réalistes)

Exemple de cas de test :

test_cases = [
    {
        "question": "How do I reset my password?",
        "expected_elements": ["settings page", "reset link", "email"],
        "category": "account_management"
    },
    {
        "question": "What's your refund policy?",
        "expected_elements": ["30 days", "full refund", "contact support"],
        "category": "billing"
    },
    {
        "question": "Can I export my data to CSV?",
        "expected_elements": ["yes", "export button", "dashboard"],
        "category": "features"
    },
    {
        "question": "Does your API support webhooks?",
        "expected_elements": ["yes", "webhook endpoints", "documentation"],
        "category": "technical"
    }
]

Comment ça marche : Ici, nous définissons un ensemble de cas de test représentatifs pour évaluer le système d'IA. Chaque cas de test comprend la question de l'utilisateur, une liste d'éléments clés attendus dans la réponse et une catégorie pour l'organisation. Ces cas aident à garantir que le chatbot est testé par rapport à des scénarios réels, des cas limites et des informations importantes qui devraient apparaître dans les réponses.

Étape 3 : Évaluer les sorties

Définissez des critères d'évaluation basés sur ce qui compte pour votre cas d'usage : précision, fidélité (faithfulness), sécurité, pertinence, ton. Mesurez ensuite la sortie par rapport à ces critères.

L'évaluation se fait de deux manières principales :

Évaluation automatisée

Utilisez des métriques programmatiques et des modèles de type « LLM-as-judge » :

def evaluate_response(question: str, response: str, expected_elements: list) -> dict:
    scores = {}

    # 1. Faithfulness: Does response contain expected elements?
    scores['contains_key_info'] = all(
        elem.lower() in response.lower() 
        for elem in expected_elements
    )

    # 2. Relevance: Semantic similarity to question
    scores['relevance'] = calculate_semantic_similarity(question, response)

    # 3. Safety: Check for problematic content
    scores['is_safe'] = not contains_harmful_content(response)

    # 4. Tone: Use LLM-as-judge
    judge_prompt = f"""Rate the helpfulness of this support response on a scale of 1-5.

Question: {question}
Response: {response}

Score (1-5):"""

    scores['helpfulness'] = int(llm.generate(judge_prompt))

    return scores

# Run evaluation
for test_case in test_cases:
    response = answer_support_question(test_case['question'])
    scores = evaluate_response(
        test_case['question'],
        response,
        test_case['expected_elements']
    )
    test_case['scores'] = scores
    test_case['response'] = response

Comment ça marche : La fonction evaluate_response() applique quatre vérifications différentes à chaque réponse de l'IA :

  • Premièrement, elle vérifie la fidélité en contrôlant si tous les éléments attendus apparaissent dans la réponse via une simple correspondance de chaînes de caractères.

  • Deuxièmement, elle calcule la similitude sémantique, une mesure de la proximité entre le sens de la réponse et l'intention de la question, en utilisant des embeddings.

  • Troisièmement, elle effectue un contrôle de sécurité pour signaler tout contenu problématique.

  • Quatrièmement, elle utilise un LLM comme juge en demandant à un modèle plus puissant (comme GPT-4) d'évaluer l'utilité de la réponse sur une échelle de 1 à 5.

La boucle exécute ensuite l'évaluation pour chaque cas de test. Elle génère une réponse pour chaque question, l'évalue à l'aide de la fonction evaluate_response, puis stocke à la fois les scores et la réponse dans le cas de test. Cela crée un jeu de données complet de résultats de test pour l'analyse et les améliorations futures.

Métriques automatisées courantes :

  • Similitude sémantique (0.0–1.0) : Elle est mesurée en convertissant la question et la réponse en embeddings vectoriels et en calculant la similitude cosinus. Le score montre à quel point la réponse correspond à l'intention de la question, même si la formulation diffère.

  • Scores ROUGE / BLEU : La sortie du modèle est comparée à des réponses de référence en vérifiant le chevauchement de n-grammes. Ces métriques aident à repérer les régressions, bien que les scores puissent être modestes pour des réponses ouvertes.

  • LLM-as-judge : Un modèle plus robuste (comme GPT-4 ou Claude) peut noter la réponse sur une échelle fixe, par exemple de 1 à 5. Ces évaluations donnent une idée de la qualité et sont utiles pour suivre les améliorations ou les baisses au fil du temps.

  • Métriques de récupération (Précision@k, Rappel@k) : Pour les systèmes basés sur la récupération, ces métriques calculent combien de documents pertinents apparaissent dans les top-k résultats. La précision montre l'exactitude de l'ensemble récupéré, et le rappel indique l'exhaustivité.

  • Validateurs personnalisés : Des vérifications simples basées sur des règles, comme des motifs regex, des mots-clés ou des limites de longueur, garantissent que les réponses répondent à des exigences strictes. Elles aident à capturer des problèmes que les métriques automatisées pourraient manquer.

Évaluation manuelle

Les métriques automatisées ne peuvent pas tout capturer. Les qualités subjectives comme le ton, l'empathie et la voix de la marque nécessitent un jugement humain, tout comme les petites erreurs factuelles qui échappent aux vérifications par mots-clés et aux scores de similitude.

# Flag cases for human review
needs_review = [
    case for case in test_cases 
    if case['scores']['helpfulness'] < 3 
    or not case['scores']['contains_key_info']
]

# SMEs review and annotate
for case in needs_review:
    annotation = get_sme_feedback(case)
    case['human_rating'] = annotation['rating']
    case['improvement_notes'] = annotation['notes']

Ce code filtre les cas de test pour trouver les réponses qui nécessitent une attention humaine, à savoir celles ayant un score d'utilité inférieur à 3 ou manquant d'informations importantes. Des experts métier (SME) examinent ces cas signalés et fournissent des évaluations avec des commentaires constructifs. Leur contribution vous aide à repérer des schémas que les métriques automatisées ignorent et vous indique où améliorer vos prompts, votre configuration de récupération ou les paramètres du système.

Quand utiliser l'évaluation manuelle :

  • Évaluer le ton, l'empathie ou la voix de la marque.

  • Détecter des hallucinations subtiles que les vérifications automatisées manquent.

  • Valider des cas limites avec des nuances spécifiques au domaine.

  • Créer des étiquettes de vérité terrain pour l'entraînement de modèles d'évaluation.

Étape 4 : Apprendre et améliorer

Une fois les échecs identifiés, ajustez les parties contrôlables de votre système d'IA (les « configs ») :

Leviers de configuration courants :

  • Prompts — Ajouter des instructions, des exemples, des contraintes.

  • Récupération (Retrieval) — Modifier la taille des blocs (chunk size), le top-k, la stratégie de ré-ordonnancement (reranking).

  • Modèle — Changer de modèle, ajuster la température, le nombre max de tokens.

  • Contexte — Modifier les instructions système, ajouter de la mémoire.

  • Post-traitement — Ajouter de la validation, du formatage, des filtres de sécurité.

Exemple de cycle d'amélioration :

# Problem discovered: Chatbot missing key details
failing_case = {
    "question": "What's your refund policy?",
    "response": "We offer refunds in certain cases.",
    "issue": "Too vague, missing 30-day window and process"
}

# Root cause: Retrieval returning wrong docs
retrieved_docs = retrieve_docs(failing_case['question'], top_k=5)
# Docs about "payment processing" ranked higher than "refund policy"

# Solution 1: Improve retrieval with reranking
def retrieve_docs_v2(question: str, top_k: int) -> str:
    # Initial retrieval
    candidates = vector_search(question, top_k=20)

    # Rerank by relevance
    reranked = rerank_by_relevance(question, candidates)

    return reranked[:top_k]

# Solution 2: Update prompt to require specificity
prompt_v2 = f"""You are a helpful customer support agent.

Context: {context}

Question: {question}

Provide a clear, accurate answer based on the context. Include specific details like:
- Time windows (e.g., "within 30 days")
- Step-by-step processes
- Relevant links or contact methods

Answer:"""

# Re-evaluate
new_response = answer_support_question_v2(failing_case['question'])
new_scores = evaluate_response(
    failing_case['question'],
    new_response,
    ["30 days", "full refund", "contact support"]
)

# Verify improvement
assert new_scores['contains_key_info'] == True
assert new_scores['helpfulness'] >= 4

Comment ça marche : Dans cet exemple, la réponse du chatbot sur les remboursements était trop vague. Après vérification, le problème était que le système récupérait des documents sur le traitement des paiements au lieu de la politique de remboursement.

Pour résoudre cela, deux changements peuvent être effectués. Premièrement, la récupération est améliorée en saisissant vingt documents, puis en sélectionnant les cinq meilleurs. Deuxièmement, le prompt est mis à jour pour demander des détails spécifiques comme les dates et les étapes.

Après avoir effectué ces changements, le test est relancé pour confirmer que cela fonctionne : la réponse contient désormais toutes les informations clés et obtient un score d'au moins 4 sur 5. Ce processus transforme les problèmes en correctifs mesurables.

Étape 5 : Automatiser et répéter

Intégrez l'évaluation dans votre flux de travail de développement en utilisant le CI/CD :

# .github/workflows/eval.yml
name: Continuous Evaluation

on:
  pull_request:
  push:
    branches: [main]

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Run evaluation suite
        run: python run_evals.py

      - name: Check pass rate
        run: |
          PASS_RATE=$(python calculate_pass_rate.py)
          if (( $(echo "$PASS_RATE < 0.85" | bc -l) )); then
            echo "Pass rate $PASS_RATE below threshold"
            exit 1
          fi

      - name: Upload results
        uses: actions/upload-artifact@v2
        with:
          name: eval-results
          path: results/

Explication : Ce workflow GitHub Actions automatise votre processus d'évaluation afin qu'il s'exécute automatiquement à chaque changement de code. Le workflow se déclenche chaque fois que quelqu'un ouvre une pull request ou pousse du code sur la branche principale. Il récupère votre code, exécute votre suite d'évaluation complète via run_evals.py, puis calcule le pourcentage de cas de test réussis. Si le taux de réussite tombe en dessous de 85 %, le workflow échoue et bloque la fusion du code, empêchant les régressions de qualité d'atteindre la production.

Pratiques clés pour l'automatisation :

  • Versionnez vos cas de test — Suivez-les dans Git aux côtés du code.

  • Définissez des barrières de qualité (quality gates) — Bloquez les déploiements si le taux de réussite tombe en dessous d'un seuil.

  • Surveillez les tendances — Suivez les métriques au fil du temps pour détecter une dérive graduelle.

  • Alertez sur les régressions — Informez l'équipe lorsque des cas de test spécifiques commencent à échouer.

  • Échantillonnez le trafic de production — Ajoutez continuellement des requêtes réelles à votre jeu de données d'évaluation.

Outils et frameworks utilisables pour l'évaluation

Plusieurs plateformes peuvent aider à mettre en œuvre l'évaluation continue. Celle que vous choisirez dépendra de votre stack et de vos besoins :

Si vous construisez avec des LLM : Essayez d'abord LangSmith ou Braintrust. Les deux gèrent le versioning des prompts, les jeux de données d'évaluation et le traçage (tracing) nativement.

Si vous faites du ML traditionnel : Weights & Biases est le standard de l'industrie. Si vous êtes dans l'écosystème Microsoft, PromptFlow s'intègre bien avec Azure.

Si vous voulez un contrôle total : Construisez une solution personnalisée avec pytest pour l'exécution des tests et MLflow pour le suivi des résultats. Cela demande plus de configuration, mais vous possédez l'intégralité du pipeline.

À quoi ressemble une boucle d'évaluation complète en pratique

Ce tour d'horizon montre comment un chatbot de support s'améliore après avoir effectué un seul cycle d'évaluations. Chaque étape montre comment les signaux d'évaluation guident les améliorations et verrouillent la qualité pour la prochaine version.

ÉtapeAvantAprès
Cas de test"Puis-je utiliser votre API avec le forfait gratuit ?"Même question
Réponse du modèle"Oui, vous pouvez accéder à notre API.""Oui, vous pouvez accéder à notre API avec le forfait gratuit avec une limite de 100 requêtes par jour. Pour des limites plus élevées, passez à Pro ou Enterprise."
Scores d'évaluationcontains_key_info=False, helpfulness=2/5contains_key_info=True, helpfulness=5/5
Problème identifiéDétail crucial manquant : limites de débit du forfait gratuitN/A (problème résolu)
Analyse / Cause racineLa récupération a renvoyé des docs API généraux ; le prompt n'a pas mis l'accent sur les limitationsN/A (l'analyse a mené au correctif)
Correctifs appliqués1. Amélioration de la récupération pour extraire les docs de comparaison de forfaits. 2. Mise à jour du prompt : "Mentionnez toujours les restrictions spécifiques au forfait". 3. Ajout de validation : La réponse doit mentionner les limites de débit si la question est posée.N/A (correctif implémenté)
RésultatTest échoué, régression non empêchéeTest réussi, régression empêchée
Actions du cycle suivantN/A1. Ajouter ce cas de test à la suite permanente. 2. Rechercher des problèmes similaires (autres questions liées aux forfaits). 3. Surveiller les requêtes de production pour ce schéma.

Cycle suivant :

  • Ajouter ce cas de test à la suite permanente.

  • Rechercher des problèmes similaires (autres questions liées aux forfaits).

  • Surveiller si ce schéma apparaît dans les requêtes de production.

Points clés à retenir

  • Les systèmes d'IA nécessitent une évaluation continue, pas un test ponctuel — Les modèles dérivent, les données changent et les échecs silencieux s'accumulent sans vérifications régulières.

  • Intégrez l'évaluation dans votre workflow dès le premier jour — N'attendez pas que des échecs en production vous obligent à ajouter l'évaluation a posteriori.

  • Commencez simplement, puis montez en charge — Commencez avec 10 à 20 cas de test et des métriques de base. Développez votre suite à mesure que vous rencontrez des cas limites.

  • Automatisez ce que vous pouvez, impliquez les humains pour le reste — Utilisez des vérifications programmatiques pour la rapidité, et la révision par des experts métier pour la nuance.

  • Traitez les jeux de données d'évaluation comme des artefacts de premier ordre — Versionnez-les, révisez les changements et développez-les au fil du temps.

  • Faites de l'évaluation un sport d'équipe — Le produit, l'ingénierie et les experts du domaine doivent tous contribuer aux cas de test et aux critères d'évaluation.

Conclusion

Tout développeur a déjà ressenti le soulagement de voir « tous les tests réussis ». Dans les systèmes d'IA, cette assurance est souvent trompeuse. Un modèle peut être déployé avec succès, répondre aux critères de performance, et pourtant produire des sorties incorrectes, incomplètes ou trompeuses d'une manière que les tests traditionnels ne détectent pas.

Le volant d'évaluation comble cette lacune en rendant le comportement du modèle testable en pratique. Au lieu de supposer l'exactitude, il force le système à répondre à des questions réelles, mesure la qualité de ces réponses et met en évidence les points où les performances se dégradent au fil du temps. Cela déplace l'évaluation d'une étape de validation ponctuelle vers une partie intégrante du développement.

L'évaluation n'éliminera pas complètement l'incertitude, mais elle rend les échecs visibles avant qu'ils n'atteignent les utilisateurs. Une fois les échecs clairement exposés, les équipes cessent de deviner et commencent à corriger sur la base de résultats. Cela peut signifier l'ajustement des prompts, l'amélioration de la logique de récupération ou l'affinage des critères d'évaluation. Au fil du temps, cela conduit à des systèmes d'IA qui évoluent de manière contrôlée plutôt que de se briser silencieusement.

Ressources pour aller plus loin