Chatbot — moteur RAG + Ollama
backend/src/services/rag.service.js + chatbot.service.js + chatbot.controller.js + knowledge.controller.js + frontend/src/components/ChatbotWidget.vue + frontend/src/views/backoffice/KnowledgeManagerView.vue.
Vérifié le 2026-08-13.
Dans backend/server.js, le montage de chatbotRoutes sur /api/chatbot et l'initialisation de chatbotService au démarrage sont commentés. POST /api/chatbot/ask — pourtant appelé par ChatbotWidget.vue, qui lui reste actif dans l'UI — répond donc 404 en l'état de ce dépôt. C'est volontaire : le chatbot n'est pas encore mis en service, ce n'est pas une régression à corriger en urgence. Vérifier l'état de server.js avant de supposer que le chatbot fonctionne, et coordonner avec l'équipe avant de le réactiver (Ollama doit être accessible avec les modèles nomic-embed-text/qwen2.5:1.5b déjà tirés, sinon le serveur logue des échecs en boucle au démarrage).
1. Deux moteurs NLP coexistent — un seul est réellement branché
Le message affiché au démarrage du serveur ("NLP Model Trained Successfully with 26 intents", voir README backend) correspond à un ancien moteur (@nlpjs/basic + @nlpjs/lang-fr), avec des intents statiques dans backend/src/data/chatbot-training.json (26 entrées {intent, utterances, answer}) et un modèle compilé model.nlp à la racine du dépôt. Rien dans le code actuel ne charge ni n'entraîne ce fichier — aucune référence à NlpManager/@nlpjs dans src/.
Le moteur réellement actif est un système RAG (Retrieval-Augmented Generation) appuyé sur Ollama, entièrement différent, décrit ci-dessous. Le message de démarrage du README est trompeur — il documente un système qui n'est plus celui qui répond aux utilisateurs.
2. Architecture RAG (rag.service.js)
- Embeddings : Ollama, modèle
nomic-embed-text. Génération de texte : Ollama, modèleqwen2.5:1.5b. Hôte/modèles configurables viaOLLAMA_HOST/OLLAMA_TEXT_MODEL/OLLAMA_EMBED_MODEL. - Source de connaissance : table
knowledge_document(PostgreSQL) —title, content, embedding (Json), category, metadata. Pas de fichiers, une base éditée depuis le backoffice (section 3). initialize()charge tous les documents en cache RAM, génère les embeddings manquants (persistés en base), puis pré-charge le modèle texte (keep_alive: '24h').
Flux d'une question (askQuestion)
- Fast-path sans LLM pour salutations/remerciements/insultes/questions hors-domaine.
- Cache réponse (clé = question normalisée).
- Enrichissement contextuel avec les 2 derniers messages si la question est courte et ressemble à un suivi.
- Recherche hybride : mots-clés + synonymes bancaires d'abord (rapide, court-circuite l'appel embedding si le score dépasse un seuil), embeddings cosinus en repli.
- Réponse directe sans LLM si le meilleur document dépasse un seuil de pertinence par mots-clés.
- Sinon, appel au LLM Ollama avec un prompt contraint (français, 2-3 phrases, contexte Togo uniquement, historique de conversation inclus).
Donc : mots-clés d'abord, embeddings en repli, LLM en dernier recours — pas node-nlp dans ce flux réel.
Mémoire de conversation
En RAM uniquement (Map<sessionId, {...}>), pas en base — 6 messages max par session, expiration 15 min, nettoyage périodique. sessionId = l'adresse IP du client (pas un identifiant utilisateur réel) — deux utilisateurs derrière le même NAT/proxy partagent la même mémoire de conversation.
3. Gestion de la base de connaissance (backoffice)
knowledge.controller.js (CRUD REST protégé JWT) sur knowledge_document. KnowledgeManagerView.vue est un formulaire simple titre/catégorie/contenu — pas d'utterances/intents (ce concept appartient à l'ancien moteur @nlpjs, abandonné).
Prise en compte à chaud : créer/modifier/supprimer une entrée appelle immédiatement les fonctions correspondantes de rag.service.js, qui synchronisent le cache RAM et invalident le cache de réponses — aucun redémarrage serveur nécessaire, contrairement à ce qu'on pourrait attendre d'un système nécessitant un "réentraînement".
4. ChatbotWidget.vue (frontend public)
Historique de conversation gardé uniquement en mémoire du composant Vue — perdu au rafraîchissement de page, pas de persistance locale. Réponse complète (pas de streaming), timeout client de 120 secondes. Couleur du widget configurable via site-settings/CHATBOT_COLOR.
Pièges connus pour un futur développeur
- Vérifier
server.jsavant tout — le chatbot peut être silencieusement désactivé (section "Le chatbot est actuellement désactivé", ci-dessus) sans qu'aucune erreur visible n'indique pourquoi le widget ne répond pas. - Ne pas confondre les deux moteurs NLP —
chatbot-training.json/model.nlp(ancien, inactif) vsknowledge_document/RAG (actuel, actif). Le README backend décrit encore l'ancien. sessionId= IP client — en environnement avec NAT/proxy partagé (ex. plusieurs postes d'une même agence), plusieurs utilisateurs peuvent se retrouver avec le même contexte de conversation.