Ce que vous saurez faire à la fin
- Définir une taxonomie multi-label (catégorie, sous-catégorie, urgence, sentiment, langue) pour 10 000 tickets
- Construire un prompt few-shot robuste qui atteint 90%+ d’accord avec un annotateur humain
- Industrialiser la classification via la Batch API à 50% du tarif standard
- Comprendre pourquoi le fine-tuning n’est pas disponible chez Anthropic et comment compenser
- Intégrer les classifications retournées dans Zendesk pour automatiser routing, SLA et reporting
Durée : 4h. Pré-requis : compte Anthropic API, export CSV ou JSON de tickets Zendesk (champs ticket_id, subject, description, requester), Python 3.10+ avec SDK anthropic, budget API entre 12 000 et 25 000 FCFA pour classifier 10 000 tickets en Haiku.
Étape 1 — Concevoir la taxonomie multi-label
Une taxonomie mal conçue ruine le projet. Limitez-vous à 4 dimensions maximum : catégorie principale (8 à 12 valeurs), sous-catégorie (3 à 5 par catégorie principale), urgence (basse, moyenne, haute, critique), sentiment (positif, neutre, négatif, très_négatif). Ajoutez éventuellement une 5e dimension « langue détectée » si vous opérez sur plusieurs marchés (Sénégal, Côte d’Ivoire, Maroc).
| Catégorie | Sous-catégories | Volume estimé |
|---|---|---|
| Livraison | retard, perdu, mauvaise adresse, livreur | 32% |
| Produit | défaut, conformité, mode emploi, garantie | 24% |
| Paiement | échec carte, débit double, remboursement | 18% |
| Compte | connexion, email, mot de passe, suppression | 11% |
| Commercial | devis, B2B, gros volume, partenariat | 8% |
| Autre | presse, candidature, divers | 7% |
Étape 2 — Annoter manuellement 200 tickets de référence
Sans gold standard, impossible de mesurer la précision. Sélectionnez 200 tickets représentatifs (échantillonnage stratifié par catégorie estimée) et faites-les annoter par 2 personnes en parallèle. Mesurez l’accord inter-annotateur (Cohen’s kappa). Cible minimum : 0,75. Si l’accord est plus bas, votre taxonomie est ambiguë et vous devez la simplifier avant d’aller plus loin.
Étape 3 — Construire le prompt few-shot avec 6 à 8 exemples
PROMPT_CLASSIFICATION = """Tu es un expert en classification de tickets support pour une PME e-commerce sénégalaise.
Classifie le ticket suivant selon ces 4 dimensions et retourne UNIQUEMENT du JSON valide.
DIMENSIONS :
1. categorie : livraison | produit | paiement | compte | commercial | autre
2. sous_categorie : valeur cohérente avec la catégorie (voir exemples)
3. urgence : basse | moyenne | haute | critique
4. sentiment : positif | neutre | negatif | tres_negatif
RÈGLES URGENCE :
- critique : client mentionne avocat, presse, justice, décès, dégât matériel grave
- haute : impact financier > 100 000 FCFA, livraison événement (mariage, baptême)
- moyenne : retard livraison > 5 jours, défaut produit
- basse : question simple, FAQ
EXEMPLES :
Ticket : 'Bonjour, ma commande devait arriver lundi mais on est jeudi et toujours rien.'
Réponse : {"categorie":"livraison","sous_categorie":"retard","urgence":"moyenne","sentiment":"negatif"}
Ticket : 'J'ai été débité deux fois pour la même commande, je veux un remboursement immédiat sinon je porte plainte.'
Réponse : {"categorie":"paiement","sous_categorie":"debit_double","urgence":"critique","sentiment":"tres_negatif"}
Ticket : 'Comment changer mon adresse de livraison sur mon compte ?'
Réponse : {"categorie":"compte","sous_categorie":"adresse","urgence":"basse","sentiment":"neutre"}
Ticket : 'Merci infiniment, votre livreur a été adorable !'
Réponse : {"categorie":"livraison","sous_categorie":"livreur","urgence":"basse","sentiment":"positif"}
Ticket : 'Le produit est arrivé cassé, je dois l'offrir samedi pour un baptême, je fais comment ?'
Réponse : {"categorie":"produit","sous_categorie":"defaut","urgence":"haute","sentiment":"negatif"}
Ticket : 'Bonjour, je représente une société de 50 employés, je cherche un devis pour 200 unités.'
Réponse : {"categorie":"commercial","sous_categorie":"devis_b2b","urgence":"moyenne","sentiment":"neutre"}
Ticket à classifier :
'{ticket_text}'
Réponds UNIQUEMENT avec le JSON, sans texte avant ni après."""
Étape 4 — Premier test sur 50 tickets pour valider le prompt
import anthropic
import json
client = anthropic.Anthropic()
def classifier_ticket(texte):
prompt = PROMPT_CLASSIFICATION.format(ticket_text=texte)
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=200,
messages=[{"role": "user", "content": prompt}]
)
return json.loads(response.content[0].text)
# Test sur 50 tickets gold
correctes = 0
for ticket in tickets_gold[:50]:
pred = classifier_ticket(ticket["text"])
if pred["categorie"] == ticket["true_categorie"]:
correctes += 1
print(f"Précision catégorie : {correctes/50*100:.1f}%")
Étape 5 — Comprendre pourquoi le fine-tuning n’est pas disponible
Anthropic ne propose pas de fine-tuning sur ses modèles Claude (contrairement à OpenAI). La raison : Claude est conçu pour atteindre une qualité élevée dès le prompt zero-shot ou few-shot. Pour un cas d’usage de classification de tickets, le few-shot avec 6 à 10 exemples bien choisis atteint 88 à 94% de précision, soit l’équivalent d’un modèle fine-tuné OpenAI sur 1000 exemples. Le surcoût en tokens d’entrée (les exemples) est largement compensé par le prompt caching qui réduit le coût récurrent à 10% du tarif initial.
Étape 6 — Activer le prompt caching pour la production
def classifier_avec_cache(texte):
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=200,
system=[{
"type": "text",
"text": PROMPT_CLASSIFICATION.split("Ticket à classifier")[0],
"cache_control": {"type": "ephemeral"}
}],
messages=[{
"role": "user",
"content": f"Ticket à classifier :\n'{texte}'\n\nRéponds UNIQUEMENT avec le JSON."
}]
)
return json.loads(response.content[0].text)
Étape 7 — Bascule en Batch API pour les 10 000 tickets historiques
def construire_batch_classification(tickets):
requests_batch = []
for t in tickets:
requests_batch.append({
"custom_id": str(t["ticket_id"]),
"params": {
"model": "claude-haiku-4-5",
"max_tokens": 200,
"messages": [{
"role": "user",
"content": PROMPT_CLASSIFICATION.format(ticket_text=t["text"])
}]
}
})
return requests_batch
batch = client.messages.batches.create(
requests=construire_batch_classification(tous_les_tickets)
)
print(f"Batch lancé : {batch.id}, 10 000 tickets en cours")
Étape 8 — Calculer le coût total et le ROI
| Modèle | Tokens entrée moyens | Tokens sortie moyens | Coût 10 000 tickets (synchrone) | Coût Batch API (-50%) |
|---|---|---|---|---|
| Claude Haiku 4.5 | 900 | 50 | ~11,50 USD (7 100 FCFA) | ~5,75 USD (3 550 FCFA) |
| Claude Sonnet 4.6 | 900 | 50 | ~34,50 USD (21 400 FCFA) | ~17,25 USD (10 700 FCFA) |
Pour la classification, Haiku suffit dans 90% des cas. Réservez Sonnet aux tickets longs (> 1500 tokens) ou multilingues complexes.
Étape 9 — Récupérer les résultats et gérer les erreurs de parsing
def telecharger_classifications(batch_id):
classifications = {}
erreurs = []
for result in client.messages.batches.results(batch_id):
if result.result.type == "succeeded":
try:
data = json.loads(result.result.message.content[0].text)
classifications[result.custom_id] = data
except json.JSONDecodeError:
erreurs.append({
"ticket_id": result.custom_id,
"raw": result.result.message.content[0].text
})
else:
erreurs.append({
"ticket_id": result.custom_id,
"error": str(result.result.error)
})
return classifications, erreurs
classifs, errs = telecharger_classifications(batch.id)
print(f"{len(classifs)} succès, {len(errs)} erreurs ({len(errs)/100:.1f}%)")
Étape 10 — Re-traiter les erreurs en mode synchrone avec Sonnet
Les erreurs Haiku (généralement moins de 2%) sont souvent des tickets très longs ou ambigus. Re-traitez-les automatiquement en synchrone avec Sonnet, qui pardonne mieux les ambiguïtés et structure mieux son JSON.
def reparer_erreurs(erreurs, tickets_dict):
reparees = {}
for err in erreurs:
texte = tickets_dict[err["ticket_id"]]["text"]
try:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=200,
messages=[{
"role": "user",
"content": PROMPT_CLASSIFICATION.format(ticket_text=texte)
}]
)
reparees[err["ticket_id"]] = json.loads(response.content[0].text)
except Exception as e:
print(f"Échec persistant ticket {err['ticket_id']} : {e}")
return reparees
Étape 11 — Mesurer la précision sur le set gold
def evaluer_precision(predictions, gold):
metriques = {"categorie": 0, "sous_categorie": 0, "urgence": 0, "sentiment": 0}
for ticket_id, vraie in gold.items():
if ticket_id not in predictions:
continue
pred = predictions[ticket_id]
for dim in metriques:
if pred.get(dim) == vraie.get(dim):
metriques[dim] += 1
n = len(gold)
return {dim: round(score/n*100, 1) for dim, score in metriques.items()}
precision = evaluer_precision(classifs, gold_200)
print(precision)
# Exemple : {'categorie': 92.5, 'sous_categorie': 86.0, 'urgence': 89.5, 'sentiment': 94.0}
Étape 12 — Pousser les classifications dans Zendesk via API
import requests
ZENDESK_SUBDOMAIN = "marquexyz"
ZENDESK_AUTH = ("agent@marquexyz.com/token", "votre_token")
def maj_ticket_zendesk(ticket_id, classification):
url = f"https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets/{ticket_id}.json"
payload = {
"ticket": {
"custom_fields": [
{"id": 360001, "value": classification["categorie"]},
{"id": 360002, "value": classification["sous_categorie"]},
{"id": 360003, "value": classification["urgence"]},
{"id": 360004, "value": classification["sentiment"]}
],
"tags": [
f"cat_{classification['categorie']}",
f"urg_{classification['urgence']}"
]
}
}
return requests.put(url, json=payload, auth=ZENDESK_AUTH).json()
Étape 13 — Configurer les triggers Zendesk basés sur la classification
Une fois les champs personnalisés alimentés, créez dans Zendesk des triggers automatiques : si urgence = critique, assigner au manager + notification SMS Orange Money + SLA 30 minutes ; si catégorie = paiement et sentiment = très_négatif, escalade direction financière ; si catégorie = commercial et sous-catégorie = devis_b2b, router vers l’équipe commerciale avec SLA 4 heures. Ces triggers transforment la classification IA en automatisation business directe.
Étape 14 — Mettre en place un re-classement quotidien des nouveaux tickets
Pour les tickets entrants en temps réel, basculez en mode synchrone avec une latence cible inférieure à 3 secondes. Pour le batch quotidien (analytics et reporting), conservez la Batch API. Programmez un cron à 02h00 chaque nuit qui : récupère les tickets des dernières 24h, les classifie en batch, met à jour Zendesk, génère un rapport quotidien envoyé par email au manager support. Le coût mensuel pour 300 tickets/jour reste sous 8 000 FCFA en Haiku.
Erreurs classiques à éviter
- Lancer 10 000 tickets sans annotation gold : conséquence, vous découvrez 3 mois plus tard que la précision était de 62%, toutes les analyses sont fausses.
- Taxonomie à 30 catégories : conséquence, Claude hésite entre les classes proches, précision plafonnée à 70%, équipe perdue.
- Few-shot avec 1 seul exemple par classe : conséquence, biais de représentation, classes minoritaires ignorées.
- Oublier d’échapper les apostrophes dans les exemples : conséquence, Python casse au .format(), 2h de debug pour rien.
- Ne pas versionner le prompt : conséquence, impossible de comparer une amélioration de précision avec la version précédente.
Checklist Classification de tickets
✓ Taxonomie multi-label définie (max 4 dimensions, max 12 valeurs/dim)
✓ Set gold de 200 tickets annoté en double avec kappa > 0,75
✓ Prompt few-shot avec 6 à 8 exemples couvrant toutes les catégories
✓ Test pilote sur 50 tickets validé (précision > 88%)
✓ Prompt caching activé sur la partie statique
✓ Batch API configuré avec custom_id = ticket_id
✓ Coût simulé Haiku vs Sonnet calculé
✓ Gestion des erreurs JSON et fallback Sonnet implémentée
✓ Métriques précision par dimension calculées
✓ Champs personnalisés Zendesk créés
✓ Triggers Zendesk configurés (routing, SLA, escalade)
✓ Cron quotidien programmé pour les nouveaux tickets
✓ Rapport quotidien automatique envoyé au manager
✓ Versioning Git du prompt avec changelog
✓ Budget API plafonné dans la console Anthropic