hack
Étincelle
0 XP / 250
M8 · L'API Anthropicleçon 4/4 · 8 min

De l'expérimentation à la prod : les bonnes pratiques

Retry, timeouts, maîtrise des coûts, batch, logs : ce qui sépare un script du dimanche d'un pipeline qui tourne chaque nuit.

🧠 Concept — La prod, c'est quand personne ne regarde

Ton classifieur marche sur ton poste ? Bravo. Maintenant imagine-le tourner chaque nuit à 2 h, sur les tickets de la veille, sans humain devant l'écran. Cinq sujets font la différence :

  1. Retry avec backoff : l'API peut renvoyer une erreur transitoire (surcharge 529, limite de débit 429). Le SDK Python réessaie automatiquement ces erreurs — mais comprends le mécanisme : on attend un peu, on réessaie, on abandonne proprement après N tentatives. On ne martèle jamais l'API en boucle serrée.
  2. Timeouts : un appel qui ne répond pas ne doit pas geler ton pipeline. Fixe un délai maximal et traite le dépassement comme une erreur normale.
  3. Coûts sous contrôle : logue response.usage (tokens in/out) à chaque appel. 10 000 tickets × un system prompt obèse = une facture surprise. Estime AVANT de lancer : nb d'appels × tokens moyens × tarif du modèle (docs.claude.com).
  4. Batch API pour le non-urgent : reclassifier 50 000 tickets historiques n'a pas besoin de temps réel. Le batch coûte moins cher et lisse la charge — résultats sous 24 h.
  5. Logs exploitables : pour chaque document traité, trace l'identifiant, le résultat, les tokens, la durée, et les erreurs. Le jour où le support te demande « pourquoi ce ticket urgent a été classé urgence 2 ? », tu réponds en 30 secondes au lieu de rejouer l'appel à l'aveugle.

Les 5 pratiques dans un squelette de pipeline

import anthropic
import json
import logging
import time

logging.basicConfig(level=logging.INFO, filename="classif_tickets.log")

# max_retries : le SDK gère le backoff sur les erreurs transitoires.
# timeout : au-delà de 30 s, on considère l'appel perdu.
client = anthropic.Anthropic(max_retries=3, timeout=30.0)

def traiter_lot(tickets: list[dict]) -> list[dict]:
    resultats = []
    total_tokens = 0
    for t in tickets:
        debut = time.time()
        try:
            resp = client.messages.create(
                model="claude-haiku-4-5",
                max_tokens=300,
                system=SYSTEM,  # voir leçon 8.3
                messages=[{"role": "user", "content": t["texte"]}],
            )
            resultat = json.loads(resp.content[0].text)
            total_tokens += resp.usage.input_tokens + resp.usage.output_tokens
            logging.info("ticket=%s categorie=%s urgence=%s tokens=%s duree=%.1fs",
                         t["id"], resultat["categorie"], resultat["urgence"],
                         resp.usage.output_tokens, time.time() - debut)
        except Exception as e:
            # Un ticket en échec n'arrête pas le lot : on trace et on continue.
            logging.error("ticket=%s ECHEC %s", t["id"], e)
            resultat = {"categorie": "autre", "urgence": 3,
                        "resume": "ERREUR_TRAITEMENT", "station": None}
        resultats.append({"id": t["id"], **resultat})
    logging.info("lot termine : %s tickets, %s tokens", len(tickets), total_tokens)
    return resultats

💡 Réflexe pro — L'estimation de coût en 30 secondes

Avant tout lancement en volume, fais le calcul de coin de table : nb d'appels × (tokens du system prompt + tokens moyens du document + tokens de sortie) × tarif du modèle. Exemple : 10 000 tickets × ~700 tokens par appel → 7 millions de tokens. Compare le résultat entre modèle rapide et modèle puissant sur docs.claude.com : c'est souvent ce calcul qui décide du modèle, pas la qualité. Et si le lot n'est pas urgent : Batch API, tarif réduit.

🎮 Mini-jeu : prêt pour la prod, ou bombe à retardement ?

Ton binôme builder te montre ses choix d'implémentation pour le pipeline de classification nocturne. Trie chaque choix : bonne pratique ou bombe à retardement ?