Construire un Context Engine comme Code Buddy — l'architecture complète
Tu as probablement déjà utilisé un agent de code — Claude Code, Cursor, Copilot. Et tu as probablement remarqué que certains sont meilleurs que d'autres pour « se souvenir » du contexte. Pas juste le fichier ouvert, mais l'architecture du projet, les conventions d'équipe, les erreurs déjà corrigées, les décisions de design prises il y a 20 minutes.
Ce n'est pas de la magie. C'est un Context Engine — un système qui orchestre ce que l'agent voit, dans quel ordre, avec quelle priorité, et surtout ce qu'il ne voit pas. J'ai décortiqué l'architecture de Code Buddy, un agent de code open-source dont le context engine est particulièrement bien conçu. Voici tout ce que j'ai appris.
Le problème : la fenêtre de contexte n'est pas illimitée
Même avec des modèles à 200K ou 1M tokens de contexte, tu ne peux pas tout y mettre. Un projet TypeScript de taille moyenne contient 500 000 tokens de code source. Ajoute la conversation, les résultats d'outils, les fichiers de configuration, les messages d'erreur — tu dépasses le million en quelques minutes.
Le context engine résout un problème d'optimisation sous contrainte : maximiser la qualité des réponses de l'agent dans un budget de tokens fixe. C'est un problème de sac à dos, mais avec des interactions complexes entre les éléments (un fichier de test a plus de valeur s'il est lu en même temps que le code qu'il teste).
Les 7 hooks du cycle de vie
L'architecture de Code Buddy repose sur un système de hooks — des points d'injection dans le cycle de vie de l'agent. Chaque hook est une fonction qui peut lire et modifier le contexte avant qu'il soit envoyé au modèle.
1. bootstrap
Exécuté une seule fois au démarrage de la session. C'est ici que le context engine :
- •Charge le fichier CLAUDE.md (ou équivalent) du projet
- •Scanne la structure du répertoire (arborescence, mais pas le contenu des fichiers)
- •Identifie le langage principal, le framework, le système de build
- •Charge la mémoire persistante (sessions précédentes)
Le bootstrap est critique parce qu'il définit le « monde » dans lequel l'agent opère. Un mauvais bootstrap — qui charge trop ou pas assez — dégrade toute la session.
2. ingest
Appelé chaque fois qu'un nouveau contenu entre dans le système : fichier lu, résultat de commande, réponse d'API. Le hook ingest :
- •Tokenise le contenu entrant
- •Calcule son score d'importance (voir section suivante)
- •Décide s'il entre dans le contexte immédiatement ou va en « réserve »
- •Tronque si nécessaire (début, fin, ou milieu du contenu)
3. assemble
Le cœur du système. Appelé juste avant chaque appel au LLM. Le hook assemble compose le prompt final en respectant les budgets de tokens par catégorie. C'est lui qui décide ce qui entre et ce qui reste dehors.
4. compact
Déclenché quand le contexte dépasse un seuil critique (typiquement 80 % du budget). Le hook compact applique les stratégies de compression : résumé, éviction, réduction de précision. Il travaille en arrière-plan pour libérer de l'espace avant que l'agent ne se retrouve bloqué.
5. afterTurn
Exécuté après chaque échange complet (question utilisateur + réponse agent). C'est ici que le système :
- •Met à jour les scores d'importance (un fichier mentionné dans la réponse voit son score augmenter)
- •Persiste les observations clés en mémoire
- •Vérifie si un compactage proactif est nécessaire
6. prepareSubagentSpawn
Quand l'agent principal lance un sous-agent (pour une tâche parallèle), ce hook prépare le contexte minimal nécessaire. Le sous-agent ne reçoit pas tout le contexte du parent — seulement ce qui est pertinent pour sa tâche spécifique.
7. onSubagentEnded
Quand un sous-agent termine, ce hook intègre ses résultats dans le contexte principal. Il filtre le bruit (logs de debug, tentatives ratées) et ne garde que l'essentiel : le résultat final, les fichiers modifiés, les observations critiques.
Token budgets : la répartition
Le context engine de Code Buddy divise la fenêtre de contexte en six catégories avec des budgets fixes. Voici la répartition pour un modèle à 200K tokens :
| Catégorie | Budget | Tokens | Contenu |
|---|---|---|---|
| System | 8 % | 16 000 | Identité de l'agent, règles, capabilities |
| Instructions | 3 % | 6 000 | CLAUDE.md, conventions projet |
| Code | 18 % | 36 000 | Fichiers source, dépendances |
| Tools | 13 % | 26 000 | Résultats de commandes, logs, recherches |
| Conversation | 53 % | 106 000 | Historique des échanges |
| Memory | 4 % | 8 000 | Mémoire persistante cross-session |
La catégorie « Conversation » domine à 53 %. C'est contre-intuitif — on pourrait penser que le code est plus important. Mais l'historique de conversation est le fil conducteur de la session. Sans lui, l'agent perd le contexte des décisions, répète des erreurs, et ne comprend plus pourquoi il fait ce qu'il fait.
Le budget « System » à 8 % semble généreux pour des instructions statiques. Mais les agents modernes ont des prompts système complexes : liste des outils disponibles, format de réponse attendu, règles de sécurité, exemples de bonne utilisation. 16 000 tokens, c'est vite consommé.
Scoring d'importance : tout n'est pas égal
Chaque élément du contexte reçoit un score d'importance entre 0,0 et 1,0. Ce score détermine l'ordre de priorité quand le compactage s'active.
Voici les scores par défaut de Code Buddy :
| Type de contenu | Score | Justification |
|---|---|---|
| System prompt | 1,00 | Jamais compressé, jamais évincé |
| Erreurs et stack traces | 0,95 | Critiques pour le debugging |
| Instructions utilisateur | 0,90 | La demande actuelle |
| Code modifié récemment | 0,85 | Fichiers sur lesquels l'agent travaille |
| Résultats d'outils récents | 0,80 | Données fraîches |
| Code source (contexte) | 0,70 | Fichiers lus mais non modifiés |
| Mémoire persistante | 0,60 | Utile mais pas urgente |
| Conversation ancienne | 0,25 | Compressible sans grande perte |
| Résultats d'outils anciens | 0,15 | Premiers à être évincés |
Le scoring dynamique est ce qui distingue un bon context engine d'un système de troncation basique. Tronquer les 50 premiers messages de la conversation est brutal et stupide — le message 3 contient peut-être l'architecture du projet. Un scoring intelligent sait que ce message est plus important que le message 47 qui dit « ok, continue ».
Les 6 stratégies de compression
Quand le contexte dépasse le budget, le context engine active ses stratégies de compression par ordre de préférence.
1. Importance-weighted eviction
La plus simple : évincer les éléments avec le score d'importance le plus bas. Les résultats d'outils anciens partent en premier, puis les messages de conversation anciens. C'est une solution efficace mais grossière — elle perd de l'information de manière irréversible.
2. Observation masking
Les résultats d'outils sont souvent verbeux. Un ls -la sur un répertoire de 200 fichiers produit 200 lignes, mais l'agent n'a besoin que des 10 fichiers pertinents. L'observation masking filtre les résultats d'outils en ne gardant que les lignes qui matchent des patterns pertinents (noms de fichiers mentionnés, messages d'erreur, valeurs numériques).
3. Restorable compression
Au lieu d'évincer un message, on le remplace par un résumé + un pointeur. Le résumé dit « ce message contenait les résultats du grep sur auth.ts — 3 occurrences trouvées ». Si l'agent a besoin du détail plus tard, il peut « restaurer » le message original via le pointeur.
4. JIT (Just-In-Time) discovery
Au lieu de charger tout le code source au bootstrap, le context engine attend que l'agent en ait besoin. Quand l'agent mentionne un fichier ou une fonction, le système le charge à la demande. C'est du lazy loading appliqué au contexte.
5. Partial summarization
Les longs blocs de conversation sont résumés partiellement : le premier et le dernier message sont conservés intacts, les messages intermédiaires sont résumés par le LLM lui-même. Le coût est un appel LLM supplémentaire, mais le gain en tokens est considérable.
6. Proactive compaction
Le système n'attend pas que le budget soit atteint. Après chaque tour, le hook afterTurn évalue la « pression mémoire » et déclenche un compactage léger si nécessaire. C'est comme le garbage collector d'un langage — mieux vaut des petites collections fréquentes qu'une grosse collection qui bloque tout.
Les 9 types de mémoire
Le budget « Memory » (4 %, 8 000 tokens) est divisé en neuf types de mémoire persistante :
- 1.Épisodique — Ce qui s'est passé dans les sessions précédentes (« hier, on a refactoré le module auth »)
- 2.Sémantique — Connaissances sur le projet (« ce projet utilise Prisma avec PostgreSQL »)
- 3.Procédurale — Comment faire les choses (« pour déployer, lance npm run deploy:prod »)
- 4.Prospective — Ce qui reste à faire (« TODO : ajouter les tests pour le module payment »)
- 5.Spatiale — La structure du projet (arborescence, relations entre modules)
- 6.Émotionnelle — Les préférences de l'utilisateur (« il préfère les fonctions pures, déteste les classes »)
- 7.Déclarative — Les règles explicites du CLAUDE.md
- 8.Conversationnelle — Résumé des conversations passées pertinentes
- 9.Méta-cognitive — Ce que l'agent sait de ses propres limitations (« je n'ai pas accès à la base de données directement »)
Exemple concret : une session de debugging
Pour voir le context engine en action, imaginons une session de debugging typique.
Tour 1 — L'utilisateur dit « Le test auth.test.ts échoue ». Le hook ingest charge le fichier test, calcule un score de 0,85 (code + erreur). Le hook assemble place le fichier dans la catégorie Code.
Tour 2 — L'agent lit auth.ts (le code testé). Score 0,85. Le context engine a maintenant le test ET le code. Il détecte la relation et boost le score des deux à 0,90.
Tour 3 — L'agent lance le test. Le résultat (stack trace) entre avec un score de 0,95 (erreur). Le hook assemble le place en haut de la catégorie Tools.
Tour 4 — L'agent identifie le bug et propose un fix. Le message de conversation contenant le fix reçoit un score de 0,85.
Tour 5 — L'utilisateur dit « ok, applique le fix ». Score 0,90 (instruction). L'agent modifie auth.ts. Le hook afterTurn met à jour la mémoire procédurale : « le module auth avait un bug dans la validation JWT — corrigé ».
À ce stade, le contexte contient ~25 000 tokens. Pas de compactage nécessaire. Mais si la session continue pendant 30 minutes de plus, les premiers tours seront progressivement compressés — leurs scores d'importance décroissent avec le temps.
Construire le tien : par où commencer
Si tu veux implémenter un context engine pour ton propre agent, voici l'ordre que je recommande :
- 1.Commence par les budgets fixes — Définis tes catégories et pourcentages. Ne les rends pas dynamiques au début.
- 2.Implémente le scoring statique — Assigne des scores fixes par type de contenu. Ça suffit pour 80 % des cas.
- 3.Ajoute la troncation simple — Quand le budget est dépassé, tronque les éléments les moins importants.
- 4.Puis itère — Scoring dynamique, restorable compression, mémoire persistante. Chaque couche améliore la qualité mais ajoute de la complexité.
Le piège classique, c'est de vouloir tout implémenter d'un coup. Un context engine avec des budgets fixes et un scoring statique bat déjà 90 % des agents qui font du « tout dans le prompt, on verra bien ».
Pour creuser le sujet de la mémoire des LLM — du KV-cache matériel jusqu'aux systèmes de mémoire épisodique des agents —, j'ai écrit un guide complet qui couvre toute la chaîne.
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