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 :
- Retry avec backoff : l'API peut renvoyer une erreur transitoire (surcharge
529, limite de débit429). 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. - 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.
- 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). - 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.
- 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 ?