AI Prompt Engineering · Leçon

Gestion des erreurs dans les chaînes d’invites

Validez les sorties intermédiaires et récupérez les chaînes après leurs échecs.

Leçon 4 sur 413 étapes

Gestion des erreurs dans les chaînes d’invites est une leçon AI Prompt Engineering gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Prompt Engineering, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Prompt Engineering comprend 4 leçons au total.

Pourquoi les chaînes échouent

Les chaînes d’invites introduisent de nouveaux modes de défaillance que les systèmes à invite unique ne connaissent pas. Chaque étape peut échouer à sa manière, et les défaillances se cumulent : une mauvaise sortie de l’étape 2 corrompt toutes les étapes suivantes.

Modes de défaillance courants :

  • Le modèle renvoie un JSON malformé qui ne peut pas être analysé
  • Le modèle comprend mal la tâche et produit une sortie sémantiquement incorrecte
  • Les limites de débit ou les délais d’expiration API provoquent des échecs d’étape
  • La fenêtre de contexte est dépassée dans une chaîne longue
  • Le modèle invente des données que les étapes suivantes considèrent comme des faits

Valider la sortie après chaque étape

La première ligne de défense consiste à valider la sortie immédiatement après chaque étape, avant de la transmettre à la suivante. Ne supposez jamais que le modèle a renvoyé ce que vous lui avez demandé.

import json

def validate_json_output(raw_text, required_fields):
    'Parse and validate that required fields are present in model output.'
    try:
        data = json.loads(raw_text.strip())
    except json.JSONDecodeError as e:
        raise ValueError(f'Invalid JSON: {e}. Raw: {raw_text[:200]}')

    missing = [f for f in required_fields if f not in data]
    if missing:
        raise ValueError(f'Missing required fields: {missing}. Got: {list(data.keys())}')

    return data

# Usage after a chain step
raw = '{"sentiment": "positive", "priority": "high"}'
validated = validate_json_output(raw, required_fields=['sentiment', 'priority'])
print('Valid:', validated)

Logique de nouvelle tentative pour les échecs transitoires

Les échecs d’API — limites de débit, délais d’expiration et erreurs de serveur — sont temporaires. Mettez en œuvre une logique de nouvelles tentatives avec temporisation exponentielle pour les échecs au niveau du réseau :

import anthropic, time

client = anthropic.Anthropic(api_key='YOUR_API_KEY')

def call_with_retry(prompt, max_retries=3, base_delay=1.0):
    last_error = None
    for attempt in range(max_retries):
        try:
            r = client.messages.create(
                model='claude-opus-4-5', max_tokens=500,
                messages=[{'role': 'user', 'content': prompt}]
            )
            return r.content[0].text
        except anthropic.RateLimitError as e:
            wait = base_delay * (2 ** attempt)
            print(f'Rate limited. Waiting {wait}s before retry {attempt+1}/{max_retries}...')
            time.sleep(wait)
            last_error = e
        except anthropic.APIError as e:
            last_error = e
            if attempt < max_retries - 1:
                time.sleep(base_delay)
    raise RuntimeError(f'All retries exhausted: {last_error}')

Validation sémantique

Certains échecs sont structurellement valides, mais sémantiquement incorrects : le modèle renvoie un JSON valide contenant des valeurs incorrectes. Utilisez une étape de validation légère pour vérifier la justesse sémantique :

def semantic_validate(data, schema_rules):
    'Apply semantic validation rules to parsed output.'
    errors = []

    for field, rules in schema_rules.items():
        value = data.get(field)

        if rules.get('required') and value is None:
            errors.append(f'{field} is required but missing')
            continue

        if 'allowed_values' in rules and value not in rules['allowed_values']:
            errors.append(f'{field} must be one of {rules["allowed_values"]}, got: {value}')

        if 'min_length' in rules and isinstance(value, list) and len(value) < rules['min_length']:
            errors.append(f'{field} must have at least {rules["min_length"]} items, got {len(value)}')

    if errors:
        raise ValueError('Semantic validation failed: ' + '; '.join(errors))

    return data

rules = {'sentiment': {'allowed_values': ['positive', 'negative', 'mixed']}, 'issues': {'min_length': 1}}
data = {'sentiment': 'positive', 'issues': ['login bug']}
print(semantic_validate(data, rules))

Invites de repli

Lorsqu’une étape échoue à la validation après plusieurs nouvelles tentatives, une invite de repli peut produire une sortie plus simple, mais exploitable, au lieu de faire échouer toute la chaîne :

import json

def call_with_fallback(primary_prompt, fallback_prompt, required_fields):
    # Try primary prompt
    try:
        raw = call_with_retry(primary_prompt)
        return validate_json_output(raw, required_fields)
    except (ValueError, RuntimeError) as e:
        print(f'Primary prompt failed: {e}. Trying fallback...')

    # Try simpler fallback prompt
    try:
        raw = call_with_retry(fallback_prompt)
        return validate_json_output(raw, required_fields)
    except (ValueError, RuntimeError) as e:
        print(f'Fallback also failed: {e}. Returning safe default.')

    # Return safe default — chain continues with minimal data
    return {field: None for field in required_fields}

# Usage
primary = 'Analyze this review. Return JSON with 10 fields: {...}'
fallback = 'Classify this review. Return JSON: {"sentiment": "positive|negative|neutral"}'
result = call_with_fallback(primary, fallback, ['sentiment'])
print(result)

Disjoncteurs

Un disjoncteur empêche une chaîne défaillante de gaspiller des appels d’API. Après N échecs consécutifs, il ouvre le circuit et renvoie immédiatement une erreur sans effectuer d’autres appels d’API :

class CircuitBreaker:
    def __init__(self, failure_threshold=3, recovery_timeout=60):
        self.failure_count = 0
        self.threshold = failure_threshold
        self.state = 'closed'  # closed = normal, open = blocking
        self.opened_at = None

    def call(self, fn, *args, **kwargs):
        import time
        if self.state == 'open':
            elapsed = time.time() - self.opened_at
            if elapsed > 60:  # recovery_timeout
                self.state = 'half-open'
            else:
                raise RuntimeError('Circuit open — skipping API call')
        try:
            result = fn(*args, **kwargs)
            self.failure_count = 0
            self.state = 'closed'
            return result
        except Exception as e:
            self.failure_count += 1
            if self.failure_count >= self.threshold:
                self.state = 'open'
                self.opened_at = time.time()
                print(f'Circuit opened after {self.failure_count} failures.')
            raise e

cb = CircuitBreaker(failure_threshold=3)
print('Circuit breaker initialized.')

Créer des points de contrôle pour les chaînes longues

Pour les chaînes comportant de nombreuses étapes ou des étapes coûteuses, utilisez des points de contrôle afin d’enregistrer les résultats intermédiaires. Si une étape tardive échoue, reprenez à partir du point de contrôle au lieu de recommencer depuis l’étape 1 :

import json, os

CHECKPOINT_DIR = '/tmp/chain_checkpoints'
os.makedirs(CHECKPOINT_DIR, exist_ok=True)

def save_checkpoint(run_id, step_id, data):
    path = os.path.join(CHECKPOINT_DIR, f'{run_id}_step{step_id}.json')
    with open(path, 'w') as f:
        json.dump(data, f)
    print(f'Checkpoint saved: step {step_id}')

def load_checkpoint(run_id, step_id):
    path = os.path.join(CHECKPOINT_DIR, f'{run_id}_step{step_id}.json')
    if os.path.exists(path):
        with open(path) as f:
            return json.load(f)
    return None

def run_with_checkpoints(run_id, input_data):
    step1 = load_checkpoint(run_id, 1) or json.loads(call(f'Step 1 processing: {input_data}'))
    save_checkpoint(run_id, 1, step1)

    step2 = load_checkpoint(run_id, 2) or json.loads(call(f'Step 2 processing: {step1}'))
    save_checkpoint(run_id, 2, step2)

    return step2

print('Checkpointing system defined.')

Dégradation maîtrisée

Lorsqu’une étape de la chaîne échoue sans possibilité de récupération, la dégradation maîtrisée poursuit la chaîne avec des données partielles au lieu de provoquer un échec complet :

def process_with_degradation(tickets):
    results = []
    for ticket in tickets:
        try:
            # Full chain: extract -> classify -> respond
            extracted = json.loads(call(f'Extract issue from ticket. Return JSON: {{"issue": str}}\n\n{ticket}'))
            classified = json.loads(call(f'Classify priority. Return JSON: {{"priority": str}}\n\n{extracted["issue"]}'))
            response = call(f'Draft response for {classified["priority"]} priority: {extracted["issue"]}')
            results.append({'ticket': ticket, 'response': response, 'degraded': False})
        except Exception as e:
            print(f'Chain failed for ticket, using fallback: {e}')
            # Fallback: simple direct response without classification
            simple_response = call(f'Respond to this support ticket:\n{ticket}')
            results.append({'ticket': ticket, 'response': simple_response, 'degraded': True})
    return results

print('Graceful degradation pipeline defined.')

Journalisation structurée des erreurs

Journalisez les erreurs avec suffisamment de contexte pour déterminer quelle étape a échoué, quelle était l’entrée et ce que le modèle a renvoyé :

import logging, traceback
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('chain')

def logged_step(step_name, prompt, validator=None):
    start = datetime.utcnow()
    try:
        raw = call_with_retry(prompt)
        result = validator(raw) if validator else raw
        logger.info(f'[{step_name}] SUCCESS in {(datetime.utcnow()-start).total_seconds():.2f}s')
        return result
    except Exception as e:
        logger.error(f'[{step_name}] FAILED after {(datetime.utcnow()-start).total_seconds():.2f}s')
        logger.error(f'[{step_name}] PROMPT: {prompt[:200]}')
        logger.error(f'[{step_name}] ERROR: {traceback.format_exc()}')
        raise

print('Structured error logging defined.')

Mettre à l’essai les scénarios d’erreur

Mettez explicitement votre gestion des erreurs à l’essai en provoquant des échecs. Utilisez des objets simulés pour reproduire les erreurs d’API et les sorties malformées :

from unittest.mock import patch, MagicMock

def test_fallback_on_json_error():
    with patch('__main__.call') as mock_call:
        # First call returns malformed JSON, fallback returns valid JSON
        mock_call.side_effect = [
            'This is not JSON at all',
            '{"sentiment": "positive"}'
        ]

        result = call_with_fallback(
            primary_prompt='Analyze review with 10 fields',
            fallback_prompt='Just classify sentiment as JSON',
            required_fields=['sentiment']
        )

        assert result['sentiment'] == 'positive'
        print('PASS: fallback activated correctly on JSON parse error')

def test_circuit_breaker_opens():
    cb = CircuitBreaker(failure_threshold=2)
    for i in range(2):
        try:
            cb.call(lambda: (_ for _ in ()).throw(RuntimeError('API fail')))
        except RuntimeError:
            pass
    assert cb.state == 'open'
    print('PASS: circuit breaker opened after 2 failures')

print('Error handling tests defined.')

Surveiller l’état de la chaîne en production

En production, suivez les métriques d’état de la chaîne afin de détecter toute dégradation avant que les utilisateurs ne la remarquent :

  • Taux de réussite de l’étape : pourcentage des exécutions où chaque étape réussit dès la première tentative
  • Taux d’activation de la solution de repli : à quelle fréquence l’invite de repli est-elle utilisée ?
  • Taux de dégradation : quelle proportion des exécutions de la chaîne s’achève en mode dégradé ?
  • Latence de l’étape : suivez la latence p50/p95 pour chaque étape — une étape lente indique des problèmes de complexité de l’invite
  • Taux d’échec de validation : un taux élevé indique que l’invite doit être affinée

Vérification rapide

Quel est le rôle d’un disjoncteur dans une chaîne d’invites ?

Gestion des erreurs dans les chaînes — Points essentiels

Une gestion robuste des erreurs est ce qui distingue les chaînes prototypes des systèmes de production :

  • Validez la sortie de chaque étape avant de la transmettre en aval — ne supposez jamais que le modèle a renvoyé des données correctes
  • Effectuez de nouvelles tentatives en cas d’échecs d’API transitoires avec une temporisation exponentielle — les limites de débit et les délais d’expiration peuvent être récupérés
  • Utilisez des invites de repli pour obtenir une sortie plus simple lorsque l’invite principale échoue à la validation sémantique
  • Les disjoncteurs évitent de gaspiller des appels d’API après des échecs répétés
  • Créez des points de contrôle pour les étapes coûteuses afin que les chaînes longues puissent reprendre après l’échec d’une étape tardive
  • La dégradation maîtrisée permet à la chaîne de traitement de continuer avec des données partielles au lieu de provoquer un échec
  • Suivez en production le taux de réussite des étapes, le taux de repli et le taux de dégradation
Gratuit pour commencer

Apprends AI Prompt Engineering avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
53
Leçons
199

Questions Fréquemment Posées

La leçon « Gestion des erreurs dans les chaînes d’invites » est-elle gratuite ?

Oui — le texte complet de « Gestion des erreurs dans les chaînes d’invites » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Prompt Engineering, passe à CoddyKit PRO. Le cours AI Prompt Engineering comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Gestion des erreurs dans les chaînes d’invites » ?

Validez les sorties intermédiaires et récupérez les chaînes après leurs échecs. Tu pratiques AI Prompt Engineering avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer AI Prompt Engineering ?

Aucune expérience préalable n'est requise. AI Prompt Engineering sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Gestion des erreurs dans les chaînes d’invites » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon AI Prompt Engineering ?

Oui. Chaque leçon AI Prompt Engineering inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Qu’est-ce que l’enchaînement d’invites ?
  2. Modèles sortie-entrée
  3. Chaînes de transformations séquentielles
  4. Gestion des erreurs dans les chaînes d’invites
← Retour à AI Prompt Engineering