Un prompt n’est pas un fichier de configuration figé : chaque modification peut casser des comportements critiques sans prévenir. Voici comment tester tes prompts avant qu’ils ne cassent ta production.

UNE LIGNE DE CODE QUI A TOUT CASSE

Imagine : ton système de requêtes fonctionne parfaitement. Tu ajoutes une seule ligne pour gérer les documents PDF et les politiques. Résultat ? Trois semaines plus tard, tes utilisateurs se plaignent : des questions comme « Quels produits ne sont pas couverts par la garantie ? » ne sont plus comprises. Pourtant, tu n’as touché ni au code de classification ni au routage. La seule chose qui a changé ? Le prompt.

C’est là que le problème apparaît : tu traitais ton prompt comme un fichier de configuration statique. Or, un prompt est une interface API stochastique : chaque instruction ajoutée modifie le comportement du modèle pour toutes les requêtes, pas seulement celles que tu vises. Une seule ligne peut faire basculer des milliers de réponses sans que personne ne s’en rende compte.

LE PROBLÈME DES RÉGRESSIONS CACHÉES

Dans le monde du Développement logiciel, on connaît bien ce phénomène : une modification apparemment anodine peut casser une fonctionnalité existante sans que les tests ne la détectent. On appelle ça une régression. Pourtant, la plupart des équipes en IA n’ont aucun test pour leurs prompts. Résultat ? Les utilisateurs deviennent les testeurs… et ce n’est pas une bonne idée.

Pire encore : parfois, une modification améliore le score global tout en faisant s’effondrer une catégorie critique. On appelle ça un faux progrès. Par exemple, si ton modèle passe de 100 % à 33 % de bonnes réponses sur les questions de négation (comme « Quels produits ne sont pas couverts ? »), mais que ton score global monte de 10 %, tu risques de déployer une version pire que la précédente. Sans test dédié, tu ne verras jamais le problème.

COMMENT FONCTIONNE UN PROMPT ?

Prenons un exemple concret. Voici une requête type et les attentes du système :

{
  "id": "NQ_01",
  "query": "Which products are not covered under the warranty policy?",
  "category": "negation",
  "expectedintent": "negationcheck",
  "expectedschemakeys": ["intent", "confidence", "querytype", "rewrittenquery"],
  "expected_patterns": ["not covered", "warranty"],
  "mustnotcontain": ["I cannot", "As an AI"],
  "failuremode": "instructionconflict"
}

Le système doit détecter que la question contient une négation (« not covered ») et vérifier qu’elle est classée comme « negation_check ». Si le prompt est modifié pour prioriser le routage des documents avant la classification, la négation ne sera jamais détectée. Résultat : le modèle répondra comme si la question était une simple recherche de politique, sans jamais activer la logique de négation.

LE TEST QUI DÉTECTE LES RÉGRESSIONS

Pour éviter ce piège, il faut un test de régression pour les prompts. Voici comment ça marche :

1. On définit une liste de requêtes de référence (40 dans l’exemple) couvrant plusieurs catégories de questions.

2. Chaque requête est associée à une signature de validation : une liste de règles strictes que la réponse doit respecter. Par exemple :

  • Le champ « intent » doit être « negation_check » pour les questions de négation.
  • La réponse ne doit pas contenir les phrases « I cannot » ou « As an AI ».
  • La réponse doit contenir les mots « not covered » et « warranty ».

3. On exécute le test sur plusieurs versions de prompts (v1, v2, v3, v4 dans l’exemple).

4. Pour chaque version, on calcule le score par catégorie : nombre de requêtes réussies divisé par le nombre total de requêtes.

5. On compare les scores entre les versions. Si une catégorie critique régresse de plus de 10 %, le test déclenche une alerte.

Voici le code qui réalise ces vérifications :

class QueryValidator:
    def validate(self, output: dict, query: dict) -> ValidationResult:
        # 1. Vérification du schéma : tous les champs attendus sont présents
        schemafailures = [k for k in expectedkeys if k not in output]
        schemapass = len(schemafailures) == 0

        # 2. Vérification des motifs : les mots-clés attendus sont dans la réponse
        output_text = " ".join(str(v) for v in output.values()).lower()
        pattern_failures = [
            p for p in expected_patterns
            if not re.search(re.escape(p.lower()), output_text)
        ]
        patternpass = len(patternfailures) == 0

        # 3. Vérification de l'intention : l'intention détectée correspond à l'attendue
        detected_intent = output.get("intent", "")
        intentpass = detectedintent == expected_intent

        # 4. Vérification des garde-fous : les phrases interdites ne sont pas présentes
        guardviolations = [g for g in mustnotcontain if g.lower() in outputtext]
        guardpass = len(guardviolations) == 0

LE PIÈGE DU FAUX PROGRÈS

« Le score global a augmenté de 10 %, mais la catégorie critique a chuté de 66 %. Résultat : ne pas déployer cette version. »

Le faux progrès est le scénario le plus dangereux. Voici comment il se produit :

1. Tu modifies ton prompt pour résoudre un problème spécifique (par exemple, améliorer la gestion des requêtes complexes).

2. Le score global augmente, car les requêtes complexes sont mieux gérées.

3. Mais en réalité, une catégorie critique (comme les questions de négation) régresse fortement.

4. Comme le score global monte, tu déploies la nouvelle version. Résultat : tes utilisateurs rencontrent des bugs sur des requêtes courantes.

Dans l’exemple concret :

  • v1 (baseline) : 57,5 % de score global, 100 % de bonnes réponses sur les questions de négation.
  • v4 (modifiée) : 67,5 % de score global, mais seulement 33 % de bonnes réponses sur les questions de négation (chute de 66 %).

Le test détecte ce faux progrès et bloque le déploiement.

COMMENT SIMULER LES ÉCHECS SANS APPELER L’IA

Pour que le test soit fiable, il doit être déterministe : chaque exécution doit donner le même résultat. Or, appeler une API d’IA en production introduit de la variabilité (modèle mis à jour, latence réseau, etc.). La solution ? Un simulateur déterministe qui reproduit les échecs réels observés en production.

Voici comment ça marche pour les versions v2, v3 et v4 :

def simulateoutput(promptversion: str, query: dict) -> dict:
    # v2 + simpleintent → le raisonnement en chaîne déborde dans rewrittenquery
    if version == "v2" and category == "simple_intent":
        return overreasoningnoise(query)

    # v3 + negation → le routage des documents prend le pas sur la classification
    if version == "v3" and category == "negation":
        if query_number in (1, 3, 5):
            return instructionconflict_moderate(query)

    # v4 + negation → les conflits s'ajoutent, l'intention est mal classée
    if version == "v4" and category == "negation":
        if query_number in (1, 2, 4, 5):
            return instructionconflict_severe(query)

Par exemple, pour v4 et une question de négation, le simulateur retourne :

{
  "intent": "ambiguous",
  "confidence": 0.39,
  "rewritten_query": "Step 1: Scan for document type signals. Step 2: Negation keyword detected: but document routing takes priority. Step 3: Therefore classifying as ambiguous pending document context resolution."
}

Cette réponse échoue à trois des quatre vérifications :

  • L’intention est « ambiguous » au lieu de « negation_check ».
  • Les motifs « not covered » et « warranty » sont absents.
  • La réponse contient des tokens de raisonnement en chaîne (« Step 1 », « Step 2 », etc.), ce qui est interdit.

POURQUOI UN SIMULATEUR DÉTERMINISTE ?

« Un test de régression ne mesure pas la qualité, mais la stabilité. Si tes résultats varient à chaque exécution, tu ne peux pas savoir si une régression est réelle ou due au hasard. »

Un simulateur déterministe a plusieurs avantages :

  • Reproductibilité : chaque exécution donne le même résultat, peu importe qui lance le test.
  • Rapidité : pas besoin d’appeler une API d’IA, le test s’exécute en moins de deux secondes.
  • Coût zéro : pas de facture d’API à payer.
  • Détection des conflits d’instructions : le simulateur reproduit des échecs réels observés en production, comme les conflits entre instructions contradictoires.

En revanche, un simulateur ne remplace pas les tests de qualité (comme un juge LLM), qui évaluent des sorties ouvertes et subjectives. Les deux approches sont complémentaires :

  • Le test de régression détecte les régressions structurelles (une modification casse quelque chose qui marchait avant).
  • Le juge LLM évalue la qualité globale des réponses (est-ce que la réponse est utile, claire, précise ?).

COMMENT CRÉER TON PROPRE TEST DE RÉGRESSION

Voici comment construire ton propre test de régression pour tes prompts :

Étape 1 : Choisis tes catégories critiques

Identifie les deux ou trois catégories de requêtes qui représentent la majorité de ton trafic. Par exemple :

  • Les questions de négation (« Quels produits ne sont pas couverts ? »).
  • Les questions simples (« Quelle est la politique de remboursement ? »).
  • Les questions complexes (« Quel est le délai de livraison pour un colis expédié en Allemagne avec un code promo ? »).

Étape 2 : Écris 10 requêtes par catégorie

Chaque requête doit être conçue pour tester un échec spécifique. Par exemple :

  • Pour la négation : « Quels services ne sont pas inclus dans l’abonnement premium ? »
  • Pour les questions simples : « Quelle est la durée de la garantie ? »
  • Pour les questions complexes : « Comparez le prix du forfait A et du forfait B pour un client en France avec une réduction de 20 %. »

Étape 3 : Définis une signature de validation pour chaque requête

Pour chaque requête, écris une liste de règles strictes que la réponse doit respecter. Par exemple :

{
  "id": "EX_01",
  "query": "Quels services ne sont pas inclus dans l’abonnement premium ?",
  "category": "negation",
  "expectedintent": "negationcheck",
  "expectedschemakeys": ["intent", "confidence", "querytype", "rewrittenquery"],
  "expected_patterns": ["ne sont pas inclus", "abonnement premium"],
  "mustnotcontain": ["Je ne peux pas", "En tant qu’IA"],
  "failuremode": "instructionconflict"
}

Étape 4 : Configure ton seuil de régression

Choisis un seuil de régression (par exemple 10 %) en fonction de l’importance de la catégorie. Pour un système médical, tu pourrais choisir 5 %. Pour un système de recommandation, 15 % pourraient être acceptables.

Étape 5 : Automatise le test

Écris un script qui exécute le test avant chaque modification de ton prompt. Si une régression est détectée, bloque le déploiement. Voici un exemple de commande :

python run_regression.py

LES RÉSULTATS CONCRETS : CE QUE LE TEST A DÉTECTÉ

Voici les résultats obtenus avec les quatre versions de prompts testées :

VERDICT: v1 → v4

  ⚠  FAUX PROGRÈS DÉTECTÉ

  Le score global a augmenté de 10,0 % mais les catégories critiques
  ont régressé : [negation]

  Régressions critiques :
    • negation   100,0 % → 33,3 %  ▼ 66,7 %
      Mode d'échec : conflit d'instructions

  STATUT : ✗  NE PAS DÉPLOYER EN PRODUCTION

Tous les faux progrès ont été détectés : v2, v3 et v4 ont toutes déclenché l’alerte. Pourtant, leurs scores globaux étaient supérieurs à la baseline v1. Sans ce test, ces versions auraient été déployées… et auraient cassé la production.

CE QUE LE TEST N’A PAS DÉTECTÉ (ET POURQUOI)

Certaines catégories sont connues pour échouer, mais ce n’est pas une régression. Par exemple, la catégorie « comparaison » (requêtes nécessitant de comparer deux entités) a un score de 0 % sur toutes les versions. C’est un échec attendu, car le système actuel ne gère pas les comparaisons. Le test le signale clairement avec une annotation [ÉCHEC CONNU] pour éviter toute confusion avec une régression.

L’important est de distinguer :

  • Les régressions : une fonctionnalité qui marchait avant ne marche plus.
  • Les échecs attendus : une fonctionnalité qui n’a jamais marché et n’est pas prioritaire.

COMMENT ADAPTER LE TEST À TON SYSTÈME

Ce framework est portable, mais tu dois l’adapter à ton système. Voici comment faire :

1. Identifie les conflits d’instructions dans ton prompt

Relis ton prompt et cherche des instructions contradictoires. Par exemple :

  • « Sois concis » vs « Explique ton raisonnement étape par étape ».
  • « Priorise le routage des documents » vs « Classe l’intention en premier ».

Pour chaque conflit, écris une fonction dans le simulateur qui reproduit l’échec correspondant.

2. Mets à jour les catégories critiques

Si tu ajoutes une nouvelle catégorie critique, ajoute-la à la liste CRITICAL_CATEGORIES et écris 10 requêtes de référence pour cette catégorie.

3. Ajuste le seuil de régression

Si ton système est critique (médical, juridique), baisse le seuil à 5 %. Si c’est moins important, tu peux monter à 15 %.

4. Utilise un parseur YAML minimal

Le framework utilise un parseur YAML maison pour éviter d’ajouter une dépendance inutile. Si tu as besoin de fonctionnalités avancées (ancres, alias), tu peux remplacer ce parseur par PyYAML en une ligne de code.

CE QUE LE TEST NE REMPLACE PAS

Ce test de régression ne mesure pas la qualité globale des réponses. Il détecte uniquement les régressions structurelles. Pour évaluer la qualité, tu as besoin d’un autre outil, comme un juge LLM qui note les réponses selon des critères subjectifs (clarté, précision, utilité).

Les deux approches sont complémentaires :

  • Le test de régression s’exécute avant chaque modification de prompt pour détecter les régressions cachées.
  • Le juge LLM s’exécute périodiquement pour auditer la qualité globale des réponses.

POUR ALLER PLUS LOIN : LE CODE COMPLET

Le framework complet est disponible en open source. Tu peux le cloner et l’adapter à ton système :

Le code est disponible à l’adresse : https://github.com/Emmimal/prompt-regression-suite

Le framework inclut :

  • Un simulateur déterministe qui reproduit les échecs réels.
  • Un validateur qui vérifie les signatures de chaque réponse.
  • Un scoreur qui détecte les faux progrès.
  • Un script d’exécution qui s’exécute en moins de deux secondes.

EN RÉSUMÉ : PROTÈGE TON SYSTÈME AVANT QU’IL NE CASSE

Un prompt n’est pas un fichier de configuration figé : c’est une interface API qui évolue à chaque modification. Sans test de régression, tu risques de déployer des versions qui cassent des fonctionnalités critiques sans que personne ne s’en aperçoive.

« Avant de modifier un prompt, demande-toi : est-ce que cette modification peut casser une fonctionnalité existante ? Si oui, écris un test pour la détecter. »

Avec ce framework, tu peux :

  • Détecter les régressions avant qu’elles n’atteignent tes utilisateurs.
  • Éviter les faux progrès qui améliorent le score global tout en cassant des catégories critiques.
  • Automatiser la validation de tes prompts avant chaque déploiement.

Le coût ? Zéro. Le temps d’exécution ? Moins de deux secondes. La tranquillité d’esprit ? Inestimable.

Sources :
  • Towards Data Science

L'indépendance de CLODCO est votre garantie.

Pour que l'actualité de l'IA reste sans filtre et sans concession, votre soutien est indispensable. Votre contribution est le seul moteur de notre liberté éditoriale.

Soutenir CLODCO