Au-delà du RAG
Note : Cet article fait suite à RAG sur vllm-gke. Le code source complet est disponible sur GitHub : iheb24/vllm-gke, et chaque fichier mentionné est lié directement.
Dans cet article de la série, j'explore d'autres acronymes populaires sur les stacks d'inférence. Après avoir exploré le RAG en profondeur, j'ai voulu examiner les méthodes voisines : TAG, CAG, MAG et KAG.
Au premier abord, on pourrait dire que toutes ces méthodes relèvent de la génération augmentée, mais cela ne s'applique pas à chacune d'entre elles, notamment le MAG, qui reste proche de la Memory-Augmented Generation par son nom tout en se comportant un peu différemment, comme je l'expliquerai dans sa section.
À première vue, je pensais également que ces concepts allaient remplacer entièrement les procédures RAG. Ce n'est pas le cas. Ils s'utilisent en complément du RAG, chacun sur un cas d'usage précis où il apporte un gain de précision et d'efficacité là où c'est nécessaire.
Le RAG fournit de la connaissance au LLM autour du prompt, et ces méthodes se comportent différemment pour répondre au mieux à leur propre objectif. Les acronymes sont faciles à décrypter : ils font généralement référence à la source de l'information.
- TAG : Table
- CAG : Cache
- MAG : Memory (Mémoire)
- KAG : Knowledge (Connaissance)
Le KAG est celui qui s'éloigne le plus de la simple récupération, puisqu'il parcourt un graphe de connaissances au lieu d'une source plate unique.
J'ai pu mettre en démonstration les trois premières méthodes sur mon dépôt. Elles sont déployables à côté de la stack RAG existante, une par une, pour montrer ce dont chacune est capable. Chaque méthode a également nécessité ses propres ressources dédiées et une configuration sur mesure, que je détaillerai dans chaque section.
TAG
Au lieu d'utiliser une base de données vectorielle et d'analyser des données non structurées, cette méthode s'appuie sur des tables mises à disposition du LLM pour être interrogées. Dans ce dépôt, cela repose sur du SQL, elle est donc basée sur des données structurées plutôt que non structurées.
C'est une solution idéale lorsqu'on manipule des détails numériques et précis que l'on souhaite fournir au LLM au moment de l'exécution, par exemple pour du reporting financier, des détails logistiques ou des cas d'usage similaires. Le TAG fonctionne parfaitement dans ce cadre. Il n'ingère pas de fichier documentaire ni de PDF ; il s'appuie sur des données strictement structurées avec un accès par colonnes.
La récupération ressemble beaucoup au RAG dans sa forme, mais le système n'utilise pas le LLM pour vectoriser la requête : il la traduit en une requête SQL qui est ensuite exécutée sur les tables accessibles au système. L'information trouvée est insérée dans le prompt avec la demande de l'utilisateur, pour l'appel final au LLM qui rédige la réponse.
Dans le prompt, l'information additionnelle est par conséquent structurée (colonnes et lignes) et insérée directement dans le message envoyé au LLM.
Tests et déploiement sur le dépôt
La démo se trouve dans showcases/tag/ avec une base de données synthétique d'analytique de serving (trente jours de métriques de requêtes et de coûts ainsi que des notes d'incidents en texte libre, toutes clairement étiquetées comme synthétiques). Le pipeline est dans pipeline.py et le garde-fou SQL dans sql_guard.py. Un détail que j'apprécie particulièrement dans cette conception : le garde-fou est appliqué par SQLite lui-même via un authorizer de connexion, de sorte que les écritures, les tables inconnues et les résultats trop volumineux sont rejetés au niveau du moteur, et non par un simple filtrage de chaînes de caractères.
Pour les tests, 41 tests locaux s'exécutent sans GPU et sans cluster, en utilisant un faux modèle scripté. Le harnais d'évaluation couvre un jeu de 17 questions via eval/run_tag_eval.py : analytique exacte, classification sémantique, tentatives d'écriture et questions hors périmètre. En conditions réelles sur le cluster avec le modèle 14B, j'ai mesuré un score exact de 8/9, les 3 tentatives d'écriture rejetées de bout en bout, et toutes les questions hors périmètre ont correctement produit zéro SQL. Les chiffres complets et l'analyse des échecs se trouvent dans le rapport live. Le bilan honnête : le contrat de sécurité a tenu à 100 %, tandis que le formatage de la réponse et le jugement du périmètre varient selon le modèle.
| Avantages | Inconvénients |
|---|---|
| Chiffres exacts, pas de dérive vectorielle | Ne répond qu'à ce que contient le schéma |
| Le SQL est inspectable et auditable | Le SQL généré par le modèle nécessite un garde-fou strict |
| Économique : un seul fichier SQLite | Les questions sémantiques nécessitent toujours un modèle performant |
Idéal pour : l'analytique sur des tables métier ou opérationnelles, là où être exactement juste l'emporte sur le fait d'être approximativement pertinent.
CAG
Le CAG fonctionne différemment, et il se prêtait particulièrement bien à une démonstration puisque nous utilisons vLLM comme moteur d'inférence. Le C signifie cache, et ici le cache vit directement au sein de la machine d'inférence.
L'énorme avantage est qu'il n'y a ni base de données vectorielle ni système de récupération : tout est directement à portée du GPU. Mais les GPU étant limités, il faut voir cela comme une petite extension de connaissances accessible avec une très faible latence, et non comme un espace de stockage bon marché.
Concrètement, la connaissance réside sous forme d'état KV dans le cache KV du GPU, à côté de l'état du prompt actif. Sur le chemin d'accès, nous chargeons l'intégralité de la connaissance une première fois et elle est traitée dans le cache KV ; ainsi, avant même qu'une question ou une tâche n'arrive au LLM, la connaissance est déjà chargée. Lorsque le prompt arrive, il est ajouté juste après le préfixe mis en cache, et le modèle utilise ce cache pour répondre instantanément ou élaborer sa réponse à partir de celui-ci. Une fois la réponse envoyée, seule la connaissance préchargée reste chaude en mémoire, prête pour le prompt suivant.
Deux points que j'ai appris à mes dépens ici. Premièrement, cela ne réduit que le travail de prefill : le temps de décodage (decode) et le démarrage du modèle restent inchangés. Deuxièmement, le cache est local au processus, il disparaît donc avec le pod, et après une réduction à zéro (scale-to-zero), la première requête paie à nouveau le prefill complet. L'utilisation de cette connaissance peut être prouvée en suivant précisément les métriques du cache KV du GPU, ce que j'ai fait.
Tests et déploiement sur le dépôt
La démo se trouve dans showcases/cag/, avec le constructeur de préfixe dans corpus.py et le harnais de benchmark dans benchmark.py. Le corpus est figé par un manifeste contenant une version SHA-256 et un nombre de tokens mesuré avec le tokenizer du modèle servi : 7 628 tokens, plafonné à 12 000 afin que la question et la réponse tiennent toujours dans la fenêtre de 32K.
Pour les tests, 29 tests locaux couvrent l'identité à l'octet près du préfixe, l'obsolescence du manifeste, les dépassements de budget et la logique de verdict basée sur les compteurs. En conditions réelles sur le cluster, j'ai mesuré le temps avant le premier token (TTFT) à 7,23 s lors de la première exécution à froid (pod neuf, compteurs à zéro), contre environ 0,53 s à chaud. Le fichier de benchmark publié contient les preuves de compteurs de l'exécution de vérification : 85 798 requêtes sur le cache de préfixe, 85 712 hits (99,9 %) sur 11 requêtes, relevés depuis les métriques du serveur. La provenance complète des deux chiffres se trouve dans le champ run_history de eval/reports/2026-10-28-cag-benchmark.md et les exécutions de qualité dans eval/reports/2026-10-28-cag-live.md. Le volet qualité des réponses est honnêtement plus faible avec mon modèle 14B : le contenu reste ancré dans les sources, mais la rigueur des citations varie d'une exécution à l'autre.
| Avantages | Inconvénients |
|---|---|
| Zéro infra de récupération : ni base vectorielle, ni embeddings | Le corpus doit rester petit (budget de contexte) |
| Prefill à chaud quasi gratuit (TTFT 13x plus rapide ici) | Le cache disparaît au redémarrage du pod et au scale-to-zero |
| Connaissances déterministes et versionnées | Gain de latence, pas une garantie de qualité supérieure au RAG |
Idéal pour : de nombreuses questions portant sur un petit corpus stable : manuels internes, runbooks, références d'API.
MAG
L'acronyme ressemble donc à de la génération augmentée, mais après avoir creusé le concept, il est intéressant de noter qu'il est légèrement différent.
Le MAG prend tout son sens pour les LLM qui ont besoin d'un stockage d'état persistant. Comme vu dans les articles sur AgentCore et ADK, un agent a souvent besoin d'un stockage persistant pour enregistrer, mettre à jour et rappeler le contexte historique au cours d'une session ou d'une session à l'autre. La différence se situe précisément là : la connaissance n'est pas préchargée, elle est créée et sauvegardée à partir de l'historique des conversations, des contextes, des découvertes et des résultats.
C'est parfait pour des assistants qui apprennent de manière incrémentale au fil de leur utilisation. Au début, je pensais que le LLM lui-même connaissait l'historique, mais il s'agit en réalité d'un bloc de mémoire externe, qui peut aussi bien être de type clé-valeur si nécessaire. Le meilleur exemple est lorsque vous utilisez GPT, Gemini ou un autre concurrent sur la durée : vous remarquez qu'il se souvient d'anciennes sessions et d'informations vous concernant. C'est exactement ce pattern à l'œuvre, et il peut également être intégré via des frameworks gérés comme mentionné précédemment, tels qu'ADK ou AgentCore sur AWS.
Mais pour une fois, mon intuition était la bonne sur le MAG : l'écriture et la récupération se produisent véritablement autour du LLM pendant son exécution. Dans ma démo, j'ai gardé les écritures explicites (une commande remember: ou un appel API) afin que rien ne se produise silencieusement, et chaque identifiant de souvenir injecté apparaît dans la trace de la réponse. Des frameworks comme mem0 ou les services d'agents gérés vont plus loin et laissent le modèle piloter lui-même les écritures en mémoire.
Tests et déploiement sur le dépôt
La démo se trouve dans showcases/mag/, avec le stockage dans memory_store.py : souvenirs par utilisateur avec recherche plein texte (full-text search), historique de session borné et un petit PVC pour que l'état survive aux redémarrages du pod.
Pour les tests, 47 tests locaux couvrent le cycle de vie : mémorisation dans la session A, rappel dans la session B, correction, suppression, isolation entre utilisateurs, rejet des entrées ressemblant à des secrets et réouverture de la base de données. En conditions réelles sur le cluster, toutes les vérifications déterministes ont réussi (3/5 scénarios sur le score strict par sous-chaîne, les deux écarts étant de simples variantes de formulation) : Alice et Bob restent isolés, les modifications inter-utilisateurs renvoient 404, et après suppression du pod, le nouveau pod a remonté le PVC et a bien retrouvé la préférence enregistrée. Le rapport se trouve dans eval/reports/2026-10-28-mag-live.md.
| Avantages | Inconvénients |
|---|---|
| Personnalisation qui survit aux redémarrages | Écritures explicites uniquement ; pas encore d'extraction automatique |
| Contrôle total de l'utilisateur : corriger, supprimer, tout effacer | user_id est un identifiant de démo, pas une authentification |
| Léger : un fichier SQLite sur un PVC de 1 Gi | Récupération par mots-clés, pas de recherche sémantique en mémoire |
Idéal pour : les assistants nécessitant une continuité : préférences, corrections et contexte de projet à travers les sessions.
En définitive, le MAG est une méthode très utile. Ce n'est pas vraiment de la génération augmentée au sens strict, mais sa valeur est telle qu'il mérite largement sa place dans cette liste. Certaines entreprises ont commencé à constituer des bases de données contenant des connaissances de haut niveau sur leurs produits pour les fournir aux LLM, et il existe également des outils disponibles localement pour les compagnons IA qui font exactement cela, comme les frameworks de mémoire d'agents ou mem0.
KAG
Le KAG signifie Knowledge-Augmented Generation et s'appuie fortement sur les graphes de connaissances (knowledge graphs). Il est parfait pour les bases de connaissances avancées et volumineuses, où il offre une précision multi-sauts et réduit considérablement l'effort de recherche par rapport à une base de données vectorielle traditionnelle utilisée seule.
Le KAG ressemble au RAG en ce sens que la connaissance est préparée avant la question, mais ici elle est stockée sur un graphe d'entités cartographié, indexé conjointement avec le texte. Ainsi, au lieu de simplement chercher une information précise par correspondance de motifs (pattern matching), le système effectue un raisonnement à sauts multiples (multi-hop) sur les index cartographiés. Pour d'immenses graphes de connaissances, c'est redoutablement efficace : historique médical d'un patient pour un assistant LLM, recherche scientifique (STEM), recherche juridique, détection de fraude.
Son plus grand défaut réside dans sa complexité d'implémentation : en plus de l'énorme volume d'informations nécessaire, le graphe doit être traité et ajusté pour que l'agent puisse sauter librement et efficacement d'un nœud de connaissance à l'autre.
Par conséquent, cette méthode n'est pas implémentée en démonstration sur le dépôt, mais j'ai effectué des recherches sur son fonctionnement. Pour récupérer la connaissance, un système prend le prompt et associe ses concepts à des nœuds spécifiques du graphe. Ces nœuds possèdent des connexions logiques externes vers d'autres nœuds, d'où la traversée, ou le « saut » (hop). Le système rassemble alors le sous-graphe composé des nœuds de départ et des nœuds logiquement connectés autour d'eux. Ce sous-graphe est ensuite traduit et injecté dans le prompt destiné au LLM. Une précision importante : il n'abandonne pas pour autant la recherche par correspondance, il la combine avec les connexions logiques entre les nœuds, ce qui rend le résultat plus solide et moins sujet aux hallucinations, tout en justifiant la complexité liée à la création de bons graphes de connaissances pour obtenir les résultats souhaités avec le LLM.
Côté recherche, j'ai rédigé un cas d'usage concret pour cette stack : le diagnostic à sauts multiples sur un graphe de composants, pools de nœuds, secrets et runbooks, avec l'architecture complète, l'analyse des coûts et une checklist go/no-go pour un futur spike avec le framework officiel OpenSPG/KAG. Tout se trouve dans docs/kag-architecture.md. Il n'est pas implémenté pour deux raisons simples : l'empreinte opérationnelle ne rentre pas dans un cluster mono-GPU soumis à un suivi strict des coûts, et étiqueter « KAG » un simple script de graphe écrit à la main serait malhonnête.
| Avantages | Inconvénients |
|---|---|
| Raisonnement multi-sauts avec provenance | La stack la plus lourde à construire et à exploiter |
| Le schéma garde le graphe propre et interrogeable | La conception du schéma reste un travail humain continu |
| Le plus adapté aux domaines riches en relations | Plusieurs appels au modèle par question : latence et coût |
Idéal pour : les grandes bases de connaissances fortement relationnelles où la réponse est un chemin, et non un passage.
Runbook
Tout fonctionne un par un par politique, appliquée par showcases/demo.sh. Le script refusera de démarrer une deuxième démonstration à moins de le forcer.
Déployer une démonstration
Évaluer
Les manifestes utilisent uniquement des services ClusterIP et se trouvent dans k8s/showcases/. Les rapports, y compris les exécutions réelles sur le cluster, sont archivés sous eval/reports/. La matrice de décision complète est disponible dans docs/augmented-generation-comparison.md.
Subtilités du scale-to-zero GPU bonnes à connaître
Quelques détails opérationnels de ce projet m'ont coûté pas mal de temps, je les liste donc brièvement :
- L'intercepteur HTTP KEDA retient votre requête pendant que le nœud GPU passe de zéro à un. Sur un cluster neuf, la première attente inclut le téléchargement d'un modèle d'environ 10 Go, de sorte que la première réponse peut prendre près de dix minutes.
- La passerelle possède son propre timeout amont (upstream) et peut renvoyer une erreur 504 avant qu'un vLLM à froid ne soit prêt. Le scale-up se poursuit malgré tout ; attendez que le pod soit prêt et réessayez.
- Entre les appels, le GPU reste chaud pendant la durée
scaledownPeriod(300 s ici), puis tout redescend à zéro. - Le cache de préfixe de vLLM est local au processus. Après un scale-down, les compteurs retombent à zéro et le préfixe CAG doit être réamorcé. C'est exactement ce que j'ai mesuré.
- Les GPU L4 à la demande étaient en rupture de capacité dans ma zone ; le pool GPU tourne désormais en instances spot, qui sont à la fois plus faciles à obtenir et environ 60 à 70 % moins chères.
- L'exécution live de MAG a utilisé délibérément le niveau CPU (
AG_GATEWAY_MODEL=qwen3-4b-cpu), de sorte que tout le scénario de mémoire n'a jamais réveillé le GPU.
Conclusion
Les quatre acronymes sont plus faciles à retenir par l'état qu'ils manipulent que par leur nom : tables, cache, mémoire, graphe. Aucun d'eux ne remplace la récupération ; chacun l'emporte là où la forme de son état correspond à la question. Le dépôt montre les trois que j'ai construits honnêtement, mesures à l'appui, le quatrième restant à l'état d'architecture documentée. Si vous devez en choisir un, choisissez d'abord selon la forme de vos données, pas selon l'acronyme.