Aller au contenu
Développement medium

ChromaDB : base de données vectorielle pour Python

21 min de lecture

logo chroma

ChromaDB est une base de données vectorielle open source qui stocke vos documents sous forme d’embeddings et permet la recherche sémantique en quelques lignes de Python. Dans ce guide, vous allez créer votre première collection, indexer des documents et interroger ChromaDB — le tout avec du code testé sur la version 1.5.0.

ChromaDB se distingue par sa simplicité d’utilisation : pas besoin de serveur externe pour commencer, les embeddings sont générés automatiquement si vous ne fournissez pas les vôtres, et l’API Python est intuitive.

CaractéristiqueCe que ChromaDB offre
Démarrage rapidepip install chromadb + 5 lignes de code
Embeddings autoModèle par défaut (all-MiniLM-L6-v2) inclus
Persistance nativeStockage SQLite local, pas de serveur requis
Filtrage métadonnéesRequêtes combinant sémantique + attributs
Full-text search$contains, $regex sur les documents

Cas d’usage typiques :

  • RAG : stocker les chunks de documentation pour un assistant
  • Recherche sémantique : trouver des articles similaires
  • Mémoire d’agent : historique de conversations pour LLM
  • Recommandations : produits ou contenus similaires
Fenêtre de terminal
# Python 3.10+ requis
python3 --version
# Créer un environnement dédié
python3 -m venv chroma-env
source chroma-env/bin/activate # Linux/Mac
# Installer ChromaDB
pip install chromadb

Vérifiez l’installation :

1.5.0
import chromadb
print(f"ChromaDB version: {chromadb.__version__}")

ChromaDB propose trois façons de se connecter selon votre contexte :

Le plus simple pour expérimenter. Les données restent en mémoire et sont perdues à l’arrêt du script.

import chromadb
# Client éphémère — idéal pour les notebooks
client = chromadb.Client()
# Créer une collection de test
collection = client.create_collection("test")
print(f"Collection créée: {collection.name}")

Les données sont sauvegardées sur disque dans un dossier SQLite. C’est le mode recommandé pour le développement local et les petits projets.

import chromadb
# Les données seront sauvées dans ./chroma_db/
client = chromadb.PersistentClient(path="./chroma_db")
# La collection persiste entre les exécutions
collection = client.get_or_create_collection("docs")
print(f"Documents: {collection.count()}")

Pour les déploiements multi-utilisateurs, lancez ChromaDB comme serveur :

Fenêtre de terminal
# Démarrer le serveur (port 8000 par défaut)
chroma run --host 0.0.0.0 --port 8000 --path ./chroma_server

Puis connectez-vous depuis votre application :

import chromadb
client = chromadb.HttpClient(host="localhost", port=8000)
collection = client.get_or_create_collection("production_docs")

Une collection dans ChromaDB est l’équivalent d’une table. Elle contient vos documents, leurs embeddings et les métadonnées associées.

import chromadb
client = chromadb.PersistentClient(path="./chroma_db")
# Créer une collection simple
collection = client.create_collection(
name="runbooks",
metadata={
"description": "Documentation opérationnelle",
"hnsw:space": "cosine" # Distance : cosine, l2, ou ip
}
)
print(f"Collection '{collection.name}' créée")

Le paramètre hnsw:space définit la métrique de distance :

MétriqueUsageNotes
cosineTexte, embeddings normalisésRecommandé par défaut
l2Distance euclidienneEmbeddings non normalisés
ipProduit scalaireCas spécifiques
# Lister toutes les collections
collections = client.list_collections()
for coll in collections:
print(f"- {coll.name}: {coll.count()} documents")
# Récupérer une collection existante
collection = client.get_collection("runbooks")
# Récupérer ou créer si n'existe pas
collection = client.get_or_create_collection("runbooks")
# Attention : suppression irréversible !
client.delete_collection("collection_obsolete")

Vous pouvez ajouter des documents de deux façons : en laissant ChromaDB générer les embeddings automatiquement, ou en fournissant vos propres vecteurs.

C’est le cas le plus simple. ChromaDB utilise le modèle all-MiniLM-L6-v2 par défaut.

# Ajouter des documents avec embeddings auto-générés
collection.add(
ids=["doc1", "doc2", "doc3"],
documents=[
"Kubernetes orchestre les conteneurs à grande échelle",
"Docker permet de créer des images de conteneurs",
"Ansible automatise la configuration des serveurs"
],
metadatas=[
{"source": "runbook-k8s.md", "category": "orchestration"},
{"source": "guide-docker.md", "category": "conteneurs"},
{"source": "guide-ansible.md", "category": "automation"}
]
)
print(f"Documents ajoutés: {collection.count()}")

Ce qui se passe :

  1. ChromaDB charge le modèle d’embedding (premier appel uniquement)
  2. Chaque document est transformé en vecteur de 384 dimensions
  3. Les vecteurs sont indexés dans un index HNSW
  4. Les documents et métadonnées sont stockés pour être retournés

Si vous utilisez un autre modèle (OpenAI, Voyage, E5…), passez directement les vecteurs :

from sentence_transformers import SentenceTransformer
# Charger votre modèle
model = SentenceTransformer("intfloat/multilingual-e5-small")
documents = [
"Kubernetes gère les pods et les services",
"Terraform provisionne l'infrastructure cloud"
]
# Générer les embeddings vous-même
embeddings = model.encode(documents).tolist()
# Ajouter avec embeddings pré-calculés
collection.add(
ids=["doc4", "doc5"],
documents=documents,
embeddings=embeddings,
metadatas=[
{"source": "k8s.md"},
{"source": "terraform.md"}
]
)

upsert insère les nouveaux documents et met à jour les existants (par ID) :

# Si doc1 existe, il sera mis à jour ; sinon, il sera créé
collection.upsert(
ids=["doc1", "doc6"],
documents=[
"Kubernetes v1.29 orchestre les conteneurs (mis à jour)",
"Nouveau document sur Helm"
],
metadatas=[
{"source": "runbook-k8s.md", "version": "1.29"},
{"source": "guide-helm.md"}
]
)
# Mettre à jour uniquement doc1
collection.update(
ids=["doc1"],
documents=["Kubernetes v1.30 orchestre les conteneurs"],
metadatas=[{"source": "runbook-k8s.md", "version": "1.30"}]
)
# Par IDs
collection.delete(ids=["doc1", "doc2"])
# Par filtre sur les métadonnées
collection.delete(where={"category": "obsolete"})
# Par filtre sur le contenu
collection.delete(where_document={"$contains": "deprecated"})

La méthode query effectue une recherche par similarité : elle retourne les documents dont le sens est le plus proche de votre requête.

results = collection.query(
query_texts=["comment déployer une application"],
n_results=3
)
# Afficher les résultats
for i, (doc, dist) in enumerate(zip(
results['documents'][0],
results['distances'][0]
)):
print(f"{i+1}. (distance: {dist:.4f}) {doc[:80]}...")

Sortie typique :

1. (distance: 0.1056) Kubernetes orchestre les conteneurs à grande échelle...
2. (distance: 0.1089) Docker permet de créer des images de conteneurs...
3. (distance: 0.1368) Terraform provisionne l'infrastructure cloud...

Par défaut, query retourne documents, metadatas et distances. Utilisez include pour personnaliser :

results = collection.query(
query_texts=["monitoring kubernetes"],
n_results=5,
include=["documents", "metadatas", "distances", "embeddings"]
)
# Les embeddings sont maintenant inclus
print(f"Embedding dimension: {len(results['embeddings'][0][0])}")
# Embedding dimension: 384

Si vous avez généré l’embedding de la requête vous-même :

query_embedding = model.encode(["comment monitorer un cluster"]).tolist()
results = collection.query(
query_embeddings=query_embedding,
n_results=5
)

Combinez la recherche sémantique avec des filtres sur les métadonnées pour des résultats plus précis.

OpérateurDescriptionExemple
$eqÉgal (implicite){"level": "advanced"}
$neDifférent{"status": {"$ne": "draft"}}
$gt, $gtePlus grand (ou égal){"score": {"$gte": 0.8}}
$lt, $ltePlus petit (ou égal){"date": {"$lt": "2024-01-01"}}
$inDans la liste{"tag": {"$in": ["k8s", "docker"]}}
$ninPas dans la liste{"env": {"$nin": ["dev", "test"]}}
# Filtrer par valeur exacte
results = collection.query(
query_texts=["déploiement"],
n_results=5,
where={"source": "runbook-k8s.md"}
)
# Filtrer avec opérateur
results = collection.query(
query_texts=["sécurité"],
n_results=5,
where={"score": {"$gte": 0.7}}
)
# Filtrer avec $in
results = collection.query(
query_texts=["automatisation"],
n_results=5,
where={"category": {"$in": ["automation", "orchestration"]}}
)
# Combiner avec $and
results = collection.query(
query_texts=["configuration"],
n_results=5,
where={
"$and": [
{"category": "automation"},
{"level": {"$in": ["intermediate", "advanced"]}}
]
}
)
# Combiner avec $or
results = collection.query(
query_texts=["conteneurs"],
n_results=5,
where={
"$or": [
{"source": "guide-docker.md"},
{"source": "runbook-k8s.md"}
]
}
)

Vous pouvez stocker des listes dans les métadonnées et filtrer avec $contains :

# Ajouter un document avec des tags (tableau)
collection.upsert(
ids=["multi-tags"],
documents=["Guide complet Docker, Kubernetes et CI/CD"],
metadatas=[{
"tags": ["docker", "kubernetes", "cicd"],
"versions": [1, 2, 3]
}]
)
# Filtrer sur un élément du tableau
results = collection.get(
where={"tags": {"$contains": "kubernetes"}}
)
print(f"Documents avec tag kubernetes: {len(results['ids'])}")

En plus de la recherche sémantique, ChromaDB permet de filtrer par contenu textuel exact.

# Documents contenant exactement "Kubernetes"
results = collection.get(
where_document={"$contains": "Kubernetes"}
)
# Combiné avec query sémantique
results = collection.query(
query_texts=["déploiement"],
n_results=5,
where_document={"$contains": "kubectl"}
)
# Insensible à la casse avec regex
results = collection.get(
where_document={"$regex": "(?i)kubernetes"}
)
# Trouver des patterns spécifiques (ex: versions)
results = collection.get(
where_document={"$regex": "v1\\.[0-9]+"}
)
# Sémantique + métadonnées + contenu
results = collection.query(
query_texts=["sécurité"],
n_results=5,
where={"source": "runbook-k8s.md"},
where_document={"$contains": "secret"}
)

Par défaut, ChromaDB utilise all-MiniLM-L6-v2. Pour du contenu multilingue ou des besoins spécifiques, utilisez une fonction d’embedding personnalisée.

from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction
# Modèle multilingue recommandé pour le français
embedding_fn = SentenceTransformerEmbeddingFunction(
model_name="intfloat/multilingual-e5-small"
)
collection = client.create_collection(
name="docs_fr",
embedding_function=embedding_fn,
metadata={"hnsw:space": "cosine"}
)
ModèleDimensionsLanguesUsage
all-MiniLM-L6-v2384AnglaisDéfaut, rapide
intfloat/multilingual-e5-small384100+Multilingue, recommandé FR
BAAI/bge-m31024100+Haute qualité, plus lent

Quand vous récupérez une collection existante avec get_collection, vous devez passer la même fonction d’embedding :

# ❌ Ne fonctionnera pas correctement (embedding par défaut utilisé)
collection = client.get_collection("docs_fr")
# ✅ Passer la fonction d'embedding
embedding_fn = SentenceTransformerEmbeddingFunction(
model_name="intfloat/multilingual-e5-small"
)
collection = client.get_collection("docs_fr", embedding_function=embedding_fn)

Voici un exemple complet d’utilisation de ChromaDB pour un système RAG simple.

"""RAG simple avec ChromaDB."""
import chromadb
from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction
def creer_collection_rag(client, nom: str):
"""Crée une collection optimisée pour le RAG."""
embedding_fn = SentenceTransformerEmbeddingFunction(
model_name="intfloat/multilingual-e5-small"
)
return client.get_or_create_collection(
name=nom,
embedding_function=embedding_fn,
metadata={"hnsw:space": "cosine"}
)
def indexer_documents(collection, documents: list[dict]):
"""
Indexe une liste de documents dans la collection.
Chaque document doit avoir : id, content, source, section
"""
collection.upsert(
ids=[d["id"] for d in documents],
documents=[d["content"] for d in documents],
metadatas=[{"source": d["source"], "section": d["section"]} for d in documents]
)
return collection.count()
def rechercher(collection, question: str, top_k: int = 3, source: str = None):
"""
Recherche les chunks pertinents pour une question.
Args:
collection: Collection ChromaDB
question: Question utilisateur
top_k: Nombre de résultats
source: Filtrer par source (optionnel)
Returns:
Liste de chunks avec métadonnées et distances
"""
where = {"source": source} if source else None
results = collection.query(
query_texts=[question],
n_results=top_k,
where=where
)
# Reformater les résultats
chunks = []
for i in range(len(results['ids'][0])):
chunks.append({
"id": results['ids'][0][i],
"content": results['documents'][0][i],
"metadata": results['metadatas'][0][i],
"distance": results['distances'][0][i]
})
return chunks
def formater_contexte(chunks: list[dict]) -> str:
"""Formate les chunks pour injection dans le prompt."""
contexte = []
for chunk in chunks:
source = chunk['metadata'].get('source', 'unknown')
section = chunk['metadata'].get('section', '')
contexte.append(f"[Source: {source} - {section}]\n{chunk['content']}")
return "\n---\n".join(contexte)
# === Utilisation ===
if __name__ == "__main__":
# Initialiser
client = chromadb.PersistentClient(path="./rag_db")
collection = creer_collection_rag(client, "documentation")
# Indexer des documents
docs = [
{
"id": "k8s-deploy-1",
"content": "Pour déployer sur Kubernetes, utilisez kubectl apply -f deployment.yaml",
"source": "runbook-k8s.md",
"section": "deployment"
},
{
"id": "k8s-secret-1",
"content": "Les secrets Kubernetes sont encodés en base64, pas chiffrés. Utilisez Sealed Secrets ou Vault.",
"source": "runbook-k8s.md",
"section": "security"
},
{
"id": "ansible-1",
"content": "Ansible utilise des playbooks YAML pour décrire l'état souhaité des serveurs.",
"source": "guide-ansible.md",
"section": "basics"
},
]
count = indexer_documents(collection, docs)
print(f"Documents indexés: {count}")
# Rechercher
question = "Comment sécuriser mes secrets Kubernetes ?"
chunks = rechercher(collection, question, top_k=2)
print(f"\nQuestion: {question}")
print(f"\nContexte RAG:\n{formater_contexte(chunks)}")

Sortie :

Documents indexés: 3
Question: Comment sécuriser mes secrets Kubernetes ?
Contexte RAG:
[Source: runbook-k8s.md - security]
Les secrets Kubernetes sont encodés en base64, pas chiffrés. Utilisez Sealed Secrets ou Vault.
---
[Source: runbook-k8s.md - deployment]
Pour déployer sur Kubernetes, utilisez kubectl apply -f deployment.yaml
# Voir les premiers documents sans recherche
peek = collection.peek(limit=5)
print(f"Aperçu: {peek['documents']}")
print(f"Total documents: {collection.count()}")
# Récupérer des documents spécifiques
docs = collection.get(ids=["doc1", "doc2"])
# Récupérer tous les documents (avec pagination)
all_docs = collection.get(limit=100, offset=0)
ErreurCauseSolution
DuplicateIDErrorID déjà existant avec addUtilisez upsert au lieu de add
Mauvais résultats après rechargementEmbedding function différentePassez la même fonction à get_collection
InvalidCollectionExceptionCollection n’existe pasUtilisez get_or_create_collection
Distance toujours ~2Embeddings incompatiblesVérifiez que vous utilisez le même modèle
  1. Trois modes : in-memory (dev), persistant (local), client-serveur (prod)

  2. Embeddings automatiques : par défaut all-MiniLM-L6-v2, utilisez multilingual-e5-small pour le français

  3. Upsert plutôt qu’add : évite les erreurs de doublon

  4. Filtrage puissant : combine sémantique (query_texts) + métadonnées (where) + contenu (where_document)

  5. Récupération cohérente : toujours passer la même embedding_function à get_collection

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.