Position du problème
Un LLM ou un SLM représente plusieurs gigaoctets en mémoire, et leur rechargement dans chaque processus provoquerait une explosion de la consommation RAM, une fragmentation CUDA, des temps de démarrage prohibitifs et une instabilité générale du système. Le mode daemon résout ce problème en garantissant qu’un modèle n’est chargé qu’une seule fois dans un processus dédié, puis partagé entre tous les autres via RPC. Cela permet un fonctionnement réellement concurrent, une gestion propre du streaming token‑par‑token, une isolation des erreurs, et une stabilité mémoire indispensable en production.
Pour cette raison, le LLM et le SLM doivent être servis via un daemon. Les deux modèles sont sollicités par plusieurs utilisateurs, plusieurs sessions et plusieurs moteurs internes, parfois simultanément. Les Thought et les réponses finales de ReAct doivent être générées sans jamais recharger les poids des modèles, sans bloquer les autres processus, et sans dépendre de l’environnement local de chaque composant. En les exécutant tous deux en daemon, l’architecture v200 devient cohérente, déterministe et scalable : un seul chargement, un seul point d’accès, un comportement identique pour tous les clients, et une capacité à monter en charge sans modifier le code des orchestrateurs ou des moteurs internes.
Ce raisonnement vaut également pour les index.
Rôle du daemon
Le daemon Pyro5 est un fournisseur multi-services.
Via ModelsConfigurator, il :
-
Charge les modèles définis dans
models.json, ce qui initialise AppSettings et les modèles globaux (embedding, LLM, SLM). -
Charge les services déclarés dans
services.jsonet les instancie dynamiquement. -
Enregistre chaque service dans Pyro5 sous un nom stable :
-
llm.server -
slm.server -
embedding.server -
rag.reader
-
Puis : Lance la boucle d’événements Pyro5.
Exemple : chargement d’un index par RagRuntime :
Composants principaux
1. MemoryManager
-
Arbitre global de la RAM.
-
Suit les allocations d’objets persistants.
-
Applique une politique d’éviction (LRU locale).
-
Fournit un état mémoire complet.
-
Thread-safe.
2. InDaemonRagReader
-
Reader interne au daemon.
-
Encode les requêtes via le modèle d’embedding global.
-
Interroge le backend vectoriel (FAISS, LlamaIndex ...).
-
Renvoie des
RichNode. -
Fournit un mode streaming (
astream).
Noter qu’une architecture séparant le modèle d’embedding et l’index dans des daemons distincts conduirait à un grand nombre d’échanges RCP et à des temps de traitement prohibitifs.
Interaction avec le client
Le client utilise InDaemonRagReaderProxy pour contacter rag.reader.
Flux typique :
RagRuntime.retrieve()
→ InDaemonRagReaderProxy("rag.reader").retrieve(theme, query, top_k)
→ InDaemonRagReader.retrieve()
→ encode la requête
→ interroge le backend vectoriel
→ renvoie des RichNode
Le client ne voit jamais MemoryManager : tout est encapsulé dans le daemon.
Avantages de cette architecture
-
Un seul daemon → cohérence mémoire.
-
Plusieurs services → modularité.
-
MemoryManager central → contrôle RAM industriel.
-
InDaemonRagReader → moteur RAG unifié.
-
Structure simple → facile à étendre.