Comment réduire les tokens IA multi-agents sans payer trop ?

En arrêtant de faire relire aux agents ce qu’ils savent déjà. Le vrai levier, c’est le cache, le chargement tardif des outils et le routage vers le bon modèle. Je vois souvent le coût exploser avant même que le workflow soit vraiment utile.

Pourquoi les tokens explosent si vite ?

Les tokens explosent parce que chaque agent relit souvent des instructions, de la mémoire, des descriptions d’outils et des contextes qui ne changent presque pas.

Dans une architecture multi-agents, le coût ne vient pas seulement de la réponse générée. Il vient surtout de tout ce que le modèle doit lire avant d’écrire quoi que ce soit. Et ça, on l’oublie vite. Un agent qui produit 300 tokens peut très bien en avoir lu 15 000 avant de répondre.

Le piège, c’est que chaque agent a besoin de contexte pour bien travailler. Mais si on lui donne tout, tout le temps, il relit du bruit. Et le bruit coûte cher.

Intégrez l’IA Générative (GenAI) dans votre activité

Nos formations IA Générative (GenAI) et prompt engineering sont conçues pour les équipes qui veulent apprendre à exploiter les IA comme un pro. Vous y apprenez à structurer des prompts efficaces, à exploiter les meilleurs outils (assistants IA type ChatGPT, générateurs d’images, audio et vidéo) et à les appliquer à vos vrais cas métiers : analyser vos données (GA4, BigQuery, CRM…), produire des contenus clairs et crédibles, prototyper plus vite et automatiser les tâches répétitives. Des ateliers 100 % pratiques, pensés pour les entreprises, pour gagner du temps, sécuriser vos usages et livrer des analyses et supports de décision de niveau pro.

Les grosses sources de consommation sont souvent les mêmes :

  • Les instructions système longues, avec les règles de comportement, le rôle de l’agent, les contraintes métier, les formats attendus.
  • Les mémoires partagées, souvent utiles, mais rarement utiles en entier à chaque appel.
  • L’historique des échanges, qui grossit tour après tour, même quand les anciens messages ne servent plus vraiment.
  • Les spécifications d’outils, comme les schémas JSON, les paramètres, les exemples d’appel.
  • La documentation d’API, parfois injectée en bloc alors que l’agent n’a besoin que d’un endpoint précis.
  • Les résultats intermédiaires entre agents, qui sont souvent recopiés tels quels au lieu d’être résumés ou filtrés.

Plus le contexte grossit, plus l’appel est lent. Et plus il est cher. C’est mécanique. Le modèle doit lire plus de tokens en entrée, produire parfois plus de tokens en sortie, et attendre plus longtemps avant de répondre. Sur un workflow avec 5 ou 10 agents, l’effet boule de neige arrive très vite.

J’ai vu ça sur un projet où le métier n’était pas si complexe. Le vrai problème, ce n’était pas la logique. C’était que chaque agent recevait presque tout le dossier client, toute la mémoire, toutes les règles, et tous les outils disponibles à chaque tour. Résultat, on payait surtout pour relire les mêmes informations.

L’objectif n’est pas de rendre l’IA moins capable. C’est l’inverse. Je veux lui donner moins de bruit et plus de contexte utile. C’est là qu’on gagne vraiment, avec quatre leviers simples : le cache d’instructions statiques, le cache sémantique, le chargement tardif des outils, et le routage de modèles.

Que faut-il mettre en cache ?

Je mets en cache tout ce qui est stable, répétitif ou déjà résolu, au lieu de le renvoyer au modèle à chaque appel. C’est souvent là que les économies commencent, surtout dans un système multi-agents où chaque agent traîne ses propres consignes, son contexte et ses petites habitudes.

Le Static Instruction Caching, aussi appelé Prefix-Match Caching, sert exactement à ça. L’idée est simple : je ne renvoie pas en permanence des blocs longs qui ne changent jamais. Si un agent a toujours le même rôle, les mêmes règles de réponse, les mêmes contraintes de format ou les mêmes politiques internes, je les stocke une fois, puis je les réutilise via une clé.

Ça peut être très concret :

  • Des règles de rôle, comme “Tu es un agent support niveau 2”.
  • Des consignes de format, comme “Réponds en JSON avec ces champs”.
  • Des politiques internes, par exemple les règles de remboursement ou de conformité.
  • Des définitions longues, comme un glossaire métier.
  • Un contexte projet stable, comme les objectifs, les outils disponibles ou les limites à respecter.

En pratique, je stocke des paires clé-valeur, des signets ou des résumés d’état statique. L’agent n’a pas besoin de relire toute la bible à chaque fois. Il récupère juste la référence utile. Chez un client, on a économisé beaucoup juste en arrêtant de renvoyer 4 pages de consignes système à chaque micro-tâche. Rien de magique, juste moins de gaspillage.

Le Semantic Caching, ou Intent-Based Recall, va plus loin. Là, je ne cherche pas seulement une correspondance exacte. Je cherche une intention proche. Pour ça, j’utilise des embeddings, c’est-à-dire des représentations numériques du sens d’un texte. Une nouvelle demande est comparée aux demandes déjà traitées. Si la similarité est élevée, je peux renvoyer une réponse existante, ou au moins éviter un appel LLM complet.

Exemple simple : un utilisateur demande “Comment réinitialiser mon mot de passe ?”, un autre écrit “J’ai perdu mon accès, je fais quoi ?”, un troisième dit “Impossible de me connecter”. Les formulations changent, mais l’intention est proche. Le cache sémantique le voit et évite de repayer une génération complète à chaque fois.

Type de cacheCe que ça économiseCas d’usage
Static Instruction CachingTokens de consignes répétéesRôles, formats, politiques, contexte stable
Prefix-Match CachingPréfixes longs identiquesPrompts système et instructions inchangées
Semantic CachingAppels LLM completsQuestions similaires, support client, FAQ intelligente
Intent-Based RecallGénérations redondantesDemandes formulées différemment mais avec la même intention

Quand charger les outils ?

Je charge les détails d’un outil seulement quand l’agent en a besoin, pas avant. C’est bête à dire, mais dans beaucoup de systèmes multi-agents, on fait encore l’inverse. On donne au modèle toutes les descriptions d’API, tous les schémas de données, toutes les règles d’usage, tous les exemples possibles dès le départ. Résultat, on paie pour du contexte qui ne sert pas, et l’agent doit réfléchir au milieu d’un tas d’infos inutiles.

Le principe s’appelle Just-in-Time Tooling, ou Lazy Loading. En clair, on ne charge pas toute la documentation des outils au démarrage. On donne d’abord un index court des capacités disponibles. Un truc simple, lisible, du genre : chercher un client, créer un ticket, résumer un document, envoyer un email, vérifier une facture.

Quand l’agent comprend ce qu’il doit faire, là seulement on charge la doc détaillée de l’outil concerné. Ses paramètres, ses contraintes, ses exemples, ses erreurs possibles. Pas ceux des autres outils. C’est souvent un petit changement dans l’orchestration, mais l’impact sur les coûts est très visible. J’ai déjà vu des agents perdre 30 à 50% de tokens juste en arrêtant de tout injecter au début.

Le bénéfice est double. On consomme moins de tokens, donc ça coûte moins cher. Mais surtout, on réduit le bruit dans la fenêtre de contexte. La fenêtre de contexte, c’est la mémoire temporaire du modèle pendant une requête. Si elle est pleine de règles CRM, facturation, emailing, calendrier et support alors que la demande concerne juste un ticket client, on augmente les risques de réponses confuses.

Un exemple très concret : un agent support reçoit “Retrouve le dernier ticket ouvert pour le client Dupont”. Il n’a pas besoin des instructions pour envoyer un email, créer une facture ou modifier une fiche CRM complète. Il a juste besoin de savoir qu’un outil de recherche de ticket existe, puis de charger sa documentation détaillée au moment où il décide de l’utiliser.

Pour décider si je charge un outil maintenant ou plus tard, je garde cette mini-checklist :

  • La demande nécessite-t-elle clairement cet outil maintenant ?
  • L’agent peut-il choisir avec un simple index des capacités ?
  • La documentation complète ajoute-t-elle une vraie information utile ?
  • Le risque d’erreur augmente-t-il si je ne charge pas les règles détaillées ?
  • Est-ce que je peux charger seulement cet outil, sans embarquer tout le reste ?

Quel modèle utiliser pour chaque tâche ?

Le bon modèle, c’est celui qui fait le travail correctement au coût le plus bas. Pas forcément le plus puissant. Dans les systèmes multi-agents, c’est même une erreur classique de tout envoyer au meilleur modèle “au cas où”. Ça marche, oui, mais la facture grimpe très vite.

J’utilise souvent deux idées simples : Task Escalation et Cost-Efficient Model Routing. Le Task Escalation consiste à commencer avec un modèle léger, puis à escalader vers un modèle plus robuste si la tâche bloque, si la confiance est faible, ou si la réponse doit être plus fiable. Le Cost-Efficient Model Routing, c’est le routage automatique vers le bon modèle selon la tâche. En clair, les petites tâches vont aux petits modèles, les tâches vraiment complexes vont aux gros modèles.

Les modèles légers sont très bons pour les opérations prévisibles. Je parle de classification, reformulation, extraction simple, résumé court, formatage JSON, détection d’intention. Pas besoin de sortir un modèle premium pour transformer une réponse en JSON propre ou détecter si un message parle de support, vente ou facturation.

Les modèles plus robustes doivent rester réservés aux tâches où il y a un vrai risque d’erreur. Arbitrage complexe, planification multi-étapes, analyse ambiguë, coordination de plusieurs agents, décision métier avec contexte incomplet. Là, payer plus cher peut être logique, parce que le coût d’une mauvaise réponse dépasse largement le coût des tokens.

Un bon routeur peut s’appuyer sur plusieurs signaux. La nature de la tâche, la longueur de l’entrée, le niveau de confiance du cache sémantique, ou un score de complexité calculé avant l’appel au modèle. Mais attention, réduire la facture ne suffit pas. Il faut mesurer la qualité. Sinon on gagne 30% sur l’API et on perd deux heures à corriger des sorties bancales. Je l’ai vu chez un client, le routage était trop agressif, les petits modèles faisaient le job 80% du temps, mais les 20% restants cassaient toute la chaîne.

TâcheModèle recommandéRaison
Classification d’un messageModèle légerTâche courte, structurée, avec peu d’ambiguïté.
Extraction simple de champsModèle légerLe modèle doit repérer des informations, pas raisonner longtemps.
Résumé courtModèle léger ou intermédiaireÇa dépend surtout de la longueur et de la précision attendue.
Planification multi-étapesModèle robusteLa tâche demande du raisonnement et une bonne gestion du contexte.
Coordination de plusieurs agentsModèle robusteLe modèle doit arbitrer, prioriser et éviter les contradictions.

La suite logique, c’est de combiner ce routage avec un cache sémantique. Comme ça, on évite d’appeler un modèle quand une réponse proche existe déjà, et quand il faut vraiment appeler l’IA, on choisit le modèle le moins cher capable de faire le travail proprement.

Comment l’implémenter simplement ?

Je commence toujours avec une version simple. Pas besoin de sortir une usine à gaz dès le départ. Un cache sémantique en mémoire, un seuil de similarité, et un petit routeur qui choisit entre un modèle léger et un modèle plus avancé. Ça suffit déjà à réduire pas mal de tokens, surtout dans les systèmes multi-agents où les mêmes demandes reviennent sous des formes légèrement différentes.

Le principe est assez direct. On encode la requête avec un modèle d’embeddings local, par exemple SentenceTransformer all-MiniLM-L6-v2. Un embedding, c’est juste une représentation numérique du texte. Ensuite, on compare cette requête avec celles déjà stockées dans le cache grâce à une similarité cosinus. Si le score dépasse 0.90, on considère que la demande est assez proche et on réutilise la réponse. Sinon, on route vers le bon modèle.

from sentence_transformers import SentenceTransformer
import numpy as np

embedding_model = SentenceTransformer("all-MiniLM-L6-v2")

semantic_cache = []

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def find_cached_response(query, threshold=0.90):
    query_embedding = embedding_model.encode(query)

    for item in semantic_cache:
        score = cosine_similarity(query_embedding, item["embedding"])
        if score >= threshold:
            return item["response"], score

    return None, None

def route_model(query):
    simple_keywords = ["résume", "corrige", "reformule", "classe"]
    complex_keywords = ["analyse", "stratégie", "architecture", "raisonnement"]

    query_lower = query.lower()

    if any(word in query_lower for word in complex_keywords):
        return "advanced_model"

    if len(query) < 180 or any(word in query_lower for word in simple_keywords):
        return "light_model"

    return "advanced_model"

def call_light_model(query):
    return f"Réponse simulée du modèle léger pour : {query}"

def call_advanced_model(query):
    return f"Réponse simulée du modèle avancé pour : {query}"

def handle_query(query):
    cached_response, score = find_cached_response(query)

    if cached_response:
        return {
            "source": "cache",
            "similarity": score,
            "response": cached_response
        }

    selected_model = route_model(query)

    if selected_model == "light_model":
        response = call_light_model(query)
    else:
        response = call_advanced_model(query)

    semantic_cache.append({
        "query": query,
        "embedding": embedding_model.encode(query),
        "model": selected_model,
        "response": response
    })

    return {
        "source": selected_model,
        "similarity": None,
        "response": response
    }

Ici, les appels LLM sont simulés. Dans un vrai projet, je branche ensuite ça sur des modèles locaux, gratuits ou à poids ouverts, ou sur des APIs payantes selon les contraintes. Chez un client, on avait commencé exactement comme ça. Rien de sexy, mais on a vite vu quelles requêtes partaient trop souvent vers le gros modèle.

Le point à surveiller, c’est les faux positifs du cache sémantique. Deux demandes peuvent se ressembler sans vouloir dire la même chose. Je journalise aussi les décisions de routage, sinon on pilote à l’aveugle. Et je mesure trois choses en continu : latence, coût, qualité.

ParamètrePourquoi je le suis
Seuil de similaritéÉvite de réutiliser une mauvaise réponse
Taux de hit cacheMesure les réponses économisées
Coût moyen par requêteMontre le gain réel
LatenceVérifie que le système reste rapide
Taux d’escalade vers modèle avancéRepère les tâches trop souvent routées cher

Et si vos agents lisaient enfin seulement ce qui compte ?

Réduire les tokens dans une IA multi-agents, ce n’est pas rogner sur l’intelligence du système. C’est éviter le gaspillage. Je garde les instructions stables en cache, je réutilise les réponses quand l’intention est déjà connue, je charge les outils seulement au bon moment, et je route chaque tâche vers le modèle adapté. C’est souvent là que les gains arrivent vite, sans tout reconstruire. Le point clé, c’est de mesurer coût, latence et qualité ensemble. Vous gardez des agents utiles, plus rapides, plus prévisibles, et surtout moins chers à faire tourner.

FAQ

  • Pourquoi une architecture multi-agents consomme autant de tokens ?
    Parce que chaque agent reçoit souvent beaucoup de contexte avant même de produire une réponse : instructions système, mémoire, historique, descriptions d’outils, résultats d’autres agents. Quand ces blocs sont répétés à chaque appel, la consommation grimpe vite.
  • Le cache sémantique peut-il remplacer un appel LLM ?
    Oui, quand l’intention est très proche d’une demande déjà traitée. Avec des embeddings et un seuil de similarité, on peut retrouver une réponse existante et éviter un nouvel appel. Il faut quand même surveiller les faux positifs.
  • Quelle différence entre cache statique et cache sémantique ?
    Le cache statique évite de renvoyer des instructions longues et inchangées. Le cache sémantique sert plutôt à reconnaître une demande déjà résolue, même si elle est formulée autrement. Les deux réduisent les tokens, mais pas au même endroit.
  • Pourquoi ne pas donner tous les outils à l’agent dès le départ ?
    Parce que ça surcharge le contexte et ajoute du bruit. Un agent n’a pas besoin de lire toute la documentation API si la tâche ne concerne qu’un seul outil. Le chargement tardif permet de fournir les détails uniquement quand ils sont utiles.
  • Comment choisir entre un modèle léger et un modèle avancé ?
    Je route les tâches simples vers un modèle léger : classification, formatage, résumé court, extraction. Je garde les modèles avancés pour les tâches complexes, ambiguës ou multi-étapes. Le bon arbitrage dépend du coût, de la latence et de la qualité attendue.

 

 

A propos de l’auteur

Je suis Franck Scandolera, responsable de l’agence webAnalyste et de l’organisme Formations Analytics. J’accompagne des entreprises sur le tracking avancé server-side, l’Analytics Engineering, l’automatisation No/Low Code avec n8n, l’intégration de l’IA dans les process business et le SEO/GEO. J’ai travaillé avec des équipes comme Logis Hôtel, Yelloh Village, BazarChic, la Fédération Française de Football ou Texdecor. Si vous voulez mettre en place des agents IA utiles, mesurables et maîtrisés côté coûts, contactez-moi.

Retour en haut
Formations Analytics