Milvus : Base de données vectorielle open source pour la recherche sémantique à grande échelle

Architectures traditionnelles versus recherche sémantique

Les systèmes relationnels classiques excellent dans l'extraction précise de données structurées (WHERE age = 25), mais échouent face aux requêtes de similarité sémantique. Milvus comble cette lacune par une conception nativement orientée vecteur :

  • Stockage haute dimension : Gestion efficace de vecteurs 768D, 1536D ou plus, issus de modèles d'embedding
  • Métriques de proximité : Support natif de la similarité cosinus, distance euclidienne, produit scalaire et distance de Jaccard
  • Évolutivité horizontale : Partitionnement automatique, réplication multi-nœuds, tolérance aux pannes
  • Interopérabilité : Connecteurs natifs pour LangChain, LlamaIndex, Haystack et les principaux frameworks LLM

Fondamentaux du modèle de données

Représentation vectorielle

Les données non structurées (texte, image, audio) sont transformées en représentations numériques denses via des modèles d'embedding. Milvus se positionne comme couche de persistance et d'indexation, distincte de la génération de ces vecteurs.

Organisation hiérarchique

Entité Analogie Fnoction
Collection Table relationnelle Conteneur de vecteurs et métadonnées associées
Partition Vue matérialisée Segmentation logique pour optimisation des requêtes
Segment Shard physique Unité de distribution sur le cluster

Stratégies d'indexation

La performence de recherche repose sur le compromis entre rappel et latence :

Configuration conteneurisée

# Orchestration avec Docker Compose
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/v2.3.0/deployments/docker/standalone/docker-compose.yml -O milvus.yml

docker compose -f milvus.yml up -d --wait

# Vérification de l'état des services
docker compose ps | grep milvus

L'instance écoute par défaut sur le port 19530 (gRPC) et expose une interface REST sur 9091.

Implémentation Python complète

from pymilvus import (
    connections, Collection, FieldSchema, 
    CollectionSchema, DataType, utility
)
import numpy.random as npr

# Établissement de la connexion persistante
connections.connect(alias="prod", host="localhost", port=19530")

# Définition du schéma avec contraintes de type
champ_identifiant = FieldSchema(
    name="doc_id",
    dtype=DataType.INT64,
    is_primary=True,
    auto_id=False  # Gestion manuelle pour traçabilité
)

champ_vecteur = FieldSchema(
    name="representation",
    dtype=DataType.FLOAT_VECTOR,
    dim=1536,  # Compatible OpenAI text-embedding-3-large
    description="Embedding sémantique du document"
)

champ_contenu = FieldSchema(
    name="extrait",
    dtype=DataType.VARCHAR,
    max_length=4096,
    description="Fragment textuel original"
)

champ_metadonnees = FieldSchema(
    name="source",
    dtype=DataType.VARCHAR,
    max_length=256,
    description="Origine du document (URL, fichier, etc.)"
)

schema_complet = CollectionSchema(
    fields=[champ_identifiant, champ_vecteur, champ_contenu, champ_metadonnees],
    enable_dynamic_field=True,  # Champs JSON flexibles
    description="Corpus pour système RAG"
)

# Création avec gestion d'existence
nom_collection = "corpus_rag_v1"
if utility.has_collection(nom_collection):
    Collection(nom_collection).drop()
    
collection = Collection(name=nom_collection, schema=schema_complet)

# Injection de données simulées
documents = [
    (1, "Les bases de données vectorielles révolutionnent la recherche sémantique", "blog"),
    (2, "Milvus offre une latence sub-millisecondaire sur milliards de vecteurs", "documentation"),
    (3, "L'intégration LangChain simplifie les pipelines RAG", "tutorial"),
]

vecteurs_synthetiques = npr.randn(len(documents), 1536).astype('float32')
vecteurs_synthetiques /= np.linalg.norm(vecteurs_synthetiques, axis=1, keepdims=True)

donnees_insertion = [
    [d[0] for d in documents],           # doc_id
    vecteurs_synthetiques,                  # representation
    [d[1] for d in documents],               # extrait
    [d[2] for d in documents]                # source
]

resultat = collection.insert(donnees_insertion)
print(f"Documents indexés : {resultat.insert_count}")

# Configuration de l'index HNSW pour recherche haute performance
parametres_index = {
    "index_type": "HNSW",
    "metric_type": "COSINE",
    "params": {
        "M": 16,           # Connexions par nœud (16-64 selon mémoire disponible)
        "efConstruction": 200  # Qualité de construction du graphe
    }
}

collection.create_index(field_name="representation", index_params=parametres_index)
collection.load()  # Montage en mémoire pour latence optimale

# Requête de similarité avec filtrage métadonnées
vecteur_requete = npr.randn(1, 1536).astype('float32')
vecteur_requete /= np.linalg.norm(vecteur_requete)

parametres_recherche = {
    "data": vecteur_requete,
    "anns_field": "representation",
    "param": {"ef": 64},  # Profondeur d'exploration du graphe
    "limit": 5,
    "expr": 'source == "documentation"',  # Prédicat de filtrage
    "output_fields": ["extrait", "source"]
}

reponses = collection.search(**parametres_recherche)

for resultat_groupe in reponses:
    for correspondance in resultat_groupe:
        print(f"Score: {1 - correspondance.distance:.4f}")
        print(f"Contenu: {correspondance.entity.get('extrait')[:100]}...")
        print(f"Provenance: {correspondance.entity.get('source')}\n")

Intégration écosystème

Chain LangChain native

from langchain_community.vectorstores import Milvus
from langchain_openai import OpenAIEmbeddings

stockage_vectoriel = Milvus(
    embedding_function=OpenAIEmbeddings(model="text-embedding-3-small"),
    collection_name="knowledge_base",
    connection_args={"uri": "http://localhost:19530"},
    index_params={"metric_type": "COSINE", "index_type": "HNSW"},
    drop_old=True
)

# Alimentation automatique avec chunking
from langchain.document_loaders import PyPDFLoader
documents = PyPDFLoader("rapport.pdf").load()

stockage_vectoriel.from_documents(
    documents,
    embedding=OpenAIEmbeddings(),
    collection_name="rapport_analyse"
)

Déploiement cluster Kubernetes

Pour les environnements de production :

# Installation via Helm
helm repo add milvus https://zilliztech.github.io/milvus-helm/
helm install milvus-prod milvus/milvus \
  --set cluster.enabled=true \
  --set etcd.replicaCount=3 \
  --set minio.mode=distributed \
  --set queryNode.replicas=4

Cas d'usage caractéristiques

Domaine Pattern d'implémentation Configuration recommandée
RAG conversationnel Hybrid search (dense + sparse) avec re-ranking HNSW + index inversé, ef=128
Recherche visuelle Multi-modal CLIP embeddings IVF_SQ8 pour compression mémoire
Détection fraude Recherche de motifs similaires en temps réel GPU_IVF_FLAT, nprobe=256
Recommandation Approximate nearest neighbor batch HNSW avec M=32, parallélisme requête

Comparatif technique

Solution Capacité max Latence typique Modèle de déploiement
Milvus 10B+ vecteurs 5-20ms Self-hosted / Managed (Zilliz)
Chroma 1M vecteurs 50-200ms Embarqué / Serverless
Qdrant 100M vecteurs 10-50ms Rust-native, edge-compatible
Pinecone Illimité (payant) <10ms SaaS uniquement
Weaviate 100M vecteurs 20-100ms GraphQL-native, modulaire

Recommandation de sélection : Privilégier Milvus pour les architectures nécessitant contrôle opérationnel, conformité data residency, et évolutivité prévisible des coûts. Opter pour les alternatives lorsque la simplicité de prototypage (Chroma) ou l'abstraction totale d'infrastructure (Pinecone) prime sur les exigences de performance.

Optimisations avancées

Étiquettes: Milvus Vector Database rag langchain embedding

Publié le 10 octobre à 15h26