Aller au contenu principal

Gestion d'erreurs et recovery

Les erreurs sont inévitables

En automatisation visuelle, les erreurs sont la norme, pas l’exception. Une page qui charge lentement, un bouton qui a changé de place, une popup inattendue, un captcha — votre agent doit savoir gérer tout cela gracieusement.

Types d’erreurs

Type Exemple Stratégie
Réseau Timeout, page non trouvée Retry avec backoff
Visuel Élément absent, popup bloquante Retry ou contournement
API Rate limit, erreur 500 Anthropic Backoff exponentiel
Logique Boucle infinie, mauvaise page Détection + arrêt
Sécurité Captcha, blocage IP Alerte + intervention humaine

Retry avec backoff exponentiel

Pour les erreurs réseau et API, implémentez un retry intelligent :

import time
import random

def retry_with_backoff(func, max_retries=3, base_delay=1.0):
    """Exécute une fonction avec retry et backoff exponentiel."""
    for attempt in range(max_retries + 1):
        try:
            return func()
        except Exception as e:
            if attempt == max_retries:
                raise

            delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
            print(f"  Erreur (tentative {attempt + 1}) : {e}")
            print(f"  Retry dans {delay:.1f}s...")
            time.sleep(delay)

Appliquez-le aux appels API :

def call_model_with_retry(client, messages, tools):
    """Appel API avec retry automatique."""
    def _call():
        return client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            tools=tools,
            messages=messages,
        )

    return retry_with_backoff(_call, max_retries=3, base_delay=2.0)

Détection de boucle infinie

Un agent peut tourner en rond s’il ne comprend pas ce qu’il voit. Détectez cela :

import hashlib

class LoopDetector:
    """Détecte quand l'agent tourne en boucle."""

    def __init__(self, max_repeats: int = 3):
        self.screenshot_hashes = []
        self.max_repeats = max_repeats

    def check(self, screenshot_bytes: bytes) -> bool:
        """Retourne True si une boucle est détectée."""
        h = hashlib.md5(screenshot_bytes).hexdigest()
        self.screenshot_hashes.append(h)

        # Vérifier les N derniers screenshots
        recent = self.screenshot_hashes[-self.max_repeats:]
        if len(recent) == self.max_repeats and len(set(recent)) == 1:
            return True  # Boucle détectée

        return False

# Utilisation dans la boucle
detector = LoopDetector(max_repeats=3)

for i in range(MAX_ITERATIONS):
    screenshot = page.screenshot()

    if detector.check(screenshot):
        print("Boucle détectée ! Arrêt de l'agent.")
        break

    # ... suite de la boucle

Gestion des popups inattendues

Les popups (consentement, newsletter, chat, notifications) peuvent bloquer l’agent. Ajoutez des instructions dans le system prompt :

system_prompt = """
Tu es un agent d'automatisation web.

GESTION DES OBSTACLES :
- Si une popup ou une bannière bloque l'écran, ferme-la d'abord
  (bouton X, "Fermer", "Non merci", "Dismiss")
- Si un captcha apparaît, ARRÊTE-TOI et signale-le
- Si une erreur 404/500 s'affiche, retourne à la page précédente
- Si la page demande une mise à jour ou une confirmation,
  refuse et continue ta tâche principale
"""

Système de checkpoints

Pour les workflows longs, sauvegardez l’état régulièrement :

import json
from datetime import datetime

class CheckpointManager:
    """Sauvegarde et restaure l'état du workflow."""

    def __init__(self, filepath: str = "checkpoint.json"):
        self.filepath = filepath

    def save(self, step_index: int, context: dict):
        """Sauvegarde un checkpoint."""
        state = {
            "step_index": step_index,
            "context": context,
            "timestamp": datetime.utcnow().isoformat(),
        }
        with open(self.filepath, "w") as f:
            json.dump(state, f, indent=2)
        print(f"  Checkpoint sauvegardé (étape {step_index})")

    def load(self) -> dict:
        """Charge le dernier checkpoint."""
        try:
            with open(self.filepath, "r") as f:
                return json.load(f)
        except FileNotFoundError:
            return None

    def clear(self):
        """Supprime le checkpoint (workflow terminé)."""
        import os
        if os.path.exists(self.filepath):
            os.remove(self.filepath)

Intégrez-le dans votre orchestrateur :

def resilient_workflow(workflow, page):
    """Workflow avec checkpoints et recovery."""
    ckpt = CheckpointManager()
    saved = ckpt.load()

    start_index = 0
    if saved:
        start_index = saved["step_index"]
        print(f"Reprise depuis l'étape {start_index}")

    for i, step in enumerate(workflow[start_index:], start=start_index):
        try:
            run_agent(step["task"], page=page)
            ckpt.save(i + 1, step)
        except Exception as e:
            print(f"Erreur à l'étape {i} : {e}")
            ckpt.save(i, step)  # Sauvegarder pour reprendre
            raise

    ckpt.clear()  # Workflow terminé
    print("Workflow terminé avec succès")

Alertes et escalade

Quand l’agent ne peut pas résoudre un problème seul, il doit alerter un humain :

def alert_human(reason: str, screenshot_path: str = None):
    """Envoie une alerte quand l'agent est bloqué."""
    import smtplib
    from email.message import EmailMessage

    msg = EmailMessage()
    msg["Subject"] = f"Agent bloqué : {reason}"
    msg["From"] = "[email protected]"
    msg["To"] = "[email protected]"
    msg.set_content(f"L'agent Computer Use est bloqué.\nRaison : {reason}")

    if screenshot_path:
        with open(screenshot_path, "rb") as f:
            msg.add_attachment(
                f.read(), maintype="image", subtype="png",
                filename="screenshot.png"
            )

    with smtplib.SMTP("smtp.company.com", 587) as s:
        s.starttls()
        s.login("[email protected]", "password")
        s.send_message(msg)

Points clés à retenir

  • Implémentez du retry avec backoff exponentiel pour les erreurs réseau et API
  • Détectez les boucles infinies en comparant les hashes des screenshots successifs
  • Prévoyez la gestion des popups et obstacles dans le system prompt
  • Utilisez des checkpoints pour reprendre un workflow interrompu
  • Mettez en place des alertes pour les situations que l’agent ne peut pas résoudre seul