Context limit reached : comment j'ai audité 20 000 appels pour sauver mes sous-agents Claude Code

Débat audio sur l'article:

**Source :** DRAFT-016-crash-context-limit-audit-hooks.md **Speakers :** Mathieu (homme), Vivienne (femme) **Duration :** ~5 minutes **Date :** 2026-02-17 --- [Vivienne] Context limit reached. Trois mots que personne ne veut voir. Mathieu, raconte-nous ce qui s'est passé. [Mathieu] C'est arrivé sur un fichier PHP. Un fichier de 2 500 lignes appelé DoctrineEntityManipulator. Mon sous-agent devait modifier trois lignes autour de la ligne 2 590. Il a lu le fichier en entier, et la fenêtre de contexte a explosé. Session perdue. [Vivienne] Et c'est là que tu as décidé d'auditer ? [Mathieu] Pas tout de suite. J'ai d'abord fait ce que tout le monde fait : relancer la session et continuer. Mais le crash s'est reproduit. Alors j'ai lancé un audit complet. 20 000 appels d'outils, 170 sessions, 11,5 millions de tokens passés au microscope. [Vivienne] Et le résultat ? [Mathieu] Le score initial était de 5,6 sur 10. Tout était cassé. Le suivi des tokens affichait zéro ligne. Le logging d'exécution fonctionnait à 0,13 %. Et le ratio entre les lectures de fichiers et les recherches était de 7 pour 1. Pour chaque recherche, mes agents faisaient sept lectures complètes de fichiers. [Vivienne] Sept lectures pour une recherche ? Comment c'est possible ? [Mathieu] Parce que personne ne leur avait dit de chercher avant de lire. Ils ouvraient des fichiers entiers sans vérifier d'abord si l'information s'y trouvait. Sur la pire session, le ratio montait à 73 pour 1. [Vivienne] Et ce n'était pas le seul problème. [Mathieu] Non. L'audit a révélé quelque chose de bien pire. Mes propres hooks, les automatisations que j'avais installées pour améliorer la qualité du code, étaient les premiers coupables. [Vivienne] Comment ça ? [Mathieu] J'avais quatre hooks qui se déclenchaient à chaque édition de fichier. Un pour la revue de code, un pour les conventions de nommage, un pour traduire les commentaires, et un pour générer de la documentation. Les quatre appelaient un modèle local en parallèle. Chaque simple modification de fichier déclenchait cinq processus en arrière-plan. [Vivienne] Cinq, pas quatre ? [Mathieu] Cinq, parce qu'il y avait aussi une vérification TypeScript automatique en plus des quatre hooks. Le résultat : saturation du processeur, latence sur chaque opération, et surtout une pression supplémentaire sur le contexte quand les résultats revenaient dans la conversation. [Vivienne] Et les lectures répétées ? [Mathieu] Un fichier Svelte appelé ConfigBuilder a été lu 284 fois au total. 191 000 tokens pour relire le même fichier. Aucun mécanisme de cache, aucun avertissement de relecture. Et les commandes Bash non filtrées ont consommé 285 000 tokens à elles seules. [Vivienne] D'accord. Donc le problème est identifié. Comment tu l'as corrigé ? [Mathieu] Par une méthode itérative. Diagnostiquer, corriger, remesurer. 9 cycles en 48 heures. Le premier saut a été spectaculaire : de 5,6 à 8,0 en une seule itération, grâce au backfill des métriques et à la catégorisation du Bash. [Vivienne] Et ensuite ? [Mathieu] Les versions suivantes ont affiné la détection du gaspillage, puis stabilisé le score à 8,3 sur 10 pendant quatre itérations consécutives. C'est le signal que le plateau est atteint. [Vivienne] Concrètement, qu'est-ce que tu as déployé ? [Mathieu] D'abord, j'ai fusionné les quatre hooks en un seul. Un prompt, un appel, quatre analyses. Résultat : 75 % d'appels en moins par édition. Ensuite, j'ai créé un garde de lecture avec quatre couches de protection pour les gros fichiers. [Vivienne] Quatre couches ? [Mathieu] Oui. Une règle projet qui éduque l'agent, un hook qui bloque les fichiers de plus de 10 kilooctets, un avertissement sur les fichiers de plus de 20 kilooctets, et un budget cumulatif de 15 000 tokens par fichier. Si une couche laisse passer quelque chose, la suivante rattrape. [Vivienne] Et ça change quoi en pratique ? [Mathieu] La modification qui consommait 8 000 tokens par lecture complète n'en consomme plus que 600 avec une recherche ciblée. Soit une réduction de 93 %. [Vivienne] Et les résultats globaux ? [Mathieu] Le taux de gaspillage est passé de 8 % à 2,16 %. Les lectures de gros fichiers sont passées de 33 % à 2,7 % par jour. Les sorties non redirigées sont tombées à zéro sur 502 appels. Et j'ai déployé 6 hooks d'enforcement et 7 vues de monitoring pour suivre tout ça dans le temps. [Vivienne] Zéro sortie non redirigée, c'est impressionnant. [Mathieu] C'est le résultat le plus satisfaisant. Le 17 février a été le premier jour avec un gaspillage de 1 % seulement. Le meilleur score jamais enregistré. [Vivienne] La leçon à retenir ? [Mathieu] Vos outils vous aident, mais ils consomment aussi. Chaque hook, chaque plugin, chaque skill ajoute une couche de traitement invisible. Sans audit, sans mesure, vous ne savez pas combien vous payez pour ces bénéfices. [Vivienne] Et si quelqu'un veut commencer à auditer ? [Mathieu] Il commence par compter ses hooks. Combien se déclenchent sur une édition de fichier ? Combien appellent un modèle ? Combien produisent une sortie qui revient dans le contexte ? La réponse surprend toujours. [Vivienne] Merci Mathieu. L'article complet est disponible sur le blog avec toutes les métriques et les solutions détaillées. [Mathieu] Merci Vivienne. Et n'oubliez pas : le crash n'est jamais le problème. C'est le symptôme.

« Context limit reached. » Trois mots, session perdue. L'éditeur se fige, le sous-agent meurt, et tout le travail en cours disparaît dans le vide.

Ce crash est arrivé pendant une modification banale sur un fichier PHP. Un fichier de 2 500 lignes, un sous-agent qui tente un Edit autour de la ligne 2 590, et la fenêtre de contexte qui explose. Sur le moment, j'ai fait ce que tout le monde fait : j'ai relancé la session et j'ai continué. Mais le crash s'est reproduit. Alors j'ai creusé.

Ce que j'ai découvert m'a surpris. Le problème n'était pas le fichier trop gros. C'étaient mes propres hooks, skills et plugins qui consommaient mes tokens en silence, à chaque appel d'outil, comme une taxe invisible sur chaque action.

Avant de continuer de lire la suite de l'article, je vous invite à vous inscrire à ma newsletter, pour connaître en avant première les futurs sujets traités chaque semaine.



L'incident : un fichier trop gros, un sous-agent trop gourmand

Le fichier s'appelle DoctrineEntityManipulator.php. C'est un monstre : plus de 2 500 lignes, environ 95 Ko. Dans mon framework Symfony, il génère automatiquement les entités Doctrine. Le sous-agent devait ajouter une garde d'hydratation DTO autour de la ligne 2 590.

En pratique, voici ce qui s'est passé dans la fenêtre de contexte :

ÉtapeTokens estimésCumulé
Prompt système + règles globales~8 0008K
CLAUDE.md du projet (chargé automatiquement)~3 00011K
Lecture complète du fichier PHP~8 00019K
Historique de conversation (modifications précédentes)~15 000+34K+
Lectures de fichiers liés (entités, DTOs)~10 000+44K+
Sortie de l'outil Edit (affichage du diff)~2 00046K+

Avec une fenêtre pratique de 80-90K tokens (la limite théorique de 128K moins la réserve pour la sortie et le raisonnement), une session faisant plusieurs éditions sur un fichier de 2 500 lignes atteint la limite en quelques échanges.

Le problème : aucun garde-fou n'existait. Ni règle projet interdisant la lecture complète de ce fichier, ni hook bloquant l'opération, ni budget cumulatif alertant sur la consommation. Le sous-agent lisait le fichier entier pour modifier trois lignes. C'est l'équivalent de photocopier un livre pour corriger une faute de frappe.

Le diagnostic : 20 000 appels sous le microscope

J'ai décidé de ne pas me contenter d'un patch sur ce fichier. J'ai lancé un audit comportemental complet de mes sous-agents. Premier résultat : le score global était de 5,6 sur 10. Tout était cassé.

Ce que l'audit a révélé

DimensionScoreConstat
Santé RAG et embeddings10/10La seule chose qui fonctionnait
Logging d'exécution1/1099,87 % des enregistrements sans métriques
Suivi des tokens0/10Infrastructure morte, zéro ligne
Efficacité de récupération3/10Ratio Read:Grep de 7:1 (cible : 1-2:1)
Utilisation des sous-agents3/100 à 3 appels Task par session
Précision du routing7/10Fonctionnel mais non testable

Le chiffre qui m'a le plus choqué : le ratio Read:Grep. Pour chaque appel Grep (chercher dans un fichier), mes agents faisaient sept lectures complètes. Autrement dit, ils lisaient des fichiers entiers sans jamais chercher d'abord si l'information s'y trouvait. Sur la pire session, le ratio montait à 73:1 — 73 lectures pour un seul Grep.

Les patterns de gaspillage étaient clairs :

Pattern détectéOccurrencesTokens gaspillés
cat au lieu de l'outil Read2812 833
grep en Bash au lieu de l'outil Grep2911 495
find en Bash au lieu de l'outil Glob144 690
Lectures répétées du même fichier5+non estimé
Total immédiat76~29 000

Ces chiffres ne représentaient qu'une journée. Extrapolés sur 170 sessions, le gaspillage cumulé dépassait 500 000 tokens.

Les consommateurs silencieux : hooks, skills et plugins

Voici la partie que personne ne soupçonne. Quand vous installez des hooks PostToolUse dans Claude Code, ils se déclenchent à chaque appel d'outil. Chaque Edit, chaque Write. Le problème, c'est qu'ils s'empilent.

4 hooks Ollama simultanés par édition

J'avais configuré quatre hooks PostToolUse qui appelaient chacun un modèle LLM local via Ollama :

HookFonctionTemps moyen
code-review.tsRevue de code300-900 ms
naming-convention.tsVérification de nommage500 ms
translate-comments.tsTraduction des commentaires FR→EN600-1 400 ms
suggest-docstring.tsSuggestion de documentation1 200-2 600 ms

Les quatre se déclenchaient simultanément sur chaque édition de fichier. Quatre appels concurrents à Ollama sur un modèle 4B, plus un tsc --noEmit en PostToolUse pour la vérification TypeScript. En pratique, cela signifie que chaque simple modification de fichier déclenchait cinq processus parallèles en arrière-plan.

Le résultat : saturation du CPU, latence accrue sur chaque opération, et surtout une pression supplémentaire sur le contexte quand les résultats de ces hooks revenaient dans la conversation.

Les lectures répétées : le gaspillage invisible

L'audit a révélé un autre coupable : les fichiers lus encore et encore au fil des sessions.

FichierLecturesTokens consommés
ConfigBuilder.svelte284191 731
Agenda.svelte172189 001
[pageId]/+page.svelte11990 494
settings.json62116 302
agent-router.ts53

ConfigBuilder.svelte à lui seul a consommé 191K tokens en 284 lectures. C'est presque 200 000 tokens pour relire le même fichier. Aucun mécanisme de cache, aucun avertissement de re-lecture.

Les sorties non redirigées : la bombe à retardement

Dernière catégorie de gaspillage : les commandes Bash dont personne ne filtre la sortie. Un git diff sans --stat, un node script.ts sans redirection vers /tmp/, une requête SQL sans LIMIT.

68 appels de ce type ont consommé 285 000 tokens à eux seuls, soit une moyenne de 4 155 tokens par appel. Un seul appel non redirigé gaspille autant que 20 appels grep en Bash au lieu de l'outil Grep.

9 itérations en 48 heures : l'audit comme boucle d'amélioration

L'audit ne s'est pas arrêté au diagnostic. J'ai appliqué une méthode itérative : diagnostiquer, corriger, re-mesurer. Neuf cycles en deux jours.

La progression

PhaseVersionsFocusScore
Fondationsv1-v2Backfill des métriques, catégorisation Bash, Read token estimation5,6 → 8,0
Affinagev3-v5Waste tracking, R:G ratio enforcement, unbounded output detection8,0 → 8,3
Stabilisationv6-v9False positive removal, weekly monitoring, defense in depth8,3 stable

Le saut le plus spectaculaire est entre v1 et v2 : de 5,6 à 8,0 en une seule itération. C'est là que les fixes critiques ont été déployés — le backfill des métriques d'exécution (596 segments mis à jour sur 53 sessions), les gardes de lecture, et la catégorisation du Bash (de 10 catégories à 19, puis 32).

Les versions v6 à v9 ont toutes produit le même score : 8,3/10. Quatre itérations consécutives au même niveau. C'est le signal qu'on a atteint un plateau : les améliorations restantes sont structurelles et ne peuvent pas être résolues par des hooks.

L'infrastructure déployée

En sortie de cet audit, voici ce qui a été créé :

6 hooks d'enforcement :

  • read-guard.ts — budget cumulatif par fichier, détection de re-lecture, blocage des gros fichiers
  • command-validator — détection des sorties non redirigées (git diff, node, SQL)
  • Docker exec hook — avertissement sur les requêtes SQL sans LIMIT
  • Zero-Grep escalation — alerte quand un agent lit 10+ fichiers sans un seul Grep
  • Session cache — détection des lectures répétées intra-session
  • Grep hint — rappel de faire un Grep avant un Read

7 vues de monitoring :

  • v_rag_health, v_token_waste_daily, v_token_waste_weekly, v_session_efficiency, v_big_read_daily, v_agent_routing, v_agent_usage_summary

1 table de suivi des déploiements :

  • rag_hook_deployments — 12 entrées traçant chaque fix de V5 à V8

Les solutions : defense in depth

L'approche qui a le mieux fonctionné est la défense en profondeur : plusieurs couches de protection, chacune rattrapant ce que la précédente laisse passer.

Consolider les hooks : 4 → 1

La première action a été de fusionner les quatre hooks Ollama en un seul code-analysis.ts. Un prompt, un appel, quatre analyses. Résultat : 75 % d'appels Ollama en moins par édition, plus de saturation CPU, et un verrou de concurrence qui empêche les appels parallèles.

Le read guard : 4 couches de protection

Pour les gros fichiers comme DoctrineEntityManipulator.php, j'ai implémenté quatre couches :

CoucheMécanismeType
1Règle projet (large-files.md)Éduque Claude (soft)
2Phase 0 du read guard (fichiers >10 Ko)Bloque l'action (hard)
3Warning sur tous les fichiers >20 KoAvertit (existant)
4Budget cumulatif de 15K tokens/fichierEscalade progressive

Résultat concret : une modification qui consommait 8 000 tokens (lecture complète) n'en consomme plus que 600 (Grep + Read ciblé). Soit une réduction de 93 %.

Deny au lieu de ask

Pour les patterns de gaspillage les plus flagrants (cat au lieu de Read, grep en Bash au lieu de l'outil Grep, find au lieu de Glob), j'ai durci les hooks de « ask » (demander confirmation) à « deny » (refuser directement). En une session, 9 commandes ont été refusées automatiquement — 9 gaspillages évités sans intervention humaine.

Les résultats

Après 48 heures d'audit et d'implémentation, voici les métriques :

MétriqueAvantAprèsAmélioration
Score global5,6/108,3/10+48 %
Taux de gaspillage (appels)~8 %2,16 %-73 %
Catégories Bash « other »1 662 appels146 appels-91 %
Catégories de classification1032+220 %
Big reads (>5K tokens) par jour18-33 %2,7 %-85 à -92 %
Sorties non redirigées (par jour)77 appels0-100 %
Hooks d'enforcement06De rien à tout
Vues de monitoring07Observabilité complète

Le 17 février, jour de clôture de l'audit, a été le premier jour avec zéro sortie non redirigée sur 502 appels. Le gaspillage token du jour était à 1,0 % — le meilleur score jamais enregistré.

Conclusion

Le crash « context limit reached » n'était pas le problème. C'était le symptôme d'un écosystème mal surveillé. Mes hooks, installés pour améliorer la qualité du code, consommaient des ressources à chaque action sans que je m'en rende compte. Mes agents lisaient des fichiers entiers sans chercher d'abord. Mes commandes Bash déversaient des milliers de tokens non filtrés dans le contexte.

L'erreur courante est de traiter chaque crash individuellement. Relancer la session, éviter le gros fichier, passer à autre chose. En pratique, cela signifie ignorer un problème systémique qui se reproduira.

La leçon que j'en tire tient en une phrase : vos outils vous aident, mais ils consomment aussi. Chaque hook, chaque plugin, chaque skill ajoute une couche de traitement invisible. Sans audit, sans mesure, sans monitoring, vous ne savez pas combien vous payez pour ces bénéfices.

Commencez par une action simple : comptez vos hooks PostToolUse. Combien se déclenchent sur un Edit ? Combien appellent un LLM ? Combien produisent une sortie qui revient dans le contexte ? La réponse vous surprendra probablement autant qu'elle m'a surpris.

Qui suis je ?

Je suis Mathieu GRENIER, CTO d'Easystrat une startup de Montpellier, en France. Je manage une équipe d'une dizaine d'ingénieurs (Graphistes, IA, frontend, backend, devOps, AWS) en remote depuis le Japon.

J'ai aussi mon activité de freelance, où je conseille des entrepreneurs dans leurs projets d'application.

Avec mon expérience personnelle de plus de 15 ans en ESN, j'ai pu travailler pour un large panel d'entreprises de différentes tailles. Ma compréhension des problèmes métiers est une de mes grandes forces et permet à mes clients de pouvoir se projeter plus facilement.

L'essentiel de mon travail consiste à canaliser l'énergie des entrepreneurs sur l'essence même de leur projet.

La technologie, les méthodes, le management sont le coeur de mes compétences.

Vous pouvez me faire confiance sur ces points là.

Si vous voulez me parler d'un de vos projets, n'hésitez pas à m'envoyer un email avec vos disponibilités à : contact@mathieugrenier.fr


Tous les articles de ce blog sont écrits par moi, même si je peux m'aider de l'IA pour illustrer mes propos. Mais jamais je ne fournis d'articles 100% IA.

Mathieu Grenier 17 février 2026
Partager cet articlE