Aller au contenu principal
Observation masking : facture Claude -7% en 2 lignes
Retour au blog
IA

Observation masking : facture Claude -7% en 2 lignes

Patrice Huetz11 avril 20266 min

JetBrains a publié début 2026 une technique d'optimisation des agents LLM qui m'a bluffé par sa simplicité : masquer les observations anciennes dans le contexte de l'agent. Au lieu de les envoyer comme des blocs de texte normaux, on les remplace par des tokens spéciaux qui indiquent à Claude « ce bloc a été là mais tu n'as plus besoin de le lire ». Résultat sur mon agent perso : -7% de facture sur 1 semaine, zéro impact sur la qualité. 2 lignes de code à ajouter. Voici exactement quoi, où, pourquoi.

Le problème que ça résout

Dans un agent qui fait 20-30 tool calls, chaque appel produit une observation (le résultat du tool) qui est injectée dans le contexte pour les appels suivants. Au tour 20, le contexte contient 20 observations anciennes dont 18 n'ont plus d'intérêt — elles ont été utilisées une fois et ne servent plus qu'à « prendre de la place ».

Ces 18 observations anciennes coûtent en tokens (et donc en $). Sur un agent long, elles représentent 60-80% du contexte total.

Observation masking : avant/après
Observation masking : avant/après

L'idée de JetBrains : on garde les observations en mémoire structurée (dict, JSON) mais on ne les envoie plus au LLM après le tour N. À la place, on envoie un placeholder type [OBSERVATION #7 MASKED: 1 234 tokens] qui coûte 10 tokens au lieu de 1 234.

Le fix en 2 lignes

Voici le patch minimal. Avant, mon loop d'agent faisait ça :

python
messages.append({
    "role": "tool_result",
    "content": tool_output,  # souvent 500-2000 tokens
})

Après le patch :

python
messages.append({
    "role": "tool_result",
    "content": tool_output if (len(messages) - msg_idx) < 5 else f"[MASKED: {tool_name} @ tour {idx}]",
})

Traduction : si la tool_result a plus de 5 tours d'écart avec le tour courant, on la remplace par un placeholder court. 5 est un chiffre empirique — ça préserve les 5 derniers tool_results en entier et masque tout ce qui est plus ancien.

Les mesures sur 1 semaine de mon agent perso

Mon agent, c'est un Code Buddy qui tourne toute la journée sur mon projet patricehuetz.fr. Typiquement 30-60 tours par session, plusieurs sessions par jour.

MétriqueSans maskingAvec maskingDelta
Tokens moyens / requête48 40045 100-7%
Coût moyen / jour12,40 $11,50 $-7,3%
Latence p501 820 ms1 790 ms-1,6%
Taux de succès sur 40 tâches test92,5%92,5%0%
Taux de réponses hallucinatoires3,2%3,5%+0,3 pts

-7% de facture pour 0% d'impact qualité. Ratio imbattable. La légère hausse des hallucinations (+0,3 pts) est dans le bruit statistique — je n'ai pas vu de différence qualitative en review.

Pourquoi ça marche (et pourquoi ça ne casse pas)

L'intuition naïve : « si tu enlèves de l'info, l'agent doit devenir moins bon ». Ce n'est pas ce qui se passe. Pour 3 raisons :

  1. 1.Les observations anciennes sont rarement réutilisées. Une fois qu'un tool a répondu et que l'agent a agi dessus, il ne revient quasi jamais à cette observation 15 tours plus tard. Le placeholder informe que l'observation a existé (important pour le fil narratif), sans payer le prix de son contenu.
  1. 1.Le placeholder fait toujours partie du contexte. L'agent peut s'y référer (« j'ai lu X au tour 7 »), mais ne relit pas le détail. Si pour une raison rare il a besoin du détail, il peut rappeler le tool.
  1. 1.Les 5 derniers tool_results restent entiers. C'est eux qui comptent pour la cohérence immédiate. Masquer ne touche que l'ancien lointain.

Les 2 pièges à connaître

Piège 1 : les observations structurées

Si ton agent se base sur des observations structurées (JSON, CSV) qu'il référence numériquement (« dans le résultat de list_files au tour 4, le fichier #3 était... »), masquer casse cette référence. Pour ce type de workflow, garde les observations structurées et masque uniquement les observations textuelles libres.

Solution : un flag par type de tool.

python
MASKABLE_TOOLS = {"read_file", "grep", "search_web", "bash"}
UNMASKABLE_TOOLS = {"list_files", "parse_json", "query_db"}

if tool_name in MASKABLE_TOOLS and (len(messages) - msg_idx) >= 5:
    content = f"[MASKED: {tool_name}]"
else:
    content = tool_output

Piège 2 : les tâches d'audit rétroactif

Si tu demandes à l'agent « résume tout ce que tu as fait cette session » à la fin, un agent avec observations masquées répondra de manière incomplète. Solution : désactive le masking pendant les tâches d'audit.

⚠️
Si ton agent fait souvent du résumé rétrospectif ou des références numériques à des résultats anciens, teste avec et sans masking avant de déployer. Le gain de 7% ne vaut pas la perte de fiabilité.

Tuning du seuil

J'ai testé plusieurs valeurs du seuil (combien de tours avant masking) :

SeuilTokens économisésTaux succès
3-12%87% (dégradation)
5-7%92,5% (stable)
10-4%92,5%
15-2%92,5%

5 est le sweet spot : économie maximale sans impact qualité. Sous 5, la qualité baisse. Au-delà de 5, les gains marginaux ne justifient pas la complexité.

💡
Démarre à un seuil de 5 tours, mesure sur ton workload spécifique, ajuste vers 3 ou 10 selon le type de tâches que ton agent traite.

Ce qu'il faut retenir

  1. 1.L'observation masking économise 7% de facture sur mes workloads, sans impact qualité.
  2. 2.2 lignes de code suffisent pour l'implémentation de base.
  3. 3.Piège : ne masque pas les tools structurés que l'agent référence numériquement.
  4. 4.Seuil optimal : 5 tours avant masking — mesuré sur 40 tâches test.
  5. 5.Le gain monte à 15-20% sur les agents très longs (100+ tours).

Pour aller plus loin sur les mécanismes de gestion du contexte dans les LLM et les techniques avancées de compression, j'ai écrit un livre complet :

La Mémoire des Machines
La Mémoire des Machines

Du KV-Cache au Context Engineering.

Découvrir →
🔒

Soutenez mon travail sur Patreon

Accès anticipé aux articles, contenu exclusif, et la satisfaction de soutenir un auteur indépendant.

Rejoindre — à partir de 3€/mois

Commentaires

Chargement des commentaires...

Laisser un commentaire