ITSkillsCenter
Blog

Chroma self-hosted : vector store léger — tutoriel Python 2026

18 min de lecture

Chroma self-hosted : vector store léger — tutoriel Python 2026

📍 Article principal du cluster : Vector DB self-hosted 2026 : Qdrant, Weaviate, Chroma, Milvus
Cet article fait partie du cluster Vector DB self-hosted. Pour la vue d’ensemble comparative, lisez d’abord le pilier.

Introduction

Imaginez un data analyst sénégalais qui travaille sur un script Python d’analyse documentaire pour une PME à Dakar. Il veut construire un moteur de recherche sémantique sur un corpus de 5 000 contrats OHADA numérisés, sans budget serveur, sans DevOps, sans Kubernetes. Il ouvre le terminal, tape pip install chromadb, et cinq minutes plus tard il interroge ses documents en langage naturel. C’est exactement la promesse de Chroma.

Chroma est un vector store open source pensé d’abord pour la simplicité. Là où Qdrant ou Milvus demandent de démarrer un service séparé, de comprendre des API binaires ou de configurer des clusters, Chroma peut tourner en processus, directement dans votre script Python, sans aucune infrastructure externe. C’est son avantage distinctif et la raison pour laquelle il est devenu le choix numéro un pour le prototypage de pipelines RAG (Retrieval-Augmented Generation).

Dans ce tutoriel, vous allez installer Chroma, créer une collection, y injecter des embeddings générés par sentence-transformers, interroger par similarité cosinus, puis passer en mode serveur Docker pour un usage multi-clients. En bonus, vous verrez comment connecter tout cela à LangChain. À la fin, vous aurez une base solide pour construire vos propres applications de recherche sémantique ou de RAG sur des documents techniques, juridiques ou métier.

Prérequis

  • Python 3.10 ou supérieur — Chroma 0.6.x exige Python ≥ 3.10 (vérifié sur docs.trychroma.com)
  • pip à jour (pip install --upgrade pip)
  • Docker (optionnel, pour le mode serveur — étape 6)
  • RAM : 512 Mo suffisent pour un prototype embedded ; 1 Go recommandé pour sentence-transformers
  • Niveau : intermédiaire — vous savez écrire un script Python et utiliser pip
  • Temps estimé : 20 à 30 minutes

Étape 1 — Chroma embedded vs serveur : choisir son mode

Avant d’installer quoi que ce soit, il est important de comprendre que Chroma propose deux modes de fonctionnement fondamentalement différents, et que le choix entre les deux détermine toute l’architecture de votre application.

Le mode embedded (ou in-process) signifie que la base de données Chroma tourne dans le même processus Python que votre code. Il n’y a pas de serveur séparé, pas de port réseau, pas de daemon à gérer. Chroma lit et écrit directement sur le disque local via SQLite et des fichiers binaires. C’est le mode idéal pour les scripts d’analyse, les notebooks Jupyter, les prototypes, ou toute application mono-utilisateur où vous contrôlez l’environnement d’exécution.

Le mode serveur expose Chroma via une API HTTP REST (par défaut sur le port 8000). Votre code Python devient un client qui parle à ce serveur via chromadb.HttpClient(). Ce mode est indispensable dès que plusieurs processus ou plusieurs machines doivent accéder à la même base vectorielle — par exemple un backend FastAPI et un worker de traitement de documents qui partagent la même collection.

Pour ce tutoriel, nous commençons par le mode embedded (étapes 2 à 5), puis nous basculons en mode serveur (étape 6). Dans les deux cas, l’API Python côté collection est identique — c’est uniquement la façon d’instancier le client qui change, ce qui rend la migration transparente.

Étape 2 — Installer chromadb avec pip

L’installation de Chroma se fait via PyPI et ne nécessite aucune dépendance système particulière. La bonne pratique est de travailler dans un environnement virtuel pour isoler les dépendances de votre projet et éviter les conflits de versions, surtout si vous avez plusieurs projets Python sur la même machine.

# Créer et activer un environnement virtuel
python -m venv .venv
source .venv/bin/activate          # Linux / macOS
# .venv\Scripts\activate            # Windows

# Mettre pip à jour
pip install --upgrade pip

# Installer chromadb
pip install chromadb

# Installer sentence-transformers pour les embeddings locaux
pip install sentence-transformers

# (Optionnel) Installer LangChain avec l'intégration Chroma
pip install langchain langchain-community langchain-chroma

L’installation de chromadb tire plusieurs dépendances : onnxruntime pour les embeddings par défaut, SQLite (via Python standard), et quelques bibliothèques de traitement numérique. Sur une connexion lente (ce qui est courant en Afrique de l’Ouest), prévoyez 2 à 5 minutes selon la bande passante. L’ensemble fait environ 200 Mo avec sentence-transformers. Une fois l’installation terminée, vérifiez qu’elle a réussi avec python -c "import chromadb; print(chromadb.__version__)" — vous devriez voir 0.6.x ou supérieur.

Étape 3 — PersistentClient et création d’une collection

Chroma propose plusieurs façons de créer un client Python. La plus courante pour un usage réel (par opposition à un test unitaire éphémère) est le PersistentClient, qui sauvegarde les données sur disque entre deux exécutions du script. Sans persistance, toutes vos collections disparaissent à la fin du processus — ce qui peut surprendre les nouveaux utilisateurs.

import chromadb

# PersistentClient : les données sont sauvegardées dans le dossier ./chroma_data
# Ce dossier est créé automatiquement s'il n'existe pas
client = chromadb.PersistentClient(path="./chroma_data")

# Créer ou récupérer une collection existante
# get_or_create_collection évite une erreur si la collection existe déjà
collection = client.get_or_create_collection(
    name="documents_ohada",
    metadata={"hnsw:space": "cosine"}  # distance cosinus, recommandée pour le texte
)

print(f"Collection prête : {collection.name}")
print(f"Nombre de documents : {collection.count()}")

Le paramètre metadata={"hnsw:space": "cosine"} configure l’index HNSW (Hierarchical Navigable Small World) pour utiliser la similarité cosinus plutôt que la distance euclidienne. Pour des embeddings de texte, le cosinus est presque toujours le bon choix car il mesure l’angle entre les vecteurs sans être sensible à leur magnitude. Les autres valeurs possibles sont "l2" (distance euclidienne, par défaut) et "ip" (produit scalaire). Une fois la collection créée avec un espace donné, vous ne pouvez pas changer ce paramètre — il faut supprimer et recréer la collection si vous voulez en changer.

Étape 4 — Ajouter des embeddings avec sentence-transformers

Chroma peut générer des embeddings automatiquement via son modèle embarqué (all-MiniLM-L6-v2 par défaut), mais pour un contrôle total sur la qualité des vecteurs, il est recommandé de générer vos embeddings manuellement avec sentence-transformers. Cela vous permet de choisir un modèle multilingue qui comprend le français — crucial pour vos données documentaires.

from sentence_transformers import SentenceTransformer
import chromadb

# Charger un modèle multilingue adapté au français et aux langues africaines
# paraphrase-multilingual-MiniLM-L12-v2 supporte 50+ langues dont le français
model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")

client = chromadb.PersistentClient(path="./chroma_data")
collection = client.get_or_create_collection(
    name="documents_ohada",
    metadata={"hnsw:space": "cosine"}
)

# Documents exemples — extraits fictifs de droit OHADA
documents = [
    "Le contrat de vente commerciale doit être établi par écrit conformément à l'Acte uniforme OHADA.",
    "La société à responsabilité limitée (SARL) requiert un capital minimum de 1 FCFA depuis 2010.",
    "Le débiteur dispose de 30 jours pour contester une injonction de payer devant la juridiction compétente.",
    "L'arbitrage commercial international est régi par l'Acte uniforme relatif au droit de l'arbitrage.",
    "La mise en demeure doit être notifiée par lettre recommandée avec accusé de réception.",
]

# Identifiants uniques pour chaque document
ids = [f"doc_{i}" for i in range(len(documents))]

# Générer les embeddings
embeddings = model.encode(documents).tolist()  # .tolist() convertit numpy -> list

# Insérer dans la collection
# upsert met à jour si l'id existe déjà, sinon insère
collection.upsert(
    documents=documents,
    embeddings=embeddings,
    ids=ids,
    metadatas=[{"source": "ohada", "lang": "fr"} for _ in documents]
)

print(f"Documents insérés. Total dans la collection : {collection.count()}")

La méthode upsert() est préférable à add() car elle est idempotente : si vous relancez le script, elle met à jour les documents existants au lieu de lever une erreur de clé dupliquée. Les metadatas sont un dictionnaire arbitraire associé à chaque document — vous pouvez y stocker la source, la date, le nom de fichier, ou tout autre attribut qui vous permettra de filtrer les résultats plus tard. Le modèle paraphrase-multilingual-MiniLM-L12-v2 est téléchargé automatiquement depuis Hugging Face lors du premier appel (~420 Mo) et mis en cache localement pour les utilisations suivantes.

Étape 5 — Requête par similarité

Une fois les documents indexés, vous pouvez interroger la collection en langage naturel. Chroma calcule la similarité entre l’embedding de votre requête et les embeddings stockés, puis retourne les N documents les plus proches. C’est le coeur d’un moteur de recherche sémantique.

from sentence_transformers import SentenceTransformer
import chromadb

model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")
client = chromadb.PersistentClient(path="./chroma_data")
collection = client.get_collection("documents_ohada")

# Requête en langage naturel
query_text = "Quelles sont les règles pour contester une injonction de payer ?"

# Générer l'embedding de la requête
query_embedding = model.encode(query_text).tolist()

# Interroger la collection — n_results : nombre de résultats à retourner
results = collection.query(
    query_embeddings=[query_embedding],
    n_results=3,
    include=["documents", "metadatas", "distances"]
)

# Afficher les résultats avec leur score de similarité
for i, (doc, dist) in enumerate(zip(results["documents"][0], results["distances"][0])):
    similarite = 1 - dist  # distance cosinus -> score de similarité
    print(f"Résultat {i+1} (similarité : {similarite:.3f})")
    print(f"  → {doc}")
    print()

Les distances retournées par Chroma en mode cosinus sont des distances (de 0 = identique à 2 = opposé), pas des scores de similarité. Pour obtenir un score entre 0 et 1, on calcule 1 - distance. Un score supérieur à 0.7 indique une bonne similarité sémantique ; en dessous de 0.4, le document n’est probablement pas pertinent. Vous pouvez aussi filtrer par métadonnées avec le paramètre where={"source": "ohada"} pour restreindre la recherche à un sous-ensemble de vos documents — très utile quand votre collection contient des documents de sources hétérogènes.

Étape 6 — Mode serveur Docker pour usage multi-clients

Le mode embedded est parfait pour un script solo, mais dès que vous avez plusieurs processus qui doivent lire et écrire dans la même collection — un serveur FastAPI qui répond aux requêtes des utilisateurs et un pipeline d’ingestion qui tourne en tâche de fond — vous avez besoin du mode serveur. Chroma publie une image Docker officielle qui simplifie énormément le déploiement.

# Démarrer le serveur Chroma avec persistance sur disque
# Le volume monte ./chroma_server_data dans le conteneur
docker run -d \
  --name chromadb \
  -p 8000:8000 \
  -v "$(pwd)/chroma_server_data:/chroma/chroma" \
  -e IS_PERSISTENT=TRUE \
  chromadb/chroma:latest

# Vérifier que le serveur est opérationnel
curl http://localhost:8000/api/v1/heartbeat
# Réponse attendue : {"nanosecond heartbeat": }

Une fois le serveur lancé, modifiez votre code Python pour utiliser HttpClient à la place de PersistentClient. Toutes les opérations sur la collection restent exactement les mêmes — seule la ligne d’instanciation du client change, ce qui facilite la migration :

import chromadb

# HttpClient se connecte au serveur Chroma via HTTP
client = chromadb.HttpClient(host="localhost", port=8000)

# À partir d'ici, l'API est identique au mode embedded
collection = client.get_or_create_collection(
    name="documents_ohada",
    metadata={"hnsw:space": "cosine"}
)
print(f"Collections disponibles : {client.list_collections()}")

En production sur un VPS (par exemple un VPS OVH à 3,50 €/mois ou un Contabo à 4,50 €/mois), vous placerez ce conteneur derrière un reverse proxy Nginx ou Caddy pour sécuriser l’accès. Chroma 0.6.x supporte nativement l’authentification par token (CHROMA_SERVER_AUTH_CREDENTIALS) et par identifiant/mot de passe — consultez la documentation officielle sur docs.trychroma.com/production/auth pour configurer cela avant d’exposer votre serveur sur Internet.

Étape 7 — Intégration LangChain

LangChain est le framework de référence pour construire des pipelines RAG en Python. Son intégration avec Chroma est directe et bien documentée. L’idée d’un pipeline RAG est simple : quand un utilisateur pose une question, on cherche les documents pertinents dans Chroma, puis on les passe en contexte à un LLM (GPT-4, Claude, Llama, etc.) pour obtenir une réponse fondée sur vos données.

from langchain_chroma import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_core.documents import Document

# Utiliser le même modèle sentence-transformers via LangChain
embeddings = HuggingFaceEmbeddings(
    model_name="paraphrase-multilingual-MiniLM-L12-v2"
)

# Créer ou charger un vector store Chroma depuis LangChain
# persist_directory pointe vers le même dossier que PersistentClient
vectorstore = Chroma(
    collection_name="documents_ohada_lc",
    embedding_function=embeddings,
    persist_directory="./chroma_langchain"
)

# Ajouter des documents au format LangChain
docs = [
    Document(
        page_content="L'OHADA regroupe 17 États africains autour d'un droit des affaires harmonisé.",
        metadata={"source": "ohada-intro", "chapitre": 1}
    ),
    Document(
        page_content="Le Traité de Port-Louis a été signé le 17 octobre 1993 au Sénégal.",
        metadata={"source": "ohada-historique", "chapitre": 1}
    ),
]
vectorstore.add_documents(docs)

# Recherche sémantique simple
resultats = vectorstore.similarity_search(
    "Quand a été créé l'OHADA ?",
    k=2
)
for doc in resultats:
    print(doc.page_content)
    print(f"  Source : {doc.metadata['source']}")

Pour transformer ce vector store en retrieveur LangChain (utile pour les chaînes RAG complètes), il suffit d’appeler vectorstore.as_retriever(search_kwargs={"k": 3}). Ce retrieveur peut ensuite être branché directement sur une chaîne RetrievalQA ou un ConversationalRetrievalChain avec le LLM de votre choix. Si vous utilisez un LLM local via Ollama (déjà couvert dans le tutoriel frère de ce cluster), vous obtenez un pipeline RAG 100 % local, sans aucune donnée qui sort de votre machine.

Erreurs fréquentes

Erreur Cause probable Solution
ValueError: Collection already exists Utilisation de create_collection() sur une collection existante Remplacer par get_or_create_collection()
chromadb.errors.InvalidDimensionException Les embeddings insérés n’ont pas la même dimension que les embeddings existants Toujours utiliser le même modèle d’embedding pour une collection donnée. Supprimer et recréer la collection si vous changez de modèle.
Connection refused sur port 8000 Le conteneur Docker Chroma n’est pas démarré ou a crashé docker ps pour vérifier, docker logs chromadb pour les erreurs, docker start chromadb pour relancer
RuntimeError: no such file or directory sur le model sentence-transformers Le modèle n’a pas été téléchargé (pas d’accès Internet au moment du premier appel) Télécharger le modèle avec connexion (SentenceTransformer("...").save("./local_model")) puis le charger en local avec SentenceTransformer("./local_model")
upsert() lent sur grandes collections Insertion document par document dans une boucle Insérer en batch — passer des listes à upsert(). Batch de 100 à 500 documents selon la RAM disponible.
Résultats non pertinents malgré une requête claire Mismatch entre le modèle d’embedding de la requête et celui des documents Toujours utiliser le même modèle pour indexer et pour requêter. Le modèle par défaut de Chroma et sentence-transformers produisent des espaces vectoriels incompatibles.
ImportError: No module named 'chromadb' Installation dans le mauvais environnement virtuel Vérifier que l’environnement virtuel est activé : which python doit pointer vers .venv/bin/python

Adaptation au contexte ouest-africain

Chroma est le vector store le plus accessible pour les développeurs et data analysts en Afrique de l’Ouest, précisément parce qu’il ne nécessite pas d’infrastructure dédiée. Pour une PME à Abidjan, Dakar, Bamako ou Cotonou, le mode embedded représente une opportunité concrète : un data analyst peut construire un prototype de recherche sémantique sur les documents internes de l’entreprise — contrats, fiches techniques, procédures — avec simplement Python et un ordinateur portable, sans frais de serveur, sans avoir à convaincre le DSI d’allouer un budget cloud.

Le cas d’usage RAG sur la documentation OHADA est particulièrement pertinent pour la région. Les 17 États membres partagent le même corpus juridique (Actes uniformes), mais les textes sont volumineux et peu accessibles. Un système de question-réponse construit avec Chroma + un LLM local (Ollama + Llama 3 ou Mistral) permettrait à des avocats, des greffiers ou des entrepreneurs de consulter ce corpus en français de façon conversationnelle, sans connexion Internet permanente.

Pour la contrainte de bande passante, deux optimisations importantes : premièrement, téléchargez les modèles sentence-transformers une seule fois et sauvegardez-les en local (model.save("./models/multilingual-minilm")) avant de les redistribuer sur d’autres machines du réseau local via clé USB ou partage réseau. Deuxièmement, en mode serveur Docker, un seul serveur Chroma sur le réseau local (via un Raspberry Pi ou un vieux PC recyclé) peut servir plusieurs utilisateurs simultanément via HttpClient, sans que chaque machine ait besoin de télécharger le modèle ou de maintenir une copie locale des données.

Sur les questions de ressources matérielles : le modèle paraphrase-multilingual-MiniLM-L12-v2 tourne correctement avec 512 Mo de RAM dédiée sur un CPU classique. Sur un VPS entrée de gamme (Contabo 4 vCPU / 8 Go RAM à ~5 €/mois, ou un VPS local chez ATI pour la Tunisie ou des hébergeurs locaux pour la région) vous pouvez faire tourner Chroma en mode serveur avec plusieurs collections de dizaines de milliers de documents sans difficulté. L’empreinte est incomparablement plus légère que Milvus ou Weaviate.

Tutoriels frères

Pour aller plus loin

FAQ

Q : Chroma est-il adapté à la production ou seulement au prototypage ?
Chroma peut aller en production pour des charges modérées. Il gère bien des collections de quelques centaines de milliers de documents sur un serveur dédié en mode Docker. Pour des millions de vecteurs avec des exigences de latence strictes, orientez-vous vers Qdrant ou Milvus. La règle pratique : commencez avec Chroma pour valider votre cas d’usage, migrez vers Qdrant si vous dépassez 500 000 documents ou si vous avez besoin de requêtes en dessous de 10 ms.
Q : Quelle est la différence entre EphemeralClient, PersistentClient et HttpClient ?
EphemeralClient stocke tout en mémoire — les données sont perdues à la fin du processus (utile pour les tests). PersistentClient sauvegarde sur disque dans le même processus Python. HttpClient se connecte à un serveur Chroma externe (local ou distant). Pour tout usage réel, utilisez PersistentClient ou HttpClient.
Q : Chroma gère-t-il nativement le français et les langues locales africaines ?
Chroma lui-même est language-agnostic — c’est le modèle d’embedding que vous utilisez qui détermine la qualité linguistique. Le modèle par défaut de Chroma (all-MiniLM-L6-v2) est majoritairement anglophone. Pour le français et les langues d’Afrique de l’Ouest, utilisez paraphrase-multilingual-MiniLM-L12-v2 ou multilingual-e5-large de sentence-transformers.
Q : Peut-on utiliser Chroma sans connexion Internet après installation ?
Oui, entièrement. Une fois chromadb et sentence-transformers installés et le modèle téléchargé en cache, tout fonctionne offline. C’est un avantage majeur pour les environnements avec une connectivité intermittente — scripts d’analyse sur le terrain, traitement de données sensibles en local, contextes avec coupures fréquentes.
Q : Comment sauvegarder et restaurer une collection Chroma ?
En mode PersistentClient, les données sont dans le dossier passé en paramètre (path="./chroma_data"). Pour sauvegarder : copiez ce dossier. Pour restaurer : copiez-le sur la machine cible et pointez PersistentClient vers ce dossier. En mode serveur Docker, le volume (-v ./chroma_server_data:/chroma/chroma) contient toutes les données — sauvegardez ce dossier avec rsync ou un script de backup.
Q : Chroma supporte-t-il la recherche hybride (dense + sparse / BM25) ?
Non nativement. Chroma est un vector store pur (dense embeddings). Pour une recherche hybride combinant similarité sémantique et correspondance par mots-clés, vous devrez soit combiner Chroma avec une recherche textuelle séparée dans votre code, soit utiliser Weaviate ou Elasticsearch qui proposent cette fonctionnalité en natif.
Q : Est-il possible de filtrer les résultats par métadonnées avant la recherche vectorielle ?
Oui. Le paramètre where de collection.query() permet de filtrer par métadonnées : where={"source": "ohada", "annee": {"$gte": 2020}}. Chroma supporte les opérateurs $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin. Ce pré-filtrage est appliqué avant la recherche vectorielle, ce qui peut significativement réduire le nombre de vecteurs comparés et donc accélérer la requête sur de grandes collections.

Site réalisé par [ITS] ITSkillsCenter — Formation tech pour l’Afrique de l’Ouest


ملخص بالعربية: كروما: قاعدة بيانات متجهية مدمجة بلغة Python، خفيفة ومثالية للنماذج الأولية وتطبيقات RAG في المؤسسات الصغيرة بأفريقيا جنوب الصحراء. يشرح هذا الدليل التثبيت والاستخدام خطوة بخطوة.
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité