Aller au contenu
Développement medium

Chroma : base vectorielle pour développer un RAG

9 min de lecture

logo python

FAISS indexe des vecteurs — et rien d'autre : ni les textes, ni leurs métadonnées, ni la persistance. Chroma comble ce manque. C'est une base vectorielle embarquée qui range au même endroit les vecteurs, les documents et leurs métadonnées, gère la persistance sur disque, et filtre les recherches — « cherche, mais seulement dans les documents de 2026 ». Pas de serveur à déployer : Chroma s'utilise comme une bibliothèque. Ce guide montre comment créer une collection, y indexer des documents, faire une recherche sémantique filtrée et persister le tout. Public visé : développeur qui veut une base vectorielle confortable pour construire un RAG.

  • Ce que Chroma apporte par rapport à un index vectoriel nu.
  • Créer une collection persistante et l'alimenter.
  • Indexer des documents avec leurs métadonnées.
  • Faire une recherche sémantique et la filtrer par métadonnée.
  • Situer Chroma entre prototype et production.

Avec FAISS, on gère trois choses séparément : l'index vectoriel, la liste des textes alignée dessus, et la persistance bricolée à la main. Dès que des métadonnées entrent en jeu — la source d'un document, sa date —, l'attelage devient fragile.

Chroma réunit tout. Une collection Chroma stocke pour chaque document : son texte, son vecteur, ses métadonnées, le tout sous un identifiant. La persistance est automatique. Et la recherche peut être filtrée par métadonnée — une capacité que FAISS n'a pas nativement.

C'est ce qui fait de Chroma la base vectorielle de référence pour développer un RAG : tout ce qu'on assemblait à la main est désormais géré. Et comme Chroma s'exécute en bibliothèque embarquée, sans serveur, on garde la simplicité d'un prototype.

Chroma s'utilise via un client. Le PersistentClient écrit les données sur disque : la collection survit à l'arrêt du programme.

import chromadb
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("documentation")

get_or_create_collection est le bon réflexe : il crée la collection au premier lancement, et la réutilise ensuite. Aucune logique « si elle existe déjà » à écrire — relancer le programme retrouve la collection telle quelle.

On peuple la collection avec add. Chaque document apporte quatre éléments : un identifiant, un texte, un vecteur et des métadonnées.

collection.add(
ids=["doc-0", "doc-1"],
documents=[
"Un volume Docker conserve les données du conteneur.",
"Terraform décrit une infrastructure en code déclaratif.",
],
embeddings=vectoriser([...]), # vecteurs calculés avec Ollama
metadatas=[
{"sujet": "docker", "annee": 2026},
{"sujet": "terraform", "annee": 2024},
],
)

Les métadonnées sont la grande différence avec un index nu : ce sont des paires clé-valeur attachées à chaque document — sujet, annee, source. Elles ne servent pas à la recherche sémantique, mais à la filtrer, on le verra. Pour ne pas réindexer à chaque lancement, on conditionne l'add au fait que la collection soit vide — collection.count() == 0.

La recherche se fait avec query : on fournit le vecteur de la question et le nombre n_results de documents voulus.

def rechercher(collection, question, k=3, filtre=None):
"""Recherche les k documents les plus proches, avec filtre optionnel."""
resultat = collection.query(
query_embeddings=vectoriser([question]),
n_results=k,
where=filtre,
)
return [
{"texte": texte, "sujet": meta["sujet"], "annee": meta["annee"]}
for texte, meta in zip(resultat["documents"][0], resultat["metadatas"][0])
]

query renvoie une structure groupée par requête — d'où le [0] qui prend les résultats de la première (et seule) question. Chaque résultat ramène à la fois le texte et ses métadonnées : Chroma a tout gardé ensemble, on n'a aucune liste externe à consulter.

C'est la capacité qui distingue vraiment Chroma. Le paramètre where restreint la recherche aux documents dont les métadonnées satisfont une condition — la recherche sémantique ne s'applique qu'à ce sous-ensemble.

# Chercher uniquement dans les documents Docker de 2026.
filtre = {"$and": [{"sujet": "docker"}, {"annee": 2026}]}
resultats = rechercher(collection, "conteneur", k=5, filtre=filtre)

Les opérateurs — $and, $or, $eq, $gte — se combinent pour exprimer la condition. L'effet est puissant : on cherche « le passage le plus pertinent parmi les documents récents », ou « dans la section autorisée à cet utilisateur ». Ce filtrage est la base de fonctionnalités plus avancées — le multi-tenant, le contrôle d'accès — vues dans le guide sur le RAG en production.

Chroma occupe une place précise dans le spectre des bases vectorielles. Plus complète que FAISS — métadonnées, filtrage, persistance —, elle reste embarquée : simple, sans serveur, parfaite pour développer.

Cette simplicité a un revers. Une base embarquée se partage mal entre plusieurs services ou machines, et tient moins bien la charge de très gros volumes interrogés en continu. Quand un RAG passe en production multi-clients, on bascule vers une base vectorielle serveurQdrant, utilisée dans les guides pratiques suivants.

Étape du projetBase vectorielle
Prototype, corpus en mémoireFAISS
Développement, métadonnées, persistanceChroma
Production, service partagé, montée en chargeQdrant

Le parcours est naturel : on prototype, on développe avec Chroma, on industrialise avec Qdrant — sans jamais remettre en cause le principe, identique partout : vectoriser, indexer, rechercher.

SymptômeCause probableSolution
La collection est vide au redémarrageClient non persistantUtiliser PersistentClient, pas le client en mémoire
Documents réindexés à chaque lancementadd non conditionnéN'ajouter que si collection.count() == 0
Erreur de dimension à l'ajoutModèle d'embedding changéRecréer la collection, réindexer
where ne filtre rienOpérateur ou clé incorrectsVérifier les noms de métadonnées et la syntaxe $and
Résultats hors sujetQuestion et docs vectorisés différemmentUtiliser le même modèle des deux côtés
  • Chroma réunit vecteurs, textes et métadonnées dans une collection — fini les listes séparées.
  • Le PersistentClient écrit sur disque ; get_or_create_collection gère création et réutilisation.
  • Chaque document indexé porte un identifiant, un texte, un vecteur et des métadonnées.
  • Le paramètre where filtre la recherche sémantique par métadonnée — la base du multi-tenant.
  • Chroma est embarquée : idéale pour développer, à remplacer par Qdrant en production.
  • Le principe — vectoriser, indexer, rechercher — reste identique d'une base à l'autre.

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.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn