iancr
ChromaDB : Construis ton moteur de recherche sémantique

ChromaDB : Construis ton moteur de recherche sémantique

Tu tapes « recette gâteau chocolat » dans Google. Google ne cherche pas les pages qui contiennent exactement ces mots. Il comprend que « moelleux au cacao » et « fondant choco » parlent de la même chose. C'est la magie de la recherche sémantique. Aujourd'hui, tu vas construire la tienne, en 20 minutes, avec ChromaDB.

Le problème : quand les mots-clés ne suffisent plus

Imagine une base de documents sur l'intelligence artificielle. Un utilisateur tape « voiture autonome ». Une recherche classique (Ctrl+F) ne trouve que les fichiers qui contiennent littéralement « voiture autonome ». Elle rate « conduite automatique », « véhicule sans chauffeur », « self-driving car ».

La recherche sémantique résout ça. Elle transforme les mots en vecteurs mathématiques (des listes de 384 ou 768 nombres) et mesure la distance entre ces vecteurs. Deux phrases qui parlent de la même chose auront des vecteurs proches, même si elles n'utilisent pas les mêmes mots.

Et ChromaDB, c'est la base de données qui rend tout ça simple.

Qu'est-ce que ChromaDB ?

ChromaDB est une base de données vectorielle open-source, développée par l'équipe Chroma. Elle stocke des documents avec leurs représentations mathématiques (les embeddings) et permet de retrouver les plus pertinents en une fraction de seconde.

Parmi ses concurrents (Pinecone, Weaviate, Qdrant), ChromaDB a un avantage massif : elle s'installe en un `pip install` et fonctionne sans serveur, sans Docker, sans inscription.

Elle est devenue la base vectorielle préférée des développeurs qui construisent des applications RAG (Retrieval-Augmented Generation) ces chatbots qui « lisent » tes documents avant de répondre.

Étape 1 : Installation

Ouvre un terminal et crée un dossier pour le projet :

bash
mkdir mon-moteur-semantique cd mon-moteur-semantique python3 -m venv venv source venv/bin/activate # macOS/Linux # ou venv\Scripts\activate sur Windows

Installe les deux seules dépendances :

bash
pip install chromadb sentence-transformers

Deux bibliothèques, c'est tout. chromadb pour la base vectorielle, sentence-transformers pour créer les vecteurs (les embeddings) à partir du texte.

Étape 2 : Ton premier embedding

Avant de plonger dans la base de données, comprends ce qu'est un embedding.

Crée un fichier decouverte.py :

python
from sentence_transformers import SentenceTransformer # Charge le modèle (téléchargé une seule fois, puis mis en cache) modele = SentenceTransformer("all-MiniLM-L6-v2") # Trois phrases phrases = [ "Le chat dort sur le canapé", "Le félin fait la sieste sur le sofa", "La bourse de New York a clôturé en hausse" ] # Transforme chaque phrase en vecteur for phrase in phrases: vecteur = modele.encode(phrase) print(f"Phrase : '{phrase}'") print(f"Vecteur : [{vecteur[0]:.4f}, {vecteur[1]:.4f}, ..., {vecteur[-1]:.4f}]") print(f"Dimension : {len(vecteur)} nombres\n")

Exécute-le :

bash
python3 decouverte.py

Tu vois que chaque phrase devient une liste de 384 nombres. Les phrases 1 et 2 (« chat dort » / « félin fait la sieste ») produiront des vecteurs proches (tu peux le vérifier avec la similarité cosinus), alors que la phrase 3 sur la bourse sera très éloignée.

💡 Illustration : Un graphique en 2D (projection t-SNE) montrant 3 groupes de points colorés un pour les chats, un pour la finance, un éloigné.

Étape 3 : Ta première collection ChromaDB

Maintenant, stockons ces embeddings dans ChromaDB pour les interroger.

Crée premier_chromadb.py :

python
import chromadb # Crée un client (mode mémoire pour commencer) client = chromadb.Client() # Crée une collection (= une table dans une BDD classique) collection = client.create_collection( name="mes_documents", metadata={"description": "Ma première collection"} ) print(f"✅ Collection '{collection.name}' créée !") print(f"📊 Documents actuellement : {collection.count()}")

Exécute :

bash
python3 premier_chromadb.py

Étape 4 : Ajouter des documents avec leurs embeddings

Passons aux choses sérieuses. Crée indexer_documents.py :

python
import chromadb from sentence_transformers import SentenceTransformer # Initialisation client = chromadb.Client() modele = SentenceTransformer("all-MiniLM-L6-v2") # Supprime la collection si elle existe déjà (pratique pour les tests) try: client.delete_collection("articles_ia") print("🗑️ Ancienne collection supprimée") except: pass collection = client.create_collection(name="articles_ia") # Nos documents à indexer documents = [ "ChatGPT peut générer du code Python en quelques secondes.", "Stable Diffusion crée des images à partir de descriptions textuelles.", "YOLO détecte des objets en temps réel dans une vidéo.", "CrewAI permet d'orchestrer des agents IA autonomes.", "Ollama fait tourner Llama 3 et Mistral en local sans cloud.", "Whisper transcrit l'audio en texte avec une précision bluffante.", "LangChain facilite la création de pipelines RAG pour chatbots.", "FastAPI permet de servir un modèle d'IA via une API REST.", "PyTorch est le framework de deep learning le plus utilisé en recherche.", "Le football est le sport le plus populaire au monde." ] ids = [f"doc_{i}" for i in range(len(documents))] # ChromaDB peut calculer les embeddings automatiquement # grâce à sentence-transformers intégré collection.add( documents=documents, ids=ids ) print(f"✅ {collection.count()} documents indexés !")

Mais ChromaDB a besoin qu'on lui passe une fonction d'embedding. Ajoutons-la :

python
import chromadb from chromadb.utils import embedding_functions # Initialisation avec la fonction d'embedding embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction( model_name="all-MiniLM-L6-v2" ) client = chromadb.Client() try: client.delete_collection("articles_ia") except: pass collection = client.create_collection( name="articles_ia", embedding_function=embedding_fn ) # Mêmes documents qu'avant documents = [ "ChatGPT peut générer du code Python en quelques secondes.", "Stable Diffusion crée des images à partir de descriptions textuelles.", "YOLO détecte des objets en temps réel dans une vidéo.", "CrewAI permet d'orchestrer des agents IA autonomes.", "Ollama fait tourner Llama 3 et Mistral en local sans cloud.", "Whisper transcrit l'audio en texte avec une précision bluffante.", "LangChain facilite la création de pipelines RAG pour chatbots.", "FastAPI permet de servir un modèle d'IA via une API REST.", "PyTorch est le framework de deep learning le plus utilisé en recherche.", "Le football est le sport le plus populaire au monde.", "GPT-4o excelle en raisonnement mathématique et en analyse d'images.", "Hugging Face héberge plus de 500 000 modèles open-source." ] collection.add( documents=documents, ids=[f"doc_{i}" for i in range(len(documents))] ) print(f"✅ {collection.count()} documents indexés !")

Exécute :

bash
python3 indexer_documents.py

Chaque document est maintenant stocké avec son embedding (un vecteur de 384 nombres). ChromaDB a calculé ces embeddings automatiquement.

Étape 5 : La recherche sémantique en action

Crée rechercher.py :

python
import chromadb from chromadb.utils import embedding_functions embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction( model_name="all-MiniLM-L6-v2" ) client = chromadb.Client() collection = client.get_collection( name="articles_ia", embedding_function=embedding_fn ) # Requêtes de test requetes = [ "Comment générer des images avec une IA ?", "Je veux faire du traitement du langage naturel", "Quel outil pour déployer un modèle en ligne ?", "Résultats du dernier match de foot" ] for requete in requetes: print(f"\n🔍 Recherche : '{requete}'") print("-" * 50) resultats = collection.query( query_texts=[requete], n_results=3 ) for i, doc in enumerate(resultats["documents"][0]): distance = resultats["distances"][0][i] score = 1 - distance # Convertit distance en similarité print(f" #{i+1} [score: {score:.3f}] {doc}") print("\n" + "=" * 50) print("✨ Magique, non ? Même sans les mots exacts, les bons documents remontent !")

Exécute :

bash
python3 rechercher.py

Ce que tu dois observer :

Pour « Comment générer des images avec une IA ? », le document sur Stable Diffusion arrive en premier alors qu'il ne contient ni « générer » ni « image » (textuelles, mais pas « image » seul). ChromaDB a compris le sens, pas juste les mots.

Pour « Résultats du dernier match de foot », le document sur le football remonte il n'y a ni « match » ni « résultats » dedans, mais le sens est proche.

Étape 6 : Persistance garder tes données entre deux lancements

Jusqu'ici, tout est en mémoire. Relance le script et pouf, tout disparaît. Voici comment sauvegarder :

python
import chromadb from chromadb.utils import embedding_functions embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction( model_name="all-MiniLM-L6-v2" ) # Client persistant : les données sont sauvegardées dans ./chroma_data client = chromadb.PersistentClient(path="./chroma_data") collection = client.get_or_create_collection( name="articles_ia", embedding_function=embedding_fn ) print(f"📂 Données persistées dans ./chroma_data/") print(f"📊 {collection.count()} documents dans la collection") # Si la collection est vide, on l'alimente if collection.count() == 0: documents = [ "ChatGPT peut générer du code Python en quelques secondes.", "Stable Diffusion crée des images à partir de descriptions textuelles.", "YOLO détecte des objets en temps réel dans une vidéo.", "CrewAI permet d'orchestrer des agents IA autonomes.", "Ollama fait tourner Llama 3 et Mistral en local sans cloud.", "Whisper transcrit l'audio en texte avec une précision bluffante.", "LangChain facilite la création de pipelines RAG pour chatbots.", "FastAPI permet de servir un modèle d'IA via une API REST.", "PyTorch est le framework de deep learning le plus utilisé en recherche.", "Le football est le sport le plus populaire au monde.", "GPT-4o excelle en raisonnement mathématique et en analyse d'images.", "Hugging Face héberge plus de 500 000 modèles open-source." ] collection.add( documents=documents, ids=[f"doc_{i}" for i in range(len(documents))] ) print(f"✅ {collection.count()} documents indexés !")

Étape 7 : Projet final Un moteur de recherche pour tes notes

Voici l'application complète. Elle lit un dossier de fichiers texte, les indexe, et te permet de faire des recherches naturelles.

Crée moteur_recherche.py :

python
#!/usr/bin/env python3 """ Moteur de recherche sémantique sur tes fichiers texte. Usage : python3 moteur_recherche.py indexer dossier/ python3 moteur_recherche.py chercher "ta question" """ import sys import os import chromadb from chromadb.utils import embedding_functions DB_PATH = "./ma_base_semantique" COLLECTION_NAME = "mes_notes" embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction( model_name="all-MiniLM-L6-v2" ) client = chromadb.PersistentClient(path=DB_PATH) collection = client.get_or_create_collection( name=COLLECTION_NAME, embedding_function=embedding_fn ) def indexer_dossier(chemin_dossier): """Parcourt un dossier et indexe tous les fichiers .txt et .md""" fichiers_indexes = 0 for racine, _, fichiers in os.walk(chemin_dossier): for nom_fichier in fichiers: if not nom_fichier.endswith((".txt", ".md")): continue chemin_complet = os.path.join(racine, nom_fichier) try: with open(chemin_complet, "r", encoding="utf-8") as f: contenu = f.read().strip() except Exception as e: print(f"⚠️ Erreur lecture {chemin_complet}: {e}") continue if not contenu: continue # Ajoute au lot (on groupe pour la performance) collection.add( documents=[contenu[:2000]], # Tronque les fichiers trop longs metadatas=[{"source": chemin_complet}], ids=[chemin_complet] ) fichiers_indexes += 1 print(f" 📄 {chemin_complet}") print(f"\n✅ {fichiers_indexes} fichiers indexés dans '{COLLECTION_NAME}'") def chercher(requete, n=5): """Recherche sémantique""" print(f"\n🔍 Recherche : '{requete}'") print("=" * 60) resultats = collection.query( query_texts=[requete], n_results=n ) for i, doc in enumerate(resultats["documents"][0]): distance = resultats["distances"][0][i] score = (1 - distance) * 100 source = resultats["metadatas"][0][i]["source"] apercu = doc[:150].replace("\n", " ") + ("..." if len(doc) > 150 else "") print(f"\n #{i+1} — {score:.1f}% de pertinence") print(f" 📁 {source}") print(f" 💬 {apercu}") def afficher_stats(): """Affiche les statistiques de la base""" print(f"\n📊 Statistiques de la base") print(f" Collection : {COLLECTION_NAME}") print(f" Documents : {collection.count()}") print(f" Stockage : {DB_PATH}/") if __name__ == "__main__": if len(sys.argv) < 2: print("Usage :") print(" python3 moteur_recherche.py indexer <dossier>") print(" python3 moteur_recherche.py chercher <requete>") print(" python3 moteur_recherche.py stats") sys.exit(1) commande = sys.argv[1] if commande == "indexer": if len(sys.argv) < 3: print("❌ Spécifie un dossier à indexer") sys.exit(1) indexer_dossier(sys.argv[2]) elif commande == "chercher": if len(sys.argv) < 3: print("❌ Spécifie une requête") sys.exit(1) chercher(" ".join(sys.argv[2:])) elif commande == "stats": afficher_stats() else: print(f"❌ Commande inconnue : {commande}")

Testons-le. Crée quelques fichiers de test :

bash
mkdir -p test_notes echo "Python est un langage de programmation très populaire pour l'intelligence artificielle et le deep learning. On l'utilise avec PyTorch et TensorFlow." > test_notes/python.txt echo "Le café est une boisson préparée à partir de grains torréfiés. L'espresso est une méthode d'extraction sous pression." > test_notes/cafe.txt echo "La photosynthèse est le processus par lequel les plantes convertissent la lumière en énergie chimique." > test_notes/photosynthese.txt echo "Docker permet de conteneuriser des applications pour les déployer facilement sur n'importe quel serveur." > test_notes/docker.txt echo "Les réseaux de neurones convolutifs (CNN) sont excellents pour la classification d'images et la vision par ordinateur." > test_notes/cnn.md

Indexe-les :

bash
python3 moteur_recherche.py indexer test_notes/

Et lance une recherche :

bash
python3 moteur_recherche.py chercher "intelligence artificielle et programmation" python3 moteur_recherche.py chercher "comment faire pousser des légumes" python3 moteur_recherche.py chercher "déploiement et conteneurs"

Tu verras que : - « intelligence artificielle et programmation » python.txt en premier, puis cnn.md - « comment faire pousser des légumes » photosynthese.txt (le concept est proche !) - « déploiement et conteneurs » docker.txt avec un score élevé

🚨 Pièges classiques (et comment les éviter)

Piège 1 : Collection déjà existante

python
# ❌ Erreur si la collection existe déjà collection = client.create_collection(name="articles_ia") # ✅ Utilise get_or_create_collection collection = client.get_or_create_collection(name="articles_ia")

Piège 2 : IDs en doublon

python
# ❌ Ajouter deux documents avec le même ID écrase le premier collection.add(documents=["doc A", "doc B"], ids=["id1", "id1"]) # ✅ Utilise des IDs uniques (uuid, hash, chemin de fichier...)

Piège 3 : Mode mémoire vs persistant

python
# ❌ Les données disparaissent au redémarrage client = chromadb.Client() # ✅ Utilise PersistentClient pour garder les données client = chromadb.PersistentClient(path="./ma_base")

Piège 4 : Embedding function oubliée

python
# ❌ La recherche retourne des résultats inattendus collection = client.get_collection(name="articles_ia") # ✅ Passe toujours la même embedding function qu'à l'indexation collection = client.get_collection( name="articles_ia", embedding_function=embedding_fn )

Piège 5 : Métadonnées non indexées

python
# ❌ Les métadonnées seules ne sont pas recherchables sémantiquement collection.add(documents=[], metadatas=[{"auteur": "Marie"}], ids=["1"]) # ✅ ChromaDB 0.5+ supporte le filtrage par métadonnées resultats = collection.query( query_texts=["deep learning"], where={"auteur": "Marie"} # Filtre supplémentaire )

Pour aller plus loin

1. Intégrer ChromaDB dans un chatbot RAG

Utilise LangChain ou LlamaIndex pour brancher ChromaDB à un LLM. Le chatbot récupère d'abord les documents pertinents dans ChromaDB, puis les injecte dans le prompt du LLM. Résultat : un assistant qui « connaît » tous tes documents.

2. Utiliser des embeddings multilingues

Remplace all-MiniLM-L6-v2 par paraphrase-multilingual-MiniLM-L12-v2. Tes recherches en français trouveront des documents en anglais, et vice-versa. Magique pour les équipes internationales.

3. Déployer ChromaDB en mode serveur

bash
pip install chromadb chroma run --path ./ma_base --port 8000

Puis connecte-toi avec chromadb.HttpClient(host="localhost", port=8000). Idéal pour partager la base entre plusieurs applications.

4. Combiner avec un scraper web

Scrape des articles, indexe-les automatiquement, et construis ton propre moteur de veille technologique personnalisé qui « comprend » vraiment ce que tu cherches.


💡 Idée d'illustration : Un schéma en deux parties à gauche, une recherche classique « Ctrl+F » qui trouve uniquement le mot exact ; à droite, un nuage de points colorés avec ChromaDB qui regroupe les concepts proches. Légende : « La recherche sémantique comprend le sens, pas juste les mots. »

Tu as maintenant un moteur de recherche sémantique fonctionnel sur ta machine. Pas de cloud, pas d'abonnement, pas de limite. Et c'est la brique de base pour construire des assistants RAG, des systèmes de recommandation, ou des chatbots qui « lisent » vraiment tes documents. Bon code !