Tes agents n'ont pas besoin de mémoire
Ouvre une nouvelle session sur ton agent. Zéro contexte, zéro historique. C’est un nouveau contractor qui débarque sur ton projet pour la première fois — et il sera remplacé par un autre contractor identique à la prochaine session.
Ce que tu vas lui donner dans les prochains tokens va conditionner tout ce qu’il produit. Et là, deux philosophies s’affrontent : lui donner de la mémoire — règles, décisions passées, erreurs à éviter — ou lui donner un harness — une codebase qui encode mécaniquement ses propres contraintes.
La première approche est intuitive. La deuxième est la seule qui scale.
Chaque session est un nouveau développeur en jour 1
Le problème fondamental avec les agents IA dans un contexte de développement, c’est qu’on anthropomorphise leur rapport au temps. On parle de “mémoire”, d‘“apprentissage”, de “ce que l’agent a appris”. Comme si l’expérience accumulée d’une session survivait à la suivante.
Elle ne survit pas. Chaque nouvelle session repart de zéro. L’agent ne se “souvient” pas que la dernière fois il avait essayé entry.render() et que ça avait cassé en production. Il ne “sait” pas que deux mois de débats ont abouti à la décision d’utiliser un loader glob avec generateId. Ces choses existent quelque part dans des logs, des PR, des discussions — mais elles ne sont pas dans son contexte, donc elles n’existent pas pour lui.
C’est exactement la situation d’un nouveau développeur humain qui arrive sur ton projet de 1M de lignes. Il n’a pas d’historique, pas de contexte implicite. Comment tu l’onboardes ?
Si ta réponse c’est un document de 80 pages de “choses à savoir et à ne pas faire”, tu vas avoir deux problèmes : personne ne lit vraiment ça, et même ceux qui le lisent n’en retiennent qu’une fraction — qui sera périmée au premier refactoring significatif. La réponse qui marche, c’est une codebase avec un CI vert, un linter configuré, des tests qui décrivent le comportement attendu. La documentation existe pour expliquer le pourquoi, pas pour compenser l’absence de contraintes mécaniques.
Pour les agents, c’est identique. Sauf qu’on a systématiquement pris le mauvais chemin.
La mémoire comme dette d’attention
Le context window est une ressource finie et non-uniforme. Finie, parce que même les modèles à 1M de tokens ont une limite. Non-uniforme, parce que l’attention d’un LLM sur un segment de son contexte se dégrade avec la distance — le “lost in the middle problem” est documenté empiriquement sur la majorité des modèles actuels. Une règle noyée au token 800k dans un contexte saturé est statistiquement ignorée, quelle que soit la qualité du modèle.
Chaque règle que tu injectes dans le contexte en début de session est un token qui n’est pas alloué à la tâche réelle. Et au fil de la session — échanges, lecture de fichiers, outputs d’outils, code généré — ces règles reculent mécaniquement dans la fenêtre d’attention. Elles ne disparaissent pas, mais leur poids effectif diminue.
Le calcul est impitoyable sur un projet réel : si ton AGENTS.md fait 200 lignes de règles et que l’agent travaille sur une feature qui implique 5 fichiers, 3 appels d’outils qui outputent chacun 300 lignes, et quelques allers-retours de debug, tu es rapidement dans un contexte où les règles du début ne pèsent plus grand-chose face au signal opérationnel immédiat.
Ce n’est pas un bug. C’est la nature d’un système à attention limitée. La vraie question c’est : pourquoi compter sur l’attention pour faire appliquer des règles, alors qu’il existe des mécanismes déterministes pour ça ?
L’augmentation de la fenêtre de contexte ne résout pas ce problème — elle le déplace. Un modèle à 2M de tokens aura toujours un problème d’attention sur les segments éloignés du signal actif.
Le test de binarité
Pour chaque connaissance que tu voudrais que ton agent “retienne”, pose-toi une question simple :
Est-ce que cette connaissance peut être vérifiée de façon binaire — passe ou échoue — sans intervention humaine ?
Si oui, transforme-la en artefact vérifiable. Ce n’est pas de la mémoire, c’est une contrainte mécanique. Si non, c’est du contexte narratif — il a sa place, mais c’est une exception, pas la règle.
Voici ce que ça donne en pratique sur des exemples réels :
| Ce qu’on met dans AGENTS.md | Artefact binaire équivalent |
|---|---|
”Ne jamais utiliser entry.render() en Astro 6” | Lint rule + message d’erreur explicite |
”Toujours créer slug.fr.mdx ET slug.en.mdx” | Script lint:content qui vérifie les paires |
”On utilise pnpm, pas npm” | package.json engines + .npmrc |
”Cette fonction retourne null si l’entrée est vide” | Type null | T + test unitaire |
”Ne pas créer de sous-dossiers dans content/posts/” | Script de validation sur la structure |
| ”Erreur de build silencieuse sur les routes imbriquées” | Test de non-régression |
Chaque ligne représente quelque chose qu’on aurait mis dans un AGENTS.md. Chacune a un équivalent mécanique qui ne dépend pas de l’attention d’un agent en session.
flowchart LR
A["Nouvelle règle\nou décision"] --> B{"Test de binarité"}
B -->|"Vérifiable\nmécaniquement"| C["Artefact binaire"]
B -->|"Non-vérifiable"| D["Documentation\nd'exception"]
C --> E["Lint rule"]
C --> F["Test / CI check"]
C --> G["Type / contrat"]
C --> H["Script de validation"]
D --> I["Index dans AGENTS.md\nvers la spec détaillée"]
style C fill:#22c55e,color:#fff
style D fill:#f59e0b,color:#fff
style I fill:#f59e0b,color:#fff
La branche verte est déterministe, toujours à jour, ne consomme pas de tokens de contexte. La branche jaune est nécessaire, mais elle doit rester rare et légère.
La codebase comme source de vérité exhaustive
Ce qui précède n’est pas qu’une question d’outillage. C’est une philosophie d’architecture : dans un projet bien tenu, la codebase encode elle-même les contraintes qui régissent sa propre évolution. On appelle ça un harness.
Un harness, c’est l’ensemble des mécanismes automatiques qui constituent le filet de sécurité du projet : types stricts, linters avec règles custom, tests unitaires et d’intégration, scripts de validation, CI pipeline. Pour un agent qui arrive en session 0, ce harness est la seule mémoire qui compte vraiment. Trois raisons à ça.
Il est toujours à jour. Une lint rule qui bloque entry.render() sera vraie aujourd’hui et dans six mois. Une ligne dans AGENTS.md qui dit la même chose peut devenir fausse après un refactoring — et personne ne mettra à jour le doc, parce que personne ne sait que c’est devenu obsolète.
Il est objectif. Le CI passe ou ne passe pas. Il n’y a pas d’interprétation, pas d’attention limitée, pas de règle noyée dans le contexte. L’agent essaie quelque chose, le linter bloque, il corrige. Le feedback loop est immédiat.
Il scale. Un projet à 1M de lignes avec 300 règles dans un AGENTS.md est ingérable pour un agent comme pour un humain. Un projet à 1M de lignes avec 300 contraintes encodées mécaniquement est simplement un projet bien tenu.
flowchart TD
Agent["Agent en session"] -->|"Tente une modification"| Code["Codebase"]
Code --> TS["TypeScript strict"]
Code --> Lint["ESLint + règles custom"]
Code --> Tests["Tests unitaires\net d'intégration"]
Code --> Scripts["Scripts de validation"]
Code --> CI["CI pipeline"]
TS -->|"Erreur de type"| Agent
Lint -->|"Violation bloquante"| Agent
Tests -->|"Test failing"| Agent
Scripts -->|"Check échoué"| Agent
CI -->|"Build rouge"| Agent
L’agent n’a pas besoin de “savoir” les règles en amont. Il les découvre au moment où il les viole — feedback immédiat, sans coût de contexte préalable.
Le harness est particulièrement puissant pour les erreurs passées. Dès qu’un agent — ou un humain — introduit un bug qui passe en prod, la première chose à faire c’est d’écrire un test de non-régression. Cette erreur ne peut plus jamais être “oubliée” — elle est encodée dans le projet pour toujours.
Le paradoxe des bonnes pratiques documentées
Il y a un paradoxe dans l’approche “mémoire” qui mérite d’être nommé. Les équipes qui documentent le mieux leurs règles dans des AGENTS.md sont souvent celles qui ont les meilleurs instincts d’ingénierie. Elles identifient correctement les patterns dangereux, les erreurs récurrentes, les conventions critiques.
Mais en les mettant dans un document plutôt que dans du code, elles font exactement l’inverse de ce que leurs instincts devraient leur dicter : elles créent une règle non-vérifiable là où une règle vérifiable était possible.
C’est une forme de dette technique cognitive. La documentation remplace le travail d’encodage mécanique parce que c’est plus rapide — noter “ne jamais faire X” prend trente secondes, écrire une lint rule prend trente minutes. Mais cette dette se paie à chaque session d’agent qui ignore la règle parce qu’elle était trop loin dans le contexte.
La bonne heuristique : si une règle mérite d’être dans AGENTS.md, elle mérite encore plus d’être dans le harness. AGENTS.md est le plan de secours pour ce qui résiste à la mécanisation — pas la solution par défaut.
AGENTS.md comme index, pas comme encyclopédie
Rien de tout ça ne signifie qu’AGENTS.md est inutile. Il a juste le mauvais job depuis le début.
Voici à quoi ressemble un AGENTS.md avec la mauvaise philosophie :
# Règles importantes
- Ne jamais utiliser entry.render() — utiliser render(entry) depuis astro:content
- Toujours créer les deux fichiers .fr.mdx et .en.mdx
- Ne pas créer de sous-dossiers dans src/content/posts/
- Utiliser des chemins relatifs pour les imports dans les fichiers MDX
- Le slug doit être identique dans les deux versions linguistiques
- Mermaid via fenced code blocks uniquement, jamais via composant JSX
... [encore 40 règles]
Et à quoi il devrait ressembler :
# Navigation du projet
## Architecture et décisions techniques
Les décisions d'architecture et leur justification sont dans `docs/adr/`.
Consulter les ADRs existants avant tout changement structurel.
## Conventions de code
Encodées dans `eslint.config.js`. Le linter est la source de vérité — pas ce fichier.
Règles custom documentées dans `docs/specs/lint-rules.md`.
## Contenu / articles
Structure et conventions : `docs/specs/content-structure.md`
Validation : `npm run lint:content`
## Tests
Patterns attendus : `docs/specs/testing.md`
Seuils de coverage : définis dans `vitest.config.ts`
Ce fichier tient en 20 lignes. Il ne demande à l’agent de rien retenir — il lui dit où aller quand il a besoin de quelque chose. Et ce qu’il trouve à ces destinations est spécifique, à jour, et souvent complété par des artefacts binaires.
La différence fondamentale : un index consomme quelques tokens au début d’une session pour orienter l’agent. Une encyclopédie consomme des centaines de tokens pour créer une illusion de savoir qui se dégrade avec le contexte.
Le bon test pour évaluer ton AGENTS.md : est-ce que chaque ligne pointe vers quelque chose de vérifiable, ou est-ce qu’elle demande à l’agent de “se souvenir” de quelque chose ? Les lignes de la deuxième catégorie devraient être dans le harness, pas dans ce fichier.
Ce que ça change concrètement
Passer de l’approche mémoire à l’approche harness change trois choses dans la façon de travailler avec des agents.
D’abord, ça déplace le travail en amont. Identifier qu’une règle doit exister et l’écrire dans un lint rule ou un test prend plus de temps que de la noter dans un doc. C’est un investissement — qui se rentabilise dès la deuxième session d’agent sur ce pattern.
Ensuite, ça rend les erreurs constructives plutôt que répétitives. Quand un agent viole une contrainte et que le CI bloque, cette erreur est traitée une fois pour toutes. Sans harness, la même erreur se répète à chaque session parce que l’agent “oublie” — ou plutôt parce que la règle était dans un doc que personne n’a pensé à mettre en tête de contexte.
Enfin, ça change la relation avec AGENTS.md lui-même. Plutôt qu’un fichier qu’on craint de ne pas avoir assez bien rédigé, c’est un index qu’on consulte ponctuellement quand on cherche quelque chose. La charge cognitive passe du “tout savoir en avance” au “savoir où chercher quand c’est nécessaire” — ce qui est exactement comme ça que travaillent les bons développeurs humains sur des projets complexes.
Sur un projet de 1M de lignes, la seule mémoire fiable c’est celle qui est encodée dans le code. Tout le reste, c’est du bruit qui décroît avec le temps.