Astuces et conseils pratiques pour les développeurs créant des applications RAG
La recherche vectorielle n’est pas sans effort !
La recherche vectorielle, également appelée recherche de similarité vectorielle ou recherche du plus proche voisin, est une technique utilisée dans la récupération de données pour les applications RAG et les systèmes de recherche d’information afin de trouver des éléments ou des points de données similaires ou étroitement liés à un vecteur de requête donné. Elle est souvent présentée comme simple lorsqu’il s’agit de gérer de grands jeux de données. La perception générale est que vous pouvez simplement fournir des données à un modèle d’embedding pour générer des embeddings vectoriels, puis transférer ces vecteurs dans votre base de données vectorielle afin de récupérer les résultats souhaités.
comment effectuer une recherche vectorielle
De nombreux fournisseurs de bases de données vectorielles mettent en avant leurs capacités avec des qualificatifs tels que « facile », « convivial » et « simple ». Ils affirment que vous pouvez obtenir des résultats significatifs avec seulement quelques lignes de code, en contournant les complexités du machine learning, de l’IA, des processus ETL ou du réglage détaillé du système.
Et ils ont raison ; la recherche vectorielle est aussi facile que l’utilisation d’une bibliothèque numérique de base comme NumPy. Pour démontrer ce concept, j’ai écrit une courte démo en seulement dix lignes de code Python en utilisant l’algorithme des k plus proches voisins (KNN). Cette approche simple est efficace et précise pour les applications à petite échelle avec des jeux de données allant jusqu’à mille ou dix mille vecteurs.
import numpy as np
# Function to calculate Euclidean distance
def euclidean_distance(a, b):
return np.linalg.norm(a - b)
# Function to perform KNN
def knn(data, target, k):
# Calculate distances between target and all points in data
distances = [euclidean_distance(d, target) for d in data]
# Combine distances with data indices
distances = np.array(list(zip(distances, range(len(data)))))
# Sort by distance
sorted_distances = distances[distances[:, 0].argsort()]
# Get the top k closest indices
closest_k_indices = sorted_distances[:k, 1].astype(int)
# Return the top k closest vectors
return data[closest_k_indices]
Cependant, cette approche ne fonctionnera pas lorsque votre jeu de données atteindra un niveau modeste de plus d’un million ou de dix millions de vecteurs. C’est tout simplement parce que les applications réelles doivent interagir avec les utilisateurs, être disponibles, et sont toujours bien plus compliquées. Construire une application réelle évolutive exige de prendre soigneusement en compte divers facteurs au-delà du codage, notamment la qualité de la recherche, l’évolutivité, la disponibilité, la multi-location, le coût, la sécurité, et plus encore !
Alors, soyons honnêtes. Vous souvenez-vous de l’adage « Ça marche sur ma machine » ? La recherche vectorielle n’est pas différente : votre prototype fonctionne toujours ; la recherche vectorielle en production est souvent complexe. Alors, quelles sont les bonnes pratiques pour créer une application basée sur la recherche vectorielle en production ?
Pour vous aider à relever ces défis, nous partagerons trois conseils essentiels pour déployer efficacement votre base de données vectorielle dans l’environnement de production de votre application RAG avec Milvus :
Concevoir un schéma efficace : réfléchissez soigneusement à la structure de vos données et à la manière dont elles seront interrogées afin de créer un schéma qui optimise les performances et l’évolutivité.
Planifier l’évolutivité : anticipez la croissance future et concevez votre architecture pour prendre en charge l’augmentation des volumes de données et du trafic utilisateur.
Sélectionner l’index optimal et affiner les performances : choisissez la méthode d’indexation la plus adaptée à votre cas d’utilisation et surveillez puis ajustez en continu les paramètres de performance.
En suivant ces bonnes pratiques, vous serez bien parti pour construire une application robuste et efficace basée sur la recherche vectorielle. Dans les commentaires ci-dessous, partagez vos expériences et tout conseil supplémentaire que vous avez trouvé utile !
Concevoir une stratégie de schéma efficace
Un schéma définit la structure d’une base de données, y compris les tables, les champs, les relations et les types de données. Ce cadre organisé garantit que les données sont stockées de manière cohérente et prévisible, ce qui simplifie la gestion, les requêtes et la maintenance. Le choix d’un schéma approprié est particulièrement crucial pour les bases de données vectorielles comme Milvus, qui gèrent des vecteurs et divers types de données structurées, y compris les métadonnées et les données scalaires. Ces données peuvent améliorer la recherche filtrée et les résultats de recherche globaux. Cette section explorera les principaux facteurs à prendre en compte lors du choix de la stratégie de schéma la plus efficace.
Schéma dynamique vs schéma fixe
Dans les systèmes de bases de données, les schémas dynamiques et fixes représentent deux approches principales pour structurer les données. Les schémas dynamiques offrent de la flexibilité, simplifiant l’insertion et la récupération des données sans nécessiter un alignement étendu des données ni de processus ETL. Cette approche est parfaite pour les applications nécessitant des changements rapides de structure de données. En revanche, les développeurs apprécient les schémas fixes pour leur efficacité en matière de performances et leur économie de mémoire, grâce à leurs formats de stockage compacts.
Une approche de schéma hybride peut bénéficier aux développeurs travaillant sur des applications de bases de données vectorielles efficaces. Cette méthode combine la robustesse des schémas fixes pour les chemins de données essentiels avec la flexibilité des schémas dynamiques afin de s’adapter à divers cas d’utilisation. Par exemple, dans un système de recommandation, des éléments tels que les noms de produits et les identifiants de produits peuvent varier en importance selon le contexte. En employant un schéma hybride, les développeurs peuvent garantir des performances optimales là où c’est nécessaire tout en conservant la capacité de s’adapter à l’évolution des exigences en matière de données.
Définition des clés primaires et des clés de partition
Les clés primaires et les clés de partition sont deux concepts importants dans les bases de données vectorielles. En prenant la base de données vectorielle Milvus comme exemple, nous pouvons approfondir la manière dont ces clés fonctionnent dans les bases de données vectorielles.
L’architecture Milvus segmente les données en plusieurs composants : il existe des champs fixes et dynamiques (collectivement appelés la charge utile), un champ vectoriel obligatoire, et des champs système comme les horodatages et les identifiants universellement uniques (UUID), qui sont similaires à ceux que l’on trouve dans les bases de données relationnelles conventionnelles.
Clés primaires : Dans Milvus, la clé primaire sert souvent d’identifiant unique, qui dans un cas d’utilisation RAG peut être appliqué à un identifiant de fragment. Cette clé est fréquemment consultée et peut être configurée pour être générée automatiquement. Elle joue un rôle dans la localisation et la récupération rapides d’entrées de données spécifiques dans la base de données.
Clés de partition : Lorsque vous créez une collection dans Milvus, vous pouvez spécifier une clé de partition. Cette clé permet à Milvus de stocker des entités de données dans différentes partitions en fonction de leurs valeurs de clé, organisant efficacement les données en segments gérables. Une façon simple de penser aux clés de partition consiste à envisager d’utiliser une clé de partition s’il existe des ensembles de données sur lesquels vous souhaitez filtrer. Par exemple, l’isolation des données et une distribution efficace sont nécessaires dans les situations multilocataires, donc les stocker dans des partitions séparées peut aider à atteindre cet objectif. Les clés de partition sont également utiles pour l’évolutivité, car le partitionnement des données en fragments via le hachage permet à la base de données de gérer plus efficacement de grandes bases d’utilisateurs et la multilocation.
Les clés primaires et les clés de partition sont toutes deux fondamentales pour maintenir l’intégrité structurelle et l’efficacité opérationnelle des bases de données vectorielles, ce qui les rend indispensables pour gérer de vastes ensembles de données et garantir un accès et une récupération rapides des données.
Choix des types d’embeddings vectoriels
Lors du choix des embeddings vectoriels pour les applications RAG, il est indispensable de choisir le bon modèle de ML pour la création de vecteurs et de comprendre les différents types d’embeddings disponibles : embeddings denses, creux et binaires.
Trois catégories populaires d’embeddings vectoriels
Embeddings denses sont le type le plus couramment utilisé dans les applications de bases de données vectorielles pour la recherche de similarité sémantique. Ils sont connus pour leur robustesse et leur applicabilité générale à divers types de données. Les modèles populaires d’embeddings denses incluent OpenAI, BGE et Cohere.
Embeddings clairsemés gagnent en popularité pour leur efficacité dans la recherche de données hors domaine. Les avancées récentes dans des modèles tels que Splade et BGE M3 ont renforcé leur utilité dans les recherches hétérogènes, en faisant un choix polyvalent pour diverses applications.
Embeddings binaires, caractérisés par leur format binaire (zéros et uns), sont conçus pour être économes en mémoire, ce qui les rend idéaux pour des cas d’utilisation particuliers comme le séquençage des protéines. Des modèles comme Meta ESM-2 sont généralement utilisés pour générer ces embeddings, fournissant des solutions ciblées pour des besoins de recherche spécifiques.
Pour garantir l’exactitude des résultats de recherche pour les applications RAG, nous devons utiliser plus que de simples embeddings denses. Par conséquent, nous devons trouver des solutions qui prennent en charge différents algorithmes d’indexation afin de rechercher efficacement différents types d’embeddings vectoriels. Milvus prend en charge divers index pour gérer les embeddings denses, clairsemés, binaires, et même hybrides clairsemés et denses, permettant des recherches efficaces dans diverses dimensions de données et garantissant des performances optimales dans les applications de bases de données vectorielles.
Concevoir votre schéma : un exemple pratique
Rassemblons les éléments que nous venons d’examiner pour nous aider à concevoir efficacement une architecture de schéma qui augmentera la précision de nos résultats de recherche :
| Nom du champ | Type | Description | Exemple de valeur |
|---|---|---|---|
| chunkID | Int64 | Clé primaire, identifie de manière unique différentes parties d’un document | 123456789 |
| userID | Int64 | Clé de partition, le partitionnement des données est basé sur userID afin de garantir que les recherches s’effectuent au sein d’un seul userID | 987654321 |
| docID | Int64 | Identifiant unique d’un document, utilisé pour associer différents fragments du même document | 555666777 |
| chunkData | varchar | Une partie du document, contenant plusieurs centaines d’octets de texte | "Ceci est une partie du document..." |
| dynamicParams | JSON | Stocke les paramètres dynamiques du document, tels que le nom, l’URL source, etc. | {"name": "Document d’exemple", "source": "example.com"} |
| sparseVector | Format spécifique | Données représentant un vecteur clairsemé. Le format spécifique n’aura des valeurs non nulles qu’à certaines positions pour représenter la parcimonie. | [0.1, 0, 0, 0.8, 0.4] |
| denseVector | Format spécifique | Données représentant un vecteur dense. Le format spécifique aura un nombre fixe de dimensions avec des valeurs dans chacune. | [0.2, 0.3, 0.4, 0.1] |
Une démonstration de schéma pour une application typique de génération augmentée par récupération (RAG)
En examinant ce schéma, il est crucial de noter la présence de champs supplémentaires, allant au-delà de la clé primaire et du champ vectoriel. Ces champs supplémentaires jouent un rôle dans la construction et l’utilisation de votre base de données vectorielle. Approfondissons :
Les bases : Elles incluent votre clé primaire (chunkID) et vos embeddings (denseVector), dont chaque entrée de la base de données a besoin.
Prise en charge du multi-tenant : Nous avons ajouté un champ pour partitionner les données selon les tenants de notre solution avec l’ajout d’un userID. Cet ajout aide à séparer et à gérer l’accès aux données utilisateur par utilisateur, renforçant la sécurité et la personnalisation.
Affiner vos résultats de recherche : Nous avons ajouté quelques autres champs pour nous aider dans cet affinage, notamment :
docID: Ce champ indique l’origine du chunk et peut être utilisé pour tirer parti de la fonctionnalité de recherche par regroupement de Milvus. Dans notre exemple, nous divisons notre document en chunks et stockons l’embedding vectoriel représentatif dans le champ denseVector, et dans ce champ (docID), nous stockons les informations du document associé. Vous pouvez inclure l’argumentgroup_by_fielddans l’opération search() pour regrouper les résultats par ID de document afin de trouver des documents pertinents plutôt que des passages ou chunks similaires. Cela permet de renvoyer les documents pertinents plutôt que des chunks séparés issus du même document.dynamicParams: Ce champ peut être filtré, ce qui vous permet de gérer et de récupérer des données adaptées à vos besoins. C’est une véritable mine de métadonnées, telles que le nom du document, l’URL source, etc. Nous définissons le champ json pour stocker plusieurs paires clé-valeur dans un seul champ.sparseVector: Ce champ contient l’embedding sparse du chunk, ce qui nous permet d’effectuer une recherche ANN et de récupérer des résultats sur la base d’une valeur scalaire associée au vecteur sparse.
Le diagramme montre que nous pouvons rassembler les résultats de requêtes distinctes, puis les reranker afin d’affiner les résultats de recherche.
En concevant un schéma qui inclut ces champs supplémentaires, vous pouvez créer une base de données vectorielle plus robuste et flexible, adaptée aux exigences de votre application RAG. Elle vous permet de tirer parti des points forts de la recherche vectorielle tout en intégrant des techniques traditionnelles de gestion et de récupération des données.
Planifier la scalabilité
Une fois que votre application RAG MVP a rencontré le succès, il est temps de commencer à préparer le déploiement en production. Cela implique d’anticiper la croissance future et de concevoir votre architecture de manière à prendre en charge l’augmentation des volumes de données et du trafic utilisateur.
Pour garantir que votre application puisse évoluer efficacement, vous devez savoir que la scalabilité des bases de données vectorielles pose des défis uniques par rapport aux bases de données relationnelles traditionnelles, car les données sont stockées dans un grand index centralisé. Cette configuration peut entraîner deux problèmes principaux : des vitesses d’indexation lentes et une qualité d’index dégradée en raison de mises à jour fréquentes, ce qui peut à son tour réduire la qualité de la recherche.
La stratégie de sharding de Milvus
Milvus peut relever ces défis en divisant l’ensemble du jeu de données en segments gérables ; nous pouvons effectuer des mises à jour différées ou compacter les segments une fois qu’ils deviennent instables, en maintenant une qualité de recherche constante. Cette segmentation facilite un équilibrage de charge efficace, nous permettant de répartir les requêtes uniformément sur tous les cœurs de traitement.
L’utilisation de partitions pour le multi-tenant peut également contribuer à la scalabilité et aux performances, car les recherches sont confinées aux données pertinentes de la partition. Cette approche organise efficacement les données et renforce la sécurité et la confidentialité en limitant la visibilité aux utilisateurs appropriés. En outre, Milvus peut gérer efficacement jusqu’à dix milliards de points de données dans une seule collection. C’est énorme !
Pour les applications multi-tenant comptant moins de 10 000 tenants, la gestion des données par collection offre un meilleur contrôle des données. Cependant, les clés de partition peuvent prendre en charge efficacement un nombre illimité de tenants en segmentant dynamiquement les données pour des services comptant des millions d’utilisateurs.
Milvus est un système distribué conçu pour gérer facilement de grands volumes de requêtes. Et le meilleur dans tout ça ? Le simple ajout de nœuds supplémentaires peut améliorer considérablement les performances, ouvrant tout un monde de possibilités pour vos applications. Pour les jeux de données plus petits qui ne nécessitent pas initialement de ressources importantes, l’augmentation des réserves de mémoire — généralement deux à trois fois l’allocation actuelle — peut effectivement doubler les requêtes par seconde (QPS). Ce cadre scalable garantit qu’à mesure que vos données augmentent, les capacités de votre base de données peuvent évoluer avec elles, assurant des performances efficaces et fiables dans tous les domaines.
Choisir, évaluer et ajuster votre index
Pendant la phase de prototype, charger toutes les données en mémoire est courant pour accélérer le traitement et faciliter le développement. Cependant, lorsque vous passez en production et que vos données augmentent, il devient impossible de tout stocker en mémoire. Cela s’explique par les raisons suivantes :
La mémoire est limitée et coûteuse par rapport au stockage sur disque.
Les grands jeux de données peuvent dépasser la capacité mémoire disponible.
Charger toutes les données en mémoire peut augmenter considérablement le temps de démarrage et la consommation de ressources.
Pour gérer efficacement des jeux de données plus volumineux en production, vous devez choisir une stratégie d’indexation appropriée. Le bon index peut optimiser les performances de votre application RAG en termes de vitesse de requête, d’exigences de stockage et de latence.
Indices pris en charge par Milvus
Ce diagramme aide à visualiser les différences entre divers index sur la base de trois métriques clés :
Requêtes par seconde (QPS) : cela mesure le nombre de requêtes de recherche que l’index peut traiter par seconde, reflétant son débit et son efficacité.
Stockage : cela représente la quantité d’espace disque nécessaire pour stocker l’index, ce qui peut avoir un impact sur les coûts d’infrastructure et l’évolutivité.
La latence désigne le temps nécessaire pour traiter une seule requête et renvoyer les résultats, ce qui affecte la réactivité de votre application.
En comparant ces métriques entre différents index, vous pouvez décider quel index convient le mieux à votre cas d’utilisation spécifique et à vos exigences de performance.
Dans Milvus, nous fournissons un cadre flexible de sélection d’index adapté à différents besoins de stockage et de performance :
GPU Index est notre option phare pour les environnements haute performance, prenant en charge un traitement et une récupération rapides des données.
Memory Index est une option intermédiaire qui équilibre performance et capacité, offrant de solides taux de requêtes par seconde (QPS) et la capacité de monter en charge jusqu’à des téraoctets de stockage avec une latence moyenne d’environ 10 millisecondes.
Disk Index peut gérer des dizaines de téraoctets avec une latence raisonnable d’environ 100 millisecondes, adapté aux jeux de données plus volumineux et moins sensibles au temps. Milvus est la seule base de données vectorielle open source qui prend en charge l’index sur disque.
Swap Index facilite l’échange de données entre S3 ou d’autres solutions de stockage d’objets et la mémoire. Cette approche réduit considérablement les coûts — d’environ dix fois — tout en gérant efficacement la latence. Les temps d’accès typiques sont d’environ 100 millisecondes, mais ils peuvent s’étendre à quelques secondes pour les données moins fréquemment consultées (« plus froides »), ce qui la rend viable pour les cas d’utilisation hors ligne et les applications sensibles aux coûts.
Après avoir sélectionné un index, vous pouvez évaluer ses performances en fonction de son temps de construction, de sa précision, de ses performances et de sa consommation de ressources. Par exemple, un index non optimisé pourrait ne prendre en charge que 20 requêtes par seconde sans nécessiter de construction. L’optimisation d’un index pourrait améliorer considérablement les QPS, les augmentant potentiellement par dix à chaque itération de réglage, bien qu’au prix d’un temps de visualisation accru.
Pour sélectionner et affiner efficacement votre index, vous devez :
Choisir le type d’index approprié en fonction de vos besoins spécifiques.
Ajuster les paramètres de l’index afin d’optimiser les performances.
Évaluer vos cas d’utilisation avec des benchmarks pour vous assurer que l’index fonctionne comme prévu.
Ajuster les paramètres de recherche afin d’améliorer davantage les performances.
Si vous n’êtes pas sûr du processus d’optimisation, exploitez la puissance d’outils de benchmarking comme VectorDBBench. Cet outil, développé et publié en open source par Zilliz, peut évaluer toutes les bases de données vectorielles grand public. Il vous permet de mener des expérimentations complètes et d’affiner votre système pour obtenir des performances optimales.
Pour une référence rapide, nous avons préparé un aide-mémoire pratique qui décrit les performances de chaque index dans notre catalogue d’index GPU. Cette ressource peut vous aider à optimiser les performances et la rentabilité en vous guidant vers le meilleur index correspondant aux besoins de votre application.
Un aide-mémoire sur les index
Résumé
Dans ce guide complet, nous avons exploré le monde aux multiples facettes des bases de données vectorielles et les approches pratiques nécessaires pour maximiser leur efficacité et leur évolutivité. Des bases de la conception d’un schéma aux complexités de la gestion de grands ensembles de données, nous avons couvert les stratégies essentielles et les bonnes pratiques que les développeurs doivent connaître lorsqu’ils travaillent avec des bases de données vectorielles.
À mesure que les bases de données vectorielles évoluent, rester informé de ces aspects permettra aux développeurs de créer des applications plus robustes, efficaces et évolutives. Que vous soyez un professionnel chevronné des bases de données ou un nouveau venu dans ce domaine, les informations fournies ici vous aideront à naviguer dans les complexités des bases de données vectorielles avec plus de confiance et de compétence.
Continuer à lire

Why I’m Against Claude Code’s Grep-Only Retrieval? It Just Burns Too Many Tokens
Learn how vector-based code retrieval cuts Claude Code token consumption by 40%. Open-source solution with easy MCP integration. Try claude-context today.

Creating Collections in Zilliz Cloud Just Got Way Easier
We've enhanced the entire collection creation experience to bring advanced capabilities directly into the interface, making it faster and easier to build production-ready schemas without switching tools.

Vector Databases vs. Document Databases
Use a vector database for similarity search and AI-powered applications; use a document database for flexible schema and JSON-like data storage.



