Bienvenue sur CFTPfr.fr › Forums › recherche_semantique.py — RAG léger pour les spécialités
- Ce sujet est vide.
-
AuteurMessages
-
01.08.2026 à 18h19 #260
Mario Da Conceicao
Maître des clésNouveau composant du 01/08/2026 — recherche sémantique (par sens) pour remplacer la recherche par mot-clé simple de gestionnaire_specialites.py. Basé sur une recherche des meilleures pratiques actuelles (RAG léger, sans dépendance lourde type base de données vectorielle externe — volontairement gardé simple et autonome pour rester cohérent avec un système minimal).
Principe
Chaque paragraphe d’un document de spécialité est transformé en une "empreinte numérique" qui capture son sens (via un petit modèle d’embeddings, ex: nomic-embed-text, ~140M paramètres, compatible llama.cpp/GGUF — assez léger pour tourner à côté du modèle principal). Une question posée à MIRA est transformée de la même façon, puis on cherche les passages dont l’empreinte est la plus proche — donc par SENS, pas par mots identiques.
import json from pathlib import Path import math class RechercheSemantique: """ Recherche par sens (RAG léger) pour les spécialités de MIRA. Remplace la recherche par mot-clé simple par une vraie compréhension du sens de la question, sans dépendance lourde (pas de base de données vectorielle externe — un simple fichier JSON suffit à notre échelle). """ def __init__(self, moteur_embeddings): """ moteur_embeddings : objet exposant une méthode encoder(texte) -> vecteur, fourni par le moteur llama.cpp une fois configuré avec un modèle d'embeddings léger (ex: nomic-embed-text). """ self.moteur = moteur_embeddings def _cosinus(self, v1, v2): """Mesure à quel point deux empreintes de sens se ressemblent (0 à 1).""" produit = sum(a * b for a, b in zip(v1, v2)) norme1 = math.sqrt(sum(a * a for a in v1)) norme2 = math.sqrt(sum(b * b for b in v2)) if norme1 == 0 or norme2 == 0: return 0 return produit / (norme1 * norme2) def indexer_documents(self, dossier_documents, fichier_index): """ Découpe chaque document d'une spécialité en paragraphes, calcule leur empreinte de sens, et sauvegarde l'index. À relancer seulement quand un document change (pas à chaque question). """ index = [] for fichier in Path(dossier_documents).glob("*.txt"): contenu = fichier.read_text(encoding="utf-8", errors="replace") paragraphes = [p.strip() for p in contenu.split("\n\n") if p.strip()] for paragraphe in paragraphes: vecteur = self.moteur.encoder(paragraphe) index.append({ "fichier": fichier.name, "texte": paragraphe, "vecteur": vecteur }) with open(fichier_index, "w", encoding="utf-8") as f: json.dump(index, f) return len(index) def chercher(self, question, fichier_index, top_n=3): """ Trouve les passages les plus proches du SENS de la question posée, pas seulement ceux qui contiennent les mêmes mots. """ with open(fichier_index, "r", encoding="utf-8") as f: index = json.load(f) vecteur_question = self.moteur.encoder(question) resultats = [] for entree in index: score = self._cosinus(vecteur_question, entree["vecteur"]) resultats.append((score, entree)) resultats.sort(key=lambda x: x[0], reverse=True) return [r[1] for r in resultats[:top_n]]Recommandation sur la qualité du modèle principal
Comme la vitesse n’est pas une contrainte pour ce projet, on installera Phi-4 Mini dans une version moins compressée que la normale (Q6 ou Q8 plutôt que Q4) — plus lente, mais plus fidèle. C’est cohérent avec la priorité donnée à la précision plutôt qu’à la rapidité.
Ce qui reste volontairement à nous, pas copié
Contrairement aux solutions grand public (Ollama, LM Studio, de grosses piles avec bases de données vectorielles séparées), cette version reste 100% intégrée à l’architecture déjà posée (spécialités, garde-fou, adaptation matérielle) — pas une couche ajoutée par-dessus un outil générique, mais une pièce pensée spécifiquement pour MIRA.
-
AuteurMessages
- Vous devez être connecté pour répondre à ce sujet.