État des lieux, point mi-parcours
À mi-chemin, un système ne se résume pas. Il se lit.
Neuf épisodes derrière. Trois de fondation (la vision, les choix d'écosystème, le langage), six pour poser le cœur du système : la boucle sans intention, les modules comme ignorances nommées, les scopes comme déclarations de cycle de vie, l'orchestration en deux niveaux, le DAG qui révèle le parallélisme, les deux mémoires qui survivent aux arrêts.
Il est temps de s'arrêter. Pas pour conclure, le système n'est pas fini. Pour regarder. Un système à mi-parcours n'est pas un système incomplet, c'est un système dont on peut déjà lire la forme. Voir la carte, nommer les principes qui la tiennent, laisser visible ce qui est.
Cette pause est nécessaire. Chaque décision prise en cours de route a paru juste au moment où elle a été prise. Or la justesse d'une étape ne dit rien de la justesse d'une trajectoire. Un chemin fait de pas certains peut mener ailleurs qu'on ne le voulait, et c'est précisément le genre de dérive qu'on ne voit plus, à force d'avoir le nez sur les étapes. Le mi-parcours est le dernier endroit où la correction reste peu coûteuse.
Retour de R&D : la justesse locale ne garantit pas la justesse d'ensemble. C'est une chose qu'il m'a fallu intégrer, et ce chapitre en est l'application. Chaque décision de conception prise isolément peut être rigoureuse sans que l'accumulation de ces décisions forme un système cohérent. Le risque n'est pas de se tromper sur une étape. C'est de ne pas voir, parce qu'on ne relit plus, que l'orientation générale a glissé. Une méthodologie de réflexion, même appliquée avec rigueur, porte ses propres biais, et ces biais ne se révèlent qu'à l'échelle de l'ensemble. Plus le système avance, plus ses fondations résistent à l'examen, et plus une correction tardive devient coûteuse. S'arrêter à mi-parcours n'est pas un signe de doute sur la méthode, c'est un acte de méthode : l'audit qu'on se fait à soi-même avant de continuer.
La carte

Ce que la carte montre tient en une lecture : un cœur domaine (EvaluationModule, CompetencyModule) qui sait ce qu'est une évaluation ; un moteur générique (AgentModule) qui ne le sait pas ; un cache sémantique (VectorStoreModule) qui sert les deux sans appartenir à aucun ; et autour, des frontières externes qui portent les messages, les configurations, les états, les traces.
Que dit la vue d'ensemble?
Cinq principes tiennent la carte ensemble. Chacun part d'une posture, et descend vers les choix concrets qui la portent.
Nommer, c'est tracer. Il y a une équivalence, dans ce système, entre nommer une chose et la séparer du reste. Le nom n'est pas un étiquetage postérieur à la conception : il est la conception même. Un module n'existe pas avant d'être nommé, et l'acte de le nommer précisément (LLMModule pour ce qui appelle le modèle, SafetyModule pour ce qui surveille la boucle) trace en même temps la frontière qui le sépare de tout ce qu'il n'est pas. Le même geste traverse le système à toutes les échelles : les compétences (ep1) qui sont des noms avant d'être des notes, les modules (ep5) qui émergent des ignorances nommées, les use cases (ep7) qui isolent un scénario métier par un nom. La frontière d'un bon système coïncide avec l'endroit où un nom s'arrête.
Le générique n'est pas une abstraction, c'est une ignorance choisie. L'abstraction, habituellement, est ce qu'on extrait après coup pour factoriser. Ici, le générique est une posture maintenue à tout prix. AgentService ne sait pas ce qu'il évalue. Il ne sait pas qu'il contribue à une évaluation. Cette ignorance n'est pas une limite, c'est sa force : elle est ce qui lui permettra demain de servir un agent conversationnel, une analyse de progression, ou tout usage qu'on n'a pas encore nommé. Le Transient de l'épisode 6 est le corollaire technique de cette ignorance : si la boucle portait le moindre état partagé entre deux exécutions, elle cesserait d'être générique. Être générique coûte, et ce coût a été assumé dès le premier flux tracé.
Les flèches protègent. Dans un graphe de dépendances, la direction des flèches n'est pas cosmétique. Elle dit qui peut changer sans avertir qui. VectorStoreModule peut migrer d'implémentation, modifier sa stratégie de cache, restructurer ses indexes, sans qu'AgentModule en soit informé, parce que la flèche va d'AgentModule vers VectorStoreModule et jamais l'inverse. Ce principe traverse tous les niveaux du système : entre modules (ep5), dans la scope chain où la durée de vie se propage du consommateur vers ses dépendances (ep6), dans la cascade d'ignorance où chaque couche ne connaît que la suivante (ep7). La régularité n'est pas un hasard, c'est la même règle appliquée à toutes les échelles. Un système où chaque flèche monte du spécifique vers le générique est un système où les parties peuvent évoluer sans que le tout soit menacé.
Ce qui émerge au lieu d'être imposé. Une configuration d'évaluation ne déclare pas "exécute ces passes en parallèle, puis celles-ci une fois terminées". Elle déclare simplement "cette passe dépend de celle-là". C'est DAGExecutionOrchestrator qui en tire les conséquences. Le parallélisme n'est pas programmé, il est révélé par l'absence de dépendances entre passes d'une même couche. Cette distinction entre ce qu'on décrit et ce qui s'en déduit est philosophique autant que technique. Elle dit qu'un bon système n'a pas à être orchestré dans le détail : il suffit de poser les bonnes contraintes (les dépendances), et l'exécution trouve sa forme. La même idée s'applique à deux niveaux, entre passes et entre règles. Ce n'est pas que le code soit factorisé, c'est que le principe est invariant.
La mémoire hors du vivant. Un processus qui tourne porte un état. Quand il s'arrête, cet état disparaît avec lui. La question n'est pas de faire des processus immortels (c'est impossible), c'est de faire en sorte que la mort d'un processus n'efface que ce qui peut être reconstruit. D'où la construction des deux mémoires de l'épisode 9 : RabbitMQ conserve ce qui n'a pas encore été traité, Redis conserve ce qui a déjà été fait. Ni l'une ni l'autre ne suffit seule. Et cette construction vient avec une contrainte silencieuse mais non négociable : les règles doivent être idempotentes. Un agent qui évalue le même code deux fois doit produire le même verdict. La résilience n'est pas dans l'infrastructure seule, elle est dans la discipline de ce que l'infrastructure demande.
Ce sur quoi la carte parie
Cinq principes posés. Ils tiennent, ils s'enchaînent, ils se renforcent. Mais une carte n'est pas une preuve. Ce qu'elle montre est un pari, plusieurs paris, que les mois à venir devront valider à l'usage.
Le premier pari est celui du générique. AgentService a été conçu pour servir X agents (évaluation, assistance, progression, etc.). Un seul tourne aujourd'hui. Rien ne garantit, tant que les deux autres ne sont pas construits, que la boucle telle qu'elle est pourra réellement les accueillir sans ajustement. Un besoin spécifique (une mémoire conversationnelle pour l'agent d'assistance, une agrégation multi-évaluations pour l'agent de progression) pourrait forcer la boucle à sortir de son ignorance. Si cela arrive, la symétrie posée dans l'épisode 4 devra être repensée. Pour l'instant, cette symétrie est une hypothèse, pas un fait établi.
Le deuxième pari est celui du coût en exécution du générique. Être générique a un coût, on l'a dit. Mais ce coût-là a été pesé en conception : heures de réflexion, décisions tranchées. Le coût à l'usage, en latence, en volume de traces, en complexité de debug quand une boucle échoue à mi-parcours, ne se révélera qu'en production soutenue. Il est possible, au détour d'un goulet d'étranglement non anticipé, qu'il faille spécialiser une partie de la boucle pour des raisons de performance. Cette concession serait une entorse à un principe, et je ne sais pas aujourd'hui si elle sera nécessaire.
Le troisième pari est celui de l'idempotence. La garantie de reprise après crash repose sur une exigence silencieuse : qu'une règle exécutée deux fois sur le même code produise le même verdict. C'est une contrainte de conception, pas une propriété vérifiée. Un agent LLM n'est jamais strictement déterministe, et la tolérance à la variation reste à mesurer sous charge. Une dérive subtile, à la marge, pourrait casser la reprise sans que le code ne lève le moindre signal. L'épisode 9 formule la contrainte ; rien n'a encore vérifié qu'elle tient dans la durée.
Le quatrième pari est celui de la frontière évidences / scores. AgentService produit des évidences brutes ; EvaluationModule les traduit en scores. Cette frontière m'a pris du temps à formuler et elle me satisfait aujourd'hui. Mais il est possible qu'une règle future demande un score en cours d'exploration, pas seulement en fin de boucle, par exemple pour décider d'arrêter l'exploration plus tôt quand une évidence décisive a été trouvée. Si ce cas se présente, la frontière devra être revue. Elle n'est pas définitive, elle est provisoire.
Le cinquième pari est celui des frontières sous pression. Les modules ont été dessinés à partir des ignorances de la boucle. Cette genèse donne des frontières claires aujourd'hui. Mais un système évolue, et les ignorances évoluent avec lui. Demain, un sous-module pourrait devoir se scinder parce qu'il porterait deux responsabilités qui se distinguent à l'usage. Un autre pourrait devoir se fusionner avec un voisin devenu trop intriqué. Chaque fois que cela arrivera, les flèches devront être redessinées, et rien ne garantit que la nouvelle carte sera aussi cohérente que celle d'aujourd'hui. Le coût de ces refontes n'a pas été anticipé dans le design ; il se paiera au moment voulu.
Il y en aurait d'autres. Cinq suffisent à nommer la posture : une carte à mi-parcours n'est pas un point d'arrivée, c'est une photographie prise au milieu d'un chemin. Écrire ce chapitre, c'est accepter qu'elle sera peut-être démentie, et poser quand même ce qui tient pour qu'on puisse le relire plus tard.
Cinq principes. Six épisodes pour les poser. La carte tient. Les flèches vont dans le bon sens, les boucles ne se contaminent pas, les passes se parallélisent sans qu'on le leur demande, les règles peuvent échouer sans que l'évaluation s'effondre, les processus peuvent mourir sans qu'on recommence à zéro.
Tout cela est juste. Tout cela est propre. Et pourtant, en relisant cette carte, un manque devient visible. Ce système parie sur le comportement d'un LLM. Il l'encadre (LoopDetector surveille les dérives, StructuredOutputHandler gère l'imprévisible, SafetyModule veille sur les patterns), il le cloisonne, il le remplace quand c'est possible. Mais il n'en observe pas l'intérieur. Quand un agent prend trois tours pour comprendre une règle là où un autre en prend douze, on ne sait pas pourquoi. Quand une évaluation coûte dix fois plus cher qu'une autre, la raison reste dans la boîte noire. Les gardes-fous protègent l'exécution ; ils ne la racontent pas.
La traçabilité n'était pas dans la carte. C'est ce qui manque, et c'est ce que l'épisode suivant posera.