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 :
mkdir mon-moteur-semantique
cd mon-moteur-semantique
python3 -m venv venv
source venv/bin/activate # macOS/Linux
# ou venv\Scripts\activate sur WindowsInstalle les deux seules dépendances :
pip install chromadb sentence-transformersDeux 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 :
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 :
python3 decouverte.pyTu 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 :
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 :
python3 premier_chromadb.pyÉtape 4 : Ajouter des documents avec leurs embeddings
Passons aux choses sérieuses. Crée indexer_documents.py :
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 :
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 :
python3 indexer_documents.pyChaque 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 :
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 :
python3 rechercher.pyCe 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 :
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 :
#!/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 :
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.mdIndexe-les :
python3 moteur_recherche.py indexer test_notes/Et lance une recherche :
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
# ❌ 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
# ❌ 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
# ❌ 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
# ❌ 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
# ❌ 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
pip install chromadb
chroma run --path ./ma_base --port 8000Puis 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 !

