« 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 :
| Étape | Tokens estimés | Cumulé |
|---|---|---|
| Prompt système + règles globales | ~8 000 | 8K |
| CLAUDE.md du projet (chargé automatiquement) | ~3 000 | 11K |
| Lecture complète du fichier PHP | ~8 000 | 19K |
| 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 000 | 46K+ |
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é
| Dimension | Score | Constat |
|---|---|---|
| Santé RAG et embeddings | 10/10 | La seule chose qui fonctionnait |
| Logging d'exécution | 1/10 | 99,87 % des enregistrements sans métriques |
| Suivi des tokens | 0/10 | Infrastructure morte, zéro ligne |
| Efficacité de récupération | 3/10 | Ratio Read:Grep de 7:1 (cible : 1-2:1) |
| Utilisation des sous-agents | 3/10 | 0 à 3 appels Task par session |
| Précision du routing | 7/10 | Fonctionnel 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é | Occurrences | Tokens gaspillés |
|---|---|---|
| cat au lieu de l'outil Read | 28 | 12 833 |
| grep en Bash au lieu de l'outil Grep | 29 | 11 495 |
| find en Bash au lieu de l'outil Glob | 14 | 4 690 |
| Lectures répétées du même fichier | 5+ | non estimé |
| Total immédiat | 76 | ~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 :
| Hook | Fonction | Temps moyen |
|---|---|---|
| code-review.ts | Revue de code | 300-900 ms |
| naming-convention.ts | Vérification de nommage | 500 ms |
| translate-comments.ts | Traduction des commentaires FR→EN | 600-1 400 ms |
| suggest-docstring.ts | Suggestion de documentation | 1 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.
| Fichier | Lectures | Tokens consommés |
|---|---|---|
| ConfigBuilder.svelte | 284 | 191 731 |
| Agenda.svelte | 172 | 189 001 |
| [pageId]/+page.svelte | 119 | 90 494 |
| settings.json | 62 | 116 302 |
| agent-router.ts | 53 | — |
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
| Phase | Versions | Focus | Score |
|---|---|---|---|
| Fondations | v1-v2 | Backfill des métriques, catégorisation Bash, Read token estimation | 5,6 → 8,0 |
| Affinage | v3-v5 | Waste tracking, R:G ratio enforcement, unbounded output detection | 8,0 → 8,3 |
| Stabilisation | v6-v9 | False positive removal, weekly monitoring, defense in depth | 8,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 :
| Couche | Mécanisme | Type |
|---|---|---|
| 1 | Règle projet (large-files.md) | Éduque Claude (soft) |
| 2 | Phase 0 du read guard (fichiers >10 Ko) | Bloque l'action (hard) |
| 3 | Warning sur tous les fichiers >20 Ko | Avertit (existant) |
| 4 | Budget cumulatif de 15K tokens/fichier | Escalade 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étrique | Avant | Après | Amélioration |
|---|---|---|---|
| Score global | 5,6/10 | 8,3/10 | +48 % |
| Taux de gaspillage (appels) | ~8 % | 2,16 % | -73 % |
| Catégories Bash « other » | 1 662 appels | 146 appels | -91 % |
| Catégories de classification | 10 | 32 | +220 % |
| Big reads (>5K tokens) par jour | 18-33 % | 2,7 % | -85 à -92 % |
| Sorties non redirigées (par jour) | 77 appels | 0 | -100 % |
| Hooks d'enforcement | 0 | 6 | De rien à tout |
| Vues de monitoring | 0 | 7 | Observabilité 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.
Context limit reached : comment j'ai audité 20 000 appels pour sauver mes sous-agents Claude Code