pguso/rag-from-scratch : construire un pipeline RAG en JavaScript, sans API cloud
Demystify RAG by building it from scratch. Local LLMs, no black boxes - real understanding of embeddings, vector search, retrieval, and context-augmented generation.
En bref
- De quoi s’agit-il ?
- Un dépôt pédagogique en JavaScript qui décompose le RAG en dix étapes exécutables en local avec node-llama-cpp. Utile pour comprendre les embeddings et la recherche vectorielle, moins pour mettre un service en production.
- À qui s’adresse-t-il ?
- À adopter si vous voulez écrire vous-même chaque brique d'un pipeline RAG et lire le code qui produit les vecteurs, le chunking et le classement. À éviter si vous cherchez un service interrogeable : rien ici ne décrit un serveur, une persistance ni une gestion de montée en charge.
- Puis-je l’utiliser commercialement ?
- Oui. MIT est une licence permissive : vous pouvez utiliser, modifier et vendre un logiciel qui en dépend, à condition de conserver les mentions de droit d’auteur et de licence.
- Est-il encore maintenu ?
- L’activité ralentit. Les derniers commits datent d’il y a 6 mois.
- En quel langage est-il écrit ?
- Principalement JavaScript, d’après les statistiques de langage de GitHub.
Ces réponses reposent sur les données GitHub du projet (dernière synchronisation le 15 septembre 2026) et sur notre analyse. Elles ne constituent pas un avis juridique.
ANALYSE OPEN SOURCE APPROFONDIE
Le problème visé : le RAG comme boîte noire
La plupart des tutoriels RAG font tenir le pipeline dans un appel à une bibliothèque d'orchestration. On obtient une réponse, mais on ne sait pas ce qui s'est passé entre la question et le texte généré. pguso/rag-from-scratch part du principe inverse : chaque étape est un fichier JavaScript lisible, exécuté en local, sans API cloud. Le README annonce la couleur, il s'agit de « démystifier le RAG en le construisant soi-même ». Le public visé est donc un développeur Node.js qui connaît déjà JavaScript et veut comprendre les embeddings, la recherche vectorielle et l'injection de contexte, pas un utilisateur qui veut brancher un chatbot sur ses PDF. La dépendance centrale est node-llama-cpp, ce qui signifie que les modèles tournent sur la machine, pas sur un serveur distant.
Le parcours en dix étapes et sa numérotation incohérente
Le dépôt s'organise en dossiers numérotés sous examples/, chacun contenant example.js, CODE.md et CONCEPT.md. On trouve ainsi 00_how_rag_works, 02_data_loading, 03_text_splitting_and_chunking, 04_intro_to_embeddings, 05_building_vector_store, puis 06_retrieval_strategies qui regroupe quatre sous-dossiers : 01_basic_retrieval, 02_query_preprocessing, 03_hybrid_search et 04_multi_query_retrieval. Deux remarques. D'abord, la numérotation saute de 00 à 02 : le dossier 01 n'apparaît pas dans le README fourni, ce qui laisse un trou dans la progression annoncée. Ensuite, l'étape « 7. Query Preprocessing » du README pointe vers examples/06_retrieval_strategies/02_query_preprocessing/, donc le numéro affiché dans le texte ne correspond pas au chemin réel. Ce n'est pas bloquant, mais cela oblige à se fier aux chemins plutôt qu'aux titres. Le README mentionne aussi un fichier showcase.js dans 01_basic_retrieval, présenté comme la mise en pratique de tout ce qui précède.
Ce que fait réellement le pipeline, étape par étape
Le README décrit une séquence en dix temps : définition des besoins de connaissance, chargement des documents, découpage en chunks, génération des embeddings, stockage vectoriel, récupération, re-classement après récupération, prétraitement de la requête et normalisation des vecteurs, augmentation, puis génération par un LLM local. Deux points méritent l'attention. Le premier est le découpage : le README parle explicitement de « chevauchements, frontières et stratégies de chunking », donc l'exemple ne se contente pas de couper tous les N caractères, il expose le compromis entre granularité fine et fenêtre de contexte. Le second est la normalisation des vecteurs, présentée comme un moyen de stabiliser les embeddings avant comparaison. C'est un détail qu'on oublie souvent : sans normalisation, la similarité cosinus et le produit scalaire ne donnent pas le même classement, et le comportement change selon le modèle d'embedding choisi.
Recherche hybride, multi-requêtes et fusion RRF
Les chapitres les plus intéressants sont les quatre de 06_retrieval_strategies. Le README mentionne BM25 combiné aux embeddings, avec une pondération entre les deux signaux. Il décrit aussi la décomposition d'une requête complexe en sous-requêtes, exécutées en parallèle, puis fusionnées. Trois méthodes de fusion sont nommées : RRF (reciprocal rank fusion), la fusion pondérée, et la déduplication des listes de résultats. Le README ajoute l'expansion de requête, la récupération par perspectives et une sélection adaptative de stratégie. C'est la partie la plus dense du dépôt, et c'est aussi celle où l'écart entre un exemple pédagogique et un système réel se creuse : la fusion de listes classées demande des choix de pondération qui dépendent du corpus, et le README ne fournit pas de valeurs par défaut justifiées. Il faut donc lire le code pour voir quels poids sont utilisés.
Mise en route : chemins et dépendance au modèle local
Le README ne donne pas de commande d'installation explicite. Ce qu'on peut affirmer depuis la structure : chaque exemple s'exécute via son fichier example.js, par exemple node examples/00_how_rag_works/example.js ou node examples/06_retrieval_strategies/04_multi_query_retrieval/example.js. La dépendance node-llama-cpp implique de disposer d'un modèle au format GGUF et de le référencer dans le code, mais le README fourni ne précise ni le nom du modèle, ni la clé de configuration, ni la commande de téléchargement. C'est une lacune réelle pour un dépôt d'apprentissage : la première barrière n'est pas le code, c'est l'obtention des poids. Avant de juger le reste, il faut donc ouvrir un example.js et vérifier comment le chemin du modèle y est déclaré.
La limite structurelle : un support d'apprentissage, pas un service
Le magasin vectoriel de l'étape 4 vit en mémoire, comme l'indique le chemin 05_building_vector_store/01_in_memory_store/. Rien dans le matériel fourni ne décrit de persistance sur disque, d'index approximatif type HNSW ou IVF, ni de serveur HTTP. La recherche par plus proche voisin y est donc probablement exhaustive : correcte pour quelques centaines de chunks, coûteuse au-delà. Autre point, le dépôt ne publie aucune release, seulement des pushs sur main, donc pas de version stable à épingler. Enfin, l'exécution locale suppose un modèle d'embedding et un modèle de génération sur la même machine, ce qui impose de la RAM et du temps de calcul. Pour indexer un corpus volumineux ou servir plusieurs utilisateurs, ce n'est pas le bon outil.
Face à LangChain.js : explicite contre assemblé
L'alternative la plus directe est LangChain.js, qui fournit des abstractions prêtes à l'emploi pour les chargeurs de documents, les découpeurs, les magasins vectoriels et les chaînes de récupération. La différence d'approche est nette. LangChain.js optimise le temps de mise en place : on assemble des composants existants et on passe à autre chose. pguso/rag-from-scratch fait l'inverse, il écrit chaque composant pour que le lecteur voie la similarité cosinus, le calcul de score et la fusion de listes. Le coût est assumé : moins de fonctionnalités, pas de connecteurs, pas de magasin persistant, mais un contrôle total sur ce qui se passe entre la requête et la réponse. Un développeur qui a déjà compris le pipeline n'a aucune raison de repartir de zéro ; quelqu'un qui subit des résultats de recherche sans les comprendre en a une.
Coût de maintenance et licence MIT
Le dépôt est sous licence MIT, ce qui autorise la réutilisation, la modification et la redistribution du code, y compris dans un projet propriétaire, à condition de conserver l'avis de copyright. Ce n'est pas un avis juridique, seulement la lecture de l'identifiant de licence fourni. Sur la maintenance : aucune release n'est publiée, le projet avance par commits sur main, et le dernier push date du 11 mars 2026. Il n'y a pas de version à épingler, donc si vous copiez du code, figez le commit que vous avez lu. La dépendance node-llama-cpp évolue avec les formats de modèles et les liaisons natives, ce qui signifie que le code des exemples peut demander des ajustements lors des mises à jour. Le coût réel n'est pas la lecture, c'est de garder les exemples exécutables quand la chaîne d'outils change.
Conclusion éditoriale
À adopter si vous voulez écrire vous-même chaque brique d'un pipeline RAG et lire le code qui produit les vecteurs, le chunking et le classement. À éviter si vous cherchez un service interrogeable : rien ici ne décrit un serveur, une persistance ni une gestion de montée en charge. Avant de vous engager, ouvrez examples/05_building_vector_store/01_in_memory_store/example.js et vérifiez que le modèle d'embedding utilisé est bien téléchargé en local, car c'est cette étape qui conditionne tout le reste du parcours.
Notes de la communauté