Guidecoheretiny-ayalanggraph

Construire un agent de support multilingue avec Tiny Aya

Un agent de support multilingue construit avec Tiny Aya (3,35B) de Cohere Labs, testé en traduction technique face à des modèles 8 à 10 fois plus gros.

16 min de lecturePublié le 7 avril 2026Mis à jour le 30 octobre 2026

Geoffrey Harrazi

Directeur produit · Cofondateur Idun Group

En-tête : dans le test d'Idun, le petit modèle Tiny Aya traduit 2 000 tickets de support par heure, contre 137 pour un modèle de 27B.

Un modèle de 3,35 milliards de paramètres et un agent LangGraph débarquent dans une file de tickets de support. La suite : un pipeline multilingue étonnamment compétitif.

Quand chaque ticket est un problème de langue

Le support en entreprise ne se fait pas dans une seule langue. Un ingénieur français remonte un bug avec une stack trace complète. Une équipe d'exploitation turque ouvre un ticket sur un planning de sauvegarde qui échoue, expressions cron et extraits de logs à l'appui. Un développeur japonais décrit une fuite mémoire en citant des heap dumps et des flags JVM.

Avant de pouvoir trier, classer ou répondre à ces tickets, quelqu'un (ou quelque chose) doit les comprendre. Et comprendre un ticket de support technique multilingue est plus difficile qu'il n'y paraît. Il faut préserver les codes d'erreur, les endpoints d'API, les fragments SQL et le raisonnement causal qui rend un rapport de bug exploitable. Traduisez mal « le serveur renvoie une erreur 504 » et vous perdez le code de statut. Paraphrasez une stack trace et c'est tout le chemin de débogage qui disparaît.

L'approche classique consiste à tout faire passer par l'API d'un grand modèle, généralement 30 milliards de paramètres ou plus sur un endpoint cloud. Ça marche. C'est aussi lent (10 à 30 secondes par requête), coûteux à grande échelle, et ça crée une dépendance forte à une infrastructure externe.

On voulait savoir s'il était possible de construire un agent de support multilingue complet, propulsé principalement par un modèle de 3,35 milliards de paramètres. Détection de langue, traduction, vérification, classification, le tout assez rapide pour du tri en temps réel.

Entrée en scène de Tiny Aya

Quand Cohere Labs a sorti Tiny Aya en février 2026, le modèle a retenu notre attention. La plupart des modèles multilingues de cette taille traitent la couverture linguistique comme une case à cocher : ils supportent beaucoup de langues, mais les performances chutent brutalement en dehors de l'anglais et de quelques langues bien dotées en données. Tiny Aya fait autrement.

Plutôt que de s'éparpiller, Tiny Aya couvre 67 langues avec des variantes instruction-tuned, chacune spécialisée sur une région linguistique :

  • TinyAya-Global : le généraliste, équilibré sur les 67 langues.
  • TinyAya-Earth : Afrique et Asie de l'Ouest (arabe, turc, swahili, amharique, haoussa, etc.)
  • TinyAya-Fire : Asie du Sud (hindi, bengali, tamoul, ourdou, etc.)
  • TinyAya-Water : Asie-Pacifique et Europe (français, japonais, chinois, portugais, espagnol, coréen, etc.)

Cette structure nous a tout de suite suggéré une architecture. Au lieu de choisir un modèle en espérant qu'il marche partout, on pouvait faire tourner le modèle global comme référence et, en parallèle, la variante spécialisée pour la langue détectée. Deux traductions, une étape de vérification, la meilleure sortie l'emporte.

À noter : le tokenizer de Tiny Aya a été conçu pour réduire la fragmentation entre systèmes d'écriture. Là où d'autres tokenizers multilingues produisent de longues séquences de tokens pour les écritures non latines (inférence plus lente, plus de mémoire), Tiny Aya produit nettement moins de tokens par phrase. Concrètement : une inférence plus rapide et une consommation mémoire plus faible.

L'architecture de l'agent

On a construit l'agent sous forme de workflow LangGraph. Chaque nœud fait une seule chose et transmet le state à la suite. Voici le pipeline complet :

START ├── fetch_ticket ──────────┐ │ ├── detect_language ──┬── translate_global ──┐ └── fetch_categories ──────┘ └── translate_sub ─────┤ ├── verify_translation ── classify ── update_ticket │ │ │ END

Huit nœuds, trois points de parallélisation, deux familles de modèles.

Récupérer le contexte

Le pipeline démarre par deux opérations en parallèle : fetch_ticket récupère le ticket dans Jira, et fetch_categories interroge le projet pour obtenir les catégories disponibles. Les deux passent par des outils MCP pour dialoguer avec Jira.

python
async def fetch_ticket(state: TicketState) -> FetchTicketResult: tools = await load_mcp_tools() tool = get_tool("jira_get_issue", tools) result = await tool.ainvoke({"issue_key": state["ticket_key"]}) # Parse summary + description from the response ...

MCP (Model Context Protocol) est un standard ouvert pour connecter des agents à des outils externes. Au lieu d'écrire un client pour l'API Jira, on déclare les outils dont on a besoin et on les utilise comme des outils LangChain standards. Le code de l'agent reste concentré sur la logique métier.

Détection de la langue

detect_language utilise TinyAya-Global pour identifier la langue source, puis la fait correspondre à la variante régionale appropriée :

python
_global_model = create_tiny_aya(TinyAyaVariant.GLOBAL) def detect_language(state: TicketState) -> DetectResult: response = _global_model.invoke( [HumanMessage(content=f"What language is this? Answer in English: {state['ticket_text']}")] ) # Match response against supported languages language = next( (lang for lang in _SUPPORTED_LANGUAGES if lang.lower() in raw.lower()), None, ) variant = get_variant_for_language(language) return DetectResult(detected_language=language, sub_variant=variant)

La table de routage :

python
LANGUAGE_TO_VARIANT = { "French": "water", "Arabic": "earth", "Hindi": "fire", "Turkish": "earth", "Japanese": "water", "Chinese": "water", "Portuguese": "water", "Spanish": "water", "Swahili": "earth", "English": "global", }

Double traduction

Après la détection de langue, le pipeline se divise en deux chemins de traduction parallèles :

translate_global fait passer le ticket par TinyAya-Global. Les données d'entraînement les plus larges, la sortie la plus régulièrement formatée d'une langue à l'autre.

translate_sub fait passer le même ticket par la variante régionale détectée (Earth, Fire ou Water). Un ancrage linguistique plus profond pour ses langues cibles, potentiellement plus fin pour saisir les nuances.

Les deux tournent en parallèle avec le même prompt système :

python
_SYSTEM_PROMPT = ( "You are a professional translator. " "Translate the following text to English. " "Output ONLY the English translation, nothing else. " "Do not add notes, explanations, or commentary." )

Cohere Labs a conçu les variantes régionales pour offrir « un ancrage linguistique plus fort et davantage de nuance culturelle », tandis que le modèle global fournit « une colonne vertébrale multilingue cohérente ». On voulait voir si la combinaison des deux ferait mieux que chacun pris isolément.

Vérification

verify_translation reçoit les deux sorties et utilise TinyAya-Global comme arbitre. Il choisit la meilleure traduction, avec un biais en faveur de la variante régionale en cas de désaccord :

python
_USER_PROMPT = ( "Below are two English translations of the same text.\n\n" "Translation A:\n{global_translation}\n\n" "Translation B:\n{sub_translation}\n\n" "Pick the better translation. If they conflict, pick Translation B. " "Write ONLY the final English translation below, no explanation:\n\n" "Final translation:" )

Le biais vers la Translation B traduit l'idée que, quand deux modèles divergent, l'interprétation du spécialiste a plus de chances d'être juste. Comme on le verra dans la partie évaluation, la réalité s'est révélée plus nuancée que prévu.

Classification et mise à jour

Une fois qu'on dispose d'une traduction anglaise vérifiée, le pipeline sort du domaine de Tiny Aya. classify utilise Gemini 3 Flash pour attribuer une catégorie et une priorité (Critical/High/Medium/Low). On utilise Gemini ici parce que la classification est une tâche de raisonnement sur du texte anglais, pas un problème multilingue.

update_ticket réécrit tout dans Jira : langue détectée, variante utilisée, traduction vérifiée, catégorie et priorité. Le ticket d'origine est conservé, l'analyse est ajoutée à la suite.

Le state

Le pipeline communique via un dictionnaire de state typé :

python
class TicketState(TypedDict, total=False): ticket_key: str ticket_text: str detected_language: str global_translation: str sub_translation: str verified_translation: str sub_variant: str category: str priority: str categories: list[str] updated: bool

Chaque nœud lit ce dont il a besoin et réécrit ses résultats. LangGraph gère le routage, le parallélisme et la fusion du state. total=False signifie que les nœuds ne renseignent que les champs dont ils sont responsables.

Du notebook à la production

Construire un agent LangGraph qui marche en local, c'est une chose. Le faire tourner comme un service de production avec observabilité, contrôle d'accès, gestion des outils et couche de serving, c'en est une autre. La plupart des équipes passent plus de temps d'ingénierie sur cette infrastructure que sur la logique de l'agent.

Outils MCP et contrôle d'accès

Notre agent dialogue avec Jira : récupération des tickets, recherche des catégories, écriture des résultats. Sans protocole standard, il faut écrire un client REST, gérer l'authentification, parser les réponses, gérer les connexions : quelques centaines de lignes de code d'intégration, au minimum. Un serveur MCP (mcp-atlassian, en transport STDIO) met jira_get_issue, jira_search et jira_update_issue à disposition de l'agent. Restreignez les outils MCP accessibles à chaque agent dès que plusieurs agents tournent avec des niveaux de permission différents.

Pour un agent de support qui traite des données clients, les guardrails sur les entrées et les sorties (détection de données personnelles, défenses contre l'injection de prompt, restrictions thématiques, listes d'autorisation et de blocage) ne sont pas optionnels, pas plus que le SSO et le RBAC : l'équipe support interne a un accès complet, les intégrations externes se contentent de soumettre des tickets sans voir les traductions brutes.

Observabilité avec Langfuse

Construire l'agent, c'était la partie amusante. Comprendre pourquoi un ticket prenait 30 secondes au lieu de 3, ou pourquoi une traduction revenait vide, c'était la partie difficile.

On a choisi Langfuse parce qu'il trace les vraies paires prompt/complétion, les décomptes de tokens et le coût par appel de modèle, pas seulement des spans et des durées. Chaque exécution de nœud, chaque appel de modèle et chaque invocation d'outil est tracé. Vous pouvez ouvrir n'importe quel span pour voir les prompts exacts, les réponses brutes, les décomptes de tokens et la latence.

Quelques problèmes repérés grâce aux traces :

  • Le premier appel de traduction de chaque variante était beaucoup plus lent que les suivants. En fait, l'API d'inférence HuggingFace chargeait le modèle à la première requête. On s'est mis à mettre en cache les instances de modèle.
  • Un ticket a obtenu un score d'évaluation étonnamment bas. En remontant les spans, on a vu que le problème venait de la vérification, pas de la traduction. Le modèle global abîmait la sortie de la sous-variante pendant la comparaison.
  • L'étape de vérification était parfois le nœud le plus lent, devant la traduction. Le modèle « hésitait » entre deux traductions proches au lieu d'en choisir une rapidement.

Langfuse fournit aussi des métriques agrégées sur l'ensemble des exécutions : latence par nœud, taux d'erreur, consommation de tokens dans le temps, coûts ventilés par modèle. Quand on traite des centaines de tickets par jour, c'est comme ça qu'on sait que tout fonctionne.

Évaluer la qualité de traduction

Construire l'agent, c'était la partie facile. Il nous fallait savoir comment Tiny Aya se comporte réellement sur du contenu technique face à des modèles plusieurs fois plus gros, alors on a construit une évaluation systématique.

Le dataset

40 tickets de support technique répartis sur 10 langues : français, espagnol, portugais, arabe, turc, hindi, japonais, chinois, swahili et anglais (4 par langue). Chaque ticket contient le type de détail technique qu'on trouve dans une vraie file de support :

  • Stack traces et logs d'erreur : tracebacks Python, exceptions Java, erreurs Node.js avec des chemins de fichiers et des numéros de ligne qui doivent être préservés à l'identique.
  • Requêtes SQL et détails de base de données : SELECT avec des clauses JOIN, noms d'index, VACUUM FULL, identifiants précis de tables et de colonnes.
  • Endpoints d'API et configuration : chemins REST comme /api/v2/batch/process, variables d'environnement, clés de configuration, codes de statut HTTP.
  • Des nombres qui doivent survivre : codes d'erreur, numéros de port, tailles mémoire (3GB, 12GB), seuils (100 requêtes), durées (fenêtre de 24 heures).
  • Raisonnement causal : « Quand X se produit, Y échoue à cause de Z. » Cassez cette chaîne et le ticket devient inutile.

Les tickets ont été rédigés dans chaque langue par des prompts de niveau natif, pas traduits automatiquement depuis l'anglais. Des données de test traduites automatiquement auraient mesuré la capacité du modèle à inverser sa propre sortie, ce qui n'était pas l'objectif.

Le juge

On a utilisé Gemini 3 Flash comme LLM juge via la métrique GEval de deepeval. Chaque traduction est notée sur une grille de 0 à 10 (normalisée de 0 à 1) selon six critères :

  1. Nombres : tous les nombres sont-ils préservés à l'identique ?
  2. Identifiants techniques : endpoints d'API, chemins de fichiers, clés de configuration et extraits SQL sont-ils conservés mot pour mot ?
  3. Complétude : manque-t-il des phrases ou des détails ?
  4. Exactitude factuelle : la traduction introduit-elle des informations fausses ?
  5. Fluidité : un ingénieur support de langue maternelle anglaise la trouverait-il confuse ?
  6. Cohérence : la structure logique est-elle maintenue (étapes numérotées, cause-effet) ?

On les a associés à des ancres de score explicites via la classe Rubric de deepeval :

  • 0 à 2 : inintelligible, mauvaise langue, ou l'essentiel du contenu manquant
  • 3 à 4 : sujet général saisi mais erreurs majeures
  • 5 à 6 : compréhensible et plutôt complet, mais plusieurs erreurs
  • 7 à 8 : exact et fluide, avec seulement des défauts mineurs
  • 9 : quasi parfait
  • 10 : sans défaut

Ça a fait la différence. Dans les premières itérations, on n'utilisait que des étapes d'évaluation avec un scoring par pénalités, et tous les modèles obtenaient un score quasi parfait. La grille a donné au juge des attentes calibrées sur ce à quoi ressemble réellement chaque niveau de score.

Les concurrents

On a évalué Tiny Aya face à des modèles de 4B à 32B paramètres, tous via l'API d'inférence Hugging Face :

ModèleParamètres
TinyAya-Global3,35B
TinyAya-Routed3,35B (variantes régionales)
Gemma3n-E4B~4B
Qwen3-4B4B
Qwen3-8B8B
Qwen3-32B32B
Qwen3.5-27B27B

« TinyAya-Routed » désigne notre pipeline de routage, qui envoie chaque ticket vers la variante régionale adaptée selon la langue détectée. Cela permet de comparer directement le modèle global seul et le routage spécialisé.

Chaque modèle a traduit les 36 tickets non anglais. On a parallélisé avec pytest-xdist entre les modèles et ThreadPoolExecutor au sein du lot de tickets de chaque modèle (3 threads, pour rester dans les limites de débit de HuggingFace).

Les résultats

ModèleParamsScore moyenLatence moyenne
Qwen3.5-27B27B0,98126,2 s
Gemma3n-E4B~4B0,9724,5 s
Qwen3-32B32B0,9532,1 s
Qwen3-8B8B0,9504,3 s
Qwen3-4B4B0,9442,6 s
TinyAya-Global3,35B0,9221,7 s
TinyAya-Routed3,35B0,8582,9 s

TinyAya-Global obtient 0,922, entre 3 et 6 % sous des modèles jusqu'à 10 fois plus gros, avec l'inférence la plus rapide de la comparaison. À 1,7 seconde par ticket, cela représente environ 2 000 tickets par heure. Qwen3.5-27B obtient 0,981, mais à 26,2 secondes par ticket, soit environ 137 par heure.

L'écart d'efficacité est encore plus marqué :

ModèleScore/secondeScore/paramètre
TinyAya-Global0,5460,275
Qwen3-32B0,4550,030
Qwen3-4B0,3620,236
Qwen3-8B0,2220,119
Gemma3n-E4B0,2140,243
Qwen3.5-27B0,0370,036

Meilleur score par seconde et meilleur score par paramètre de tous les modèles testés. Aucun modèle de la comparaison n'est à la fois plus rapide et meilleur.

Performances par langue

LangueTinyAya-GlobalQwen3.5-27BGemma3n-E4B
Arabe0,971,000,97
Français0,951,000,97
Espagnol0,970,971,00
Hindi0,950,971,00
Portugais0,950,950,93
Turc0,901,000,95
Japonais0,900,950,97
Swahili0,880,971,00
Chinois0,821,000,95

Tiny Aya est le plus solide en arabe (0,97), espagnol (0,97), français (0,95), hindi (0,95) et portugais (0,95), à chaque fois entre 1 et 5 % du meilleur modèle. L'écart se creuse pour le turc (0,90), le japonais (0,90), le swahili (0,88) et le chinois (0,82). Le chinois est le point faible le plus net, à 0,18 du meilleur.

C'est la régularité qui est intéressante. La distribution des scores est très resserrée. Aucun échec catastrophique, aucune sortie dégénérée. Chaque ticket a produit une traduction exploitable. Pour un système en production, cette régularité compte davantage que des pics de score occasionnels.

Global vs routé

L'un des résultats les plus surprenants. On s'attendait à ce que le routage vers la variante régionale surpasse le modèle global sur ses langues de prédilection. Dans l'ensemble, ce n'est pas le cas.

LangueVarianteGlobalRoutéÉcart
Chinoiswater0,820,90+0,08
Hindifire0,950,950,00
Swahiliearth0,880,880,00
Arabeearth0,970,95-0,03
Françaiswater0,950,93-0,02
Espagnolwater0,970,90-0,07
Turcearth0,900,78-0,12
Portugaiswater0,950,75-0,20
Japonaiswater0,900,70-0,20

Le global obtient 0,922 contre 0,858 pour le routé. Le modèle global s'est montré plus régulier, l'approche routée plus variable.

Tout se joue sur l'adéquation à la tâche. Les variantes régionales sont conçues pour un ancrage linguistique plus profond et la nuance culturelle, ce qui compte pour la génération ouverte et les contenus culturellement sensibles. Notre tâche, elle, est une traduction technique structurée avec un format de sortie strict. L'entraînement plus large du modèle global au suivi d'instructions lui a donné l'avantage ici.

L'exception, c'est le chinois : le routage vers Water a fait passer le score de 0,82 à 0,90. Cela suggère un juste milieu : utiliser le global par défaut et ne router que les langues où il sous-performe de façon avérée. C'est exactement ce que permet notre architecture à double traduction.

Les deux configurations utilisent un modèle de 3,35B. Qu'on en soit à débattre de stratégies de routage entre variantes d'un modèle aussi petit, avec des scores entre 0,85 et 0,92, en dit long sur le niveau actuel des petits modèles multilingues.

Ce qu'on en retient

Les petits modèles peuvent rivaliser sur de vraies tâches. Avec 3,35B paramètres, TinyAya-Global reste entre 3 et 6 % sous des modèles 8 à 10 fois plus gros en traduction technique. L'écart est réel mais étroit. Et être 15 fois plus rapide que les gros modèles change ce qui devient possible : 2 000 tickets/heure contre 137.

La régularité l'emporte sur la performance de pointe. TinyAya-Global n'a jamais échoué de façon catastrophique sur 36 traductions. Chaque sortie était exploitable. L'approche routée a parfois obtenu de meilleurs scores, mais elle est aussi tombée jusqu'à 0,10. En production, le système qui obtient régulièrement 0,92 est plus utile que celui qui alterne entre 0,95 et 0,50.

Concevoir une évaluation est plus difficile qu'il n'y paraît. Notre premier run donnait des scores quasi parfaits à tous les modèles (la grille était trop indulgente). Le deuxième pénalisait comme « hallucination » un préambule inoffensif du modèle, et notait de bonnes traductions à 0,05. Il a fallu plusieurs itérations pour obtenir des scores qui différencient réellement la qualité des modèles. Des vérifications ponctuelles n'auraient jamais révélé la sous-performance du routage, l'exception du chinois ni les modes d'échec de chaque variante.

L'infrastructure est un travail non différenciant. Le temps doit aller à la logique de l'agent et à la conception de l'évaluation, pas à construire une couche de serving ou à déboguer l'authentification Jira. Un serveur MCP remplace des centaines de lignes de client API.

Les familles de modèles sont un outil de conception. La structure de Tiny Aya (une colonne vertébrale globale plus des spécialistes régionaux) a directement façonné notre architecture. L'étape de double traduction existe parce que la famille de modèles la rend possible. Le routage a fait moins bien que le global dans notre évaluation, mais l'architecture tient. Sur d'autres tâches (génération ouverte, contenus culturellement sensibles), l'ancrage plus profond des variantes régionales pourrait faire la différence.

À vous de jouer

Le code, la suite d'évaluation et les résultats sont tous open source :

Si vous lancez cette évaluation sur vos propres données et obtenez des résultats différents, on sera curieux d'en entendre parler.

Cet article vous a-t-il été utile ?

À propos de l'auteur

Geoffrey Harrazi« geoffrey »

Directeur produit · Cofondateur Idun Group

Directeur produit et cofondateur d'Idun Group. Produit, business et ventes. Ancien LVMH. Porte Idun Cockpit, qui mesure l'usage réel de l'IA déjà déployée en entreprise : licences, assistants et agents.

LinkedInSite23 articles publiés

Recevez nos guides sur la valeur de l'IA chaque mois

Retours de terrain sur l'usage réel de l'IA en entreprise : adoption, coûts, ce qu'elle rapporte et quoi en faire. Pas de spam, jamais, promis.

1 email par mois. Pas plus.

Désinscription à tout moment, par le lien présent dans chaque envoi. Vos données : politique de confidentialité