Aller au contenu principal
Construire un Context Engine comme Code Buddy — l'architecture complète
Retour au blog
IA

Construire un Context Engine comme Code Buddy — l'architecture complète

Patrice Huetz6 avril 202612 min

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).

ℹ️
Le context engine n'est pas le RAG. Le RAG récupère des documents pertinents. Le context engine orchestre TOUT ce qui entre dans le prompt : instructions système, code source, résultats d'outils, historique de conversation, mémoire persistante. Le RAG est un composant du context engine, pas son équivalent.

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.

💡
Le hook assemble est le seul qui a une vue globale sur tout le contexte disponible. C'est là que les trade-offs se font. Si tu dois débugger un context engine, c'est ici qu'il faut regarder en premier.

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égorieBudgetTokensContenu
System8 %16 000Identité de l'agent, règles, capabilities
Instructions3 %6 000CLAUDE.md, conventions projet
Code18 %36 000Fichiers source, dépendances
Tools13 %26 000Résultats de commandes, logs, recherches
Conversation53 %106 000Historique des échanges
Memory4 %8 000Mémoire persistante cross-session
ℹ️
Les pourcentages sont des plafonds, pas des garanties. Si la conversation est courte, les tokens inutilisés ne sont pas redistribués — ils restent comme marge de sécurité. Mieux vaut un prompt de 150K tokens avec de la marge qu'un prompt de 199K tokens qui force un compactage d'urgence au tour suivant.

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 contenuScoreJustification
System prompt1,00Jamais compressé, jamais évincé
Erreurs et stack traces0,95Critiques pour le debugging
Instructions utilisateur0,90La demande actuelle
Code modifié récemment0,85Fichiers sur lesquels l'agent travaille
Résultats d'outils récents0,80Données fraîches
Code source (contexte)0,70Fichiers lus mais non modifiés
Mémoire persistante0,60Utile mais pas urgente
Conversation ancienne0,25Compressible sans grande perte
Résultats d'outils anciens0,15Premiers à être évincés
⚠️
Le score d'importance n'est pas statique. Un fichier de code à 0,70 passe à 0,85 s'il est mentionné dans une erreur. Un message de conversation à 0,25 monte à 0,90 s'il contient une instruction « ne fais jamais X ». Le scoring est dynamique et contextuel.

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.

💡
La restorable compression est l'arme secrète des bons context engines. Elle permet de compresser agressivement sans perte irréversible. L'agent peut toujours récupérer l'information complète si nécessaire — au prix d'un appel d'outil supplémentaire.

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. 1.Épisodique — Ce qui s'est passé dans les sessions précédentes (« hier, on a refactoré le module auth »)
  2. 2.Sémantique — Connaissances sur le projet (« ce projet utilise Prisma avec PostgreSQL »)
  3. 3.Procédurale — Comment faire les choses (« pour déployer, lance npm run deploy:prod »)
  4. 4.Prospective — Ce qui reste à faire (« TODO : ajouter les tests pour le module payment »)
  5. 5.Spatiale — La structure du projet (arborescence, relations entre modules)
  6. 6.Émotionnelle — Les préférences de l'utilisateur (« il préfère les fonctions pures, déteste les classes »)
  7. 7.Déclarative — Les règles explicites du CLAUDE.md
  8. 8.Conversationnelle — Résumé des conversations passées pertinentes
  9. 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 »)
ℹ️
La mémoire « émotionnelle » peut sembler surprenante pour un agent de code. Mais c'est ce qui fait la différence entre un agent qui propose toujours des solutions en POO et un agent qui a appris que tu préfères la programmation fonctionnelle. Ce n'est pas de l'émotion au sens humain — c'est du profiling des préférences.

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. 1.Commence par les budgets fixes — Définis tes catégories et pourcentages. Ne les rends pas dynamiques au début.
  2. 2.Implémente le scoring statique — Assigne des scores fixes par type de contenu. Ça suffit pour 80 % des cas.
  3. 3.Ajoute la troncation simple — Quand le budget est dépassé, tronque les éléments les moins importants.
  4. 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.

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