Routage de tickets avec l’IA : Strands Decider 2B en Python
Un modèle de décision local pour router vos tickets de support en Python : certitude du choix, seuil de repli vers un humain et détection des urgences.

👁 118 lectures
Un modèle de décision est bien plus adapté au routage de tickets qu’un simple modèle de chat : il choisit un pôle dans votre liste, attribue un score de confiance et vous permet de renvoyer automatiquement les cas incertains vers un opérateur humain. Voici un script Python fonctionnel utilisant le modèle ouvert Strands Decider 2B que nous avons testé sur notre machine, avec nos résultats et deux pièges à éviter. Pour comprendre le fonctionnement de ces modèles et leurs performances sur notre banc d’essai de 30 tickets, consultez notre guide complet sur les modèles de décision IA.
Les montants en EUR sont des conversions indicatives des prix publiés en USD, calculées au taux de référence de la BCE du 2026-10-12 : 1 EUR = 1.1181 USD. Il ne s’agit pas de tarifs locaux confirmés ni de montants finaux toutes taxes comprises. Le fournisseur et la banque peuvent appliquer d’autres taux ou frais. ECB.
Ce que nous allons construire
Un script qui renvoie, pour chaque ticket entrant :
- le pôle - l’un des 6 services définis (paiements, livraison, retours, compte, pannes, ventes) ou « autre »,
- la confiance de la décision, notée de 0 à 1,
- la file de destination - le service ciblé si le score est suffisant, ou
do_czlowieka(vers l’humain), - l’urgence - booléen indiquant si l’incident exige une intervention immédiate dans la journée.
Le résultat produit une ligne JSON par ticket, prête à être injectée dans votre outil de helpdesk, un tableur ou un flux d’automatisation.
Les prérequis
- Python 3.10 ou version plus récente.
- PyTorch - configuré avec CUDA si vous disposez d’une carte NVIDIA (le modèle consomme un peu moins de 4 Go de VRAM). Sans GPU dédié, l’exécution sur processeur reste possible, mais plus lente.
- Le package
strands-decider(licence Apache-2.0). Au premier démarrage, il télécharge environ 4,4 Go : le modèle de base Qwen3.5-2B-Base et l’adaptateur fourni par AWS.
pip install strands-decider
Sous Windows, l’installateur pip remplace parfois la variante PyTorch CUDA par une version CPU standard. Vérifiez votre environnement avec python -c "import torch; print(torch.cuda.is_available())". Si la console affiche False, réinstallez PyTorch depuis l’index pytorch.org adapté à votre version de CUDA - un contretemps auquel nous avons nous-mêmes été confrontés.
Le script routing.py
# Routage de tickets avec le modele de decision Strands Decider 2B (en local, Apache-2.0).
# pip install strands-decider (avec PyTorch et CUDA si vous avez une carte NVIDIA)
import json, sys
from strands_decider.infer import load_engine
from strands_decider.schema import SystemOneRequest
MODEL = "StrandsAgents/strands-decider-2B-hobson-v19"
DZIALY = {
"platnosci": "Płatności, faktury, obciążenia karty, abonament",
"dostawa": "Wysyłka, kurier, śledzenie paczki, adres dostawy",
"zwrot": "Zwroty towaru, reklamacje, wymiana, uszkodzony produkt",
"konto": "Logowanie, hasło, dane konta, bezpieczeństwo konta, RODO",
"awaria": "Błędy aplikacji lub strony, awarie, integracje, wydajność",
"sprzedaz": "Pytania o ofertę, plany, rabaty, wdrożenia dla firm",
"inne": "Z treści nie da się ustalić, czego dotyczy zgłoszenie",
}
PROG_DZIAL = 0.60 # en dessous de ce score de confiance, le ticket part a un humain
PROG_PILNE = 0.40 # seuil calibre sur nos 30 tickets de test - a adapter a vos donnees
moteur = load_engine(MODEL, device="cuda") # "cpu" fonctionne aussi, mais plus lentement
def router(texte: str) -> dict:
requete = SystemOneRequest(state=texte, questions={
"dzial": {"type": "choice", "instructions": "Do którego działu trafia to zgłoszenie?", "criteria": DZIALY},
"pilne": {"type": "noul", "instructions": "Czy zgłoszenie wymaga reakcji jeszcze dziś (awaria, utrata pieniędzy, włamanie, nieprzekraczalny termin)?"},
})
reponse = moteur.evaluate(requete).answers
dzial, confiance, p_urgent = reponse["dzial"].choice, reponse["dzial"].confidence, reponse["pilne"].noul
file = dzial if confiance >= PROG_DZIAL and dzial != "inne" else "do_czlowieka"
return {"file": file, "pole": dzial, "confiance": round(confiance, 2),
"urgent": p_urgent >= PROG_PILNE, "p_urgent": round(p_urgent, 2)}
if __name__ == "__main__":
for ligne in sys.stdin:
if ligne.strip():
print(json.dumps({"texte": ligne.strip()[:60], **router(ligne)}, ensure_ascii=False))
Traduction des critères testés en polonais : platnosci = paiements, factures, débits, abonnement ; dostawa = livraison, coursier, suivi, adresse ; zwrot = retours, réclamations, échange, produit endommagé ; konto = connexion, mot de passe, compte, sécurité, RGPD ; awaria = bugs d’application ou de site, pannes, intégrations, performances ; sprzedaz = questions commerciales, offres, remises, intégration pro ; inne = impossible de déterminer l’objet de la demande. Questions associées : « À quel service ce ticket doit-il être adressé ? » et « Ce ticket nécessite-t-il une réaction dès aujourd’hui (panne, perte d’argent, intrusion, échéance critique) ? »
Quelques choix d’architecture à noter :
- Nous avons testé les critères en polonais. Dans notre banc d’essai, ces descriptions locales ont fonctionné aussi bien que des définitions en anglais, tout en étant plus faciles à relire et maintenir pour une équipe support.
- Deux questions regroupées en un seul appel. Le modèle traite le contexte une seule fois et évalue les questions en parallèle pour un coût marginal nul. Ajouter un troisième critère (par exemple « le client menace-t-il de résilier ? ») n’augmente pas la latence.
- La décision finale revient au code, pas au modèle. L’IA renvoie de simples probabilités : c’est votre logique Python qui applique les seuils et oriente les flux, ce qui rend le paramétrage modulable sans retoucher au modèle.
Exécution et résultats
Le script lit les tickets sur l’entrée standard, à raison d’une ligne par demande :
python routing.py < zgloszenia.txt
Voici les mesures obtenues sur notre GeForce RTX 4090 pour six requêtes que le système n’avait jamais traitées auparavant (nous avons mené ce test sur des messages rédigés en polonais) :
| Ticket | Sans option « autre » | Avec option « autre » |
|---|---|---|
| Od rana nie działa logowanie przez Google, cały zespół stoi. (Depuis ce matin la connexion via Google ne fonctionne plus, toute l’équipe est bloquée.) | compte 0,54 - humain, urgent | compte 0,46 - humain, urgent |
| Proszę o fakturę VAT na firmę do zamówienia 77120. (Merci d’éditer une facture avec TVA pour ma société pour la commande 77120.) | paiements 0,46 - humain | autre 0,63 - humain |
| Paczka wróciła do nadawcy, bo kurier nie mógł znaleźć adresu. (Le colis est retourné à l’expéditeur car le livreur n’a pas trouvé l’adresse.) | livraison 0,96 | livraison 0,90 |
| Czy macie zniżki dla szkół i uczelni? (Proposez-vous des tarifs réduits pour les écoles et universités ?) | ventes 0,58 - humain | ventes 0,31 - humain |
| Hej, mam pytanie. Coś mi nie gra. (Bonjour, j’ai une question. Quelque chose ne tourne pas rond.) | panne 0,80 | panne 0,41 - humain |
| Zwracam kurtkę, ale nie wiem, czy zwrot pójdzie na kartę czy na konto. (Je renvoie une veste, mais j’ignore si le remboursement sera versé sur ma carte ou mon compte.) | retours 0,94 | retours 0,84 |
Sur notre GPU, chaque requête a pris environ 135 ms, après une phase d’initialisation du modèle de 16 s. Toutes les demandes classées automatiquement ont atteint le bon service. De son côté, l’alerte d’urgence ne s’est déclenchée que pour le blocage de connexion impactant l’équipe au complet : le comportement attendu a été parfaitement respecté.
Premier piège : l’excès de confiance sur un message vague
Dans notre première version, nous n’avions pas prévu d’option « autre ». Face au message vague « Hej, mam pytanie. Coś mi nie gra. » (« Bonjour, j’ai une question. Quelque chose ne tourne pas rond. »), le modèle a choisi la catégorie panne avec une confiance de 0,80. Ce score dépassant notre seuil, le ticket aurait été envoyé à tort aux ingénieurs support. Il ne s’agit pas d’une défaillance du modèle : contraint de choisir parmi six catégories, la somme de ses probabilités doit toujours être égale à 1. Un modèle de décision ne répondra jamais « je ne sais pas » sans porte de sortie explicite.
La parade : créer une catégorie inne (« autre ») accompagnée de l’instruction « le contenu ne permet pas de déterminer l’objet de la demande », assortie d’une règle dans le code qui réoriente d’office cette classe vers un opérateur humain. Avec cet ajustement, le score sur le message creux est tombé à 0,41, provoquant son transfert vers un agent.
Second piège : l’option « autre » dilue la certitude
Insérer un choix fourre-tout a une contrepartie : la probabilité globale se disperse. La confiance a légèrement fléchi, y compris sur des cas évidents (le colis est passé de 0,96 à 0,90, la veste de 0,94 à 0,84), et la demande de facture a fini dans « autre » plutôt que dans les paiements. Pour corriger ce biais :
- Enrichissez vos descriptions avec le vocabulaire métier exact. Si une demande de facture glisse vers « autre », intégrez explicitement « facture avec TVA, facture rectificative, duplicata » dans la définition du service facturation.
- Calibrez votre seuil de déclenchement sur votre historique. Isolez 50 à 100 tickets déjà traités, soumettez-les au script et déterminez à quel niveau de confiance l’automatisation atteint le zéro faute. Notre seuil de 0,60 sert de point de départ, pas de dogme.
- Surveillez la proportion d’escalades humaines. Si plus de 30 à 40 % des tickets partent vers l’humain, vos définitions manquent de netteté ou vos services se chevauchent.
Mesurer l’urgence : un curseur à étalonner
Lors de nos essais sur 30 tickets, Strands Decider a attribué aux cas critiques une probabilité comprise entre 0,22 et 0,47. Avec un seuil arbitraire placé à 0,5, aucun incident n’aurait été signalé comme urgent, d’où notre choix de fixer le curseur à 0,40 dans le script. À l’inverse, le modèle plus volumineux Clef-flash de Cloudflare avait tendance à surévaluer l’urgence et donnait ses meilleurs résultats avec un seuil à 0,95. La règle reste constante : traitez les sorties probabilistes comme un score relatif et déterminez le seuil de bascule sur vos propres données de test.
Comment basculer sur un autre moteur
Strands Decider, Cloudflare Clef, ainsi que Kev et Jev de TypeSafe partagent la même structure de requêtes (System One : state + questions). Le code peut migrer vers ces alternatives simplement en modifiant l’interface d’appel :
- Clef-flash en local (sur GPU 24 Go) : via la méthode
systemone()du fichierjoint_schema_model.pydisponible dans le dépôtCloudflare/clef-flash. Lors de nos benchmarks, il s’est montré légèrement plus véloce (112 ms) et plus tranché, mais réclame 18 Go de VRAM. Retrouvez notre retour complet dans notre article dédié à Cloudflare Clef. - Workers AI : par un simple appel
POSTsur l’endpoint/ai/run/@cf/cloudflare/clef-flashavec la même structure JSON, facturé 0,0805 EUR (0,09 USD) pour un million de tokens en entrée avec un quota gratuit quotidien (compte Cloudflare nécessaire). - Serveur HTTP Strands : via la commande
strands-decider serve ... --port 8000et des requêtes adressées à/v1/systemone, ce qui évite au script Python d’instancier lui-même les poids du modèle en mémoire.
Pour approfondir les détails d’architecture, la licence et les scores complets, parcourez notre dossier sur Strands Decider 2B.
Dans quels cas éviter les modèles de décision pour le tri
- Si vous souhaitez générer directement une réponse rédigée au client : cette tâche nécessite un grand modèle génératif (LLM). En revanche, le modèle de décision peut parfaitement sélectionner le template de réponse adéquat.
- Si votre flux se limite à une poignée de messages par jour : solliciter un modèle généraliste avec du JSON structuré se révèle plus direct (une méthode détaillée dans notre guide sur le Structured output).
- Si l’organisation de vos équipes change chaque semaine sans historique étiqueté : vous manquerez de recul pour étalonner des seuils statistiques fiables.
Sources
- Strands Decider 2B sur Hugging Face
- Dépôt strands-labs/strands-decider sur GitHub
- Nous avons testé ce script le 5 octobre 2026 sur une configuration équipée d’une RTX 4090, Windows 11, Python 3.12, PyTorch 2.11, transformers 5.18 et strands-decider 0.1.0.
More on this from PROMPTIF
outils d'ia·Quelle IA pour la musique de vidéo ? Notre comparatifQuelle IA choisir pour la musique de fond d'une vidéo ? Comparatif entre Eleven Music, Stable Audio 3 et Suno : licences, prompts et tarifs.

