Recherche sémantique dans PostgreSQL avec pgvector et Node.js, aucune IA n’est nécessaire
Recherche sémantique dans PostgreSQL avec pgvector et Node.js, sans LLM : embeddings MiniLM, chunks, index IVFFlat, opérateurs de distance et méthode pour lire les scores.
Sources: Talk to Your Database Like a Human — No LLM Required: Semantic Search with Node.js + PostgreSQL
Une base de données peut répondre aux questions posées en anglais courant, sans avoir besoin d’un modèle de langage à aucun stade du processus. Ce qui rend cela possible, c’est la recherche vectorielle. Avec Node.js, PostgreSQL et l’extension pgvector, vous pouvez créer un système qui compare les intentions plutôt que les mots-clés, et le seul modèle utilisé est un petit modèle d’embedding qui transforme le texte en nombres.
Les bases de données relationnelles ont toujours été excellentes pour stocker et récupérer des données, à condition de poser la bonne question. Les correspondances exactes, les filtres et les requêtes structurées sont puissants mais limités. Les bases de données vectorielles semblent traiter un problème différent : rechercher du contenu non structuré en fonction de son sens, et non en fonction des mots qu’il contient.
Quatre termes à bien comprendre
Token. C’est la plus petite unité de texte avec laquelle un modèle travaille. Les tokens ne sont pas toujours des mots entiers. Un token peut être un mot complet comme « hello », un fragment (ainsi « unbelievable » pourrait devenir « un », « believ » et « able »), un seul caractère comme « ! » ou un peu de ponctuation ou d’espaces. Une phrase comme « ChatGPT is great » pourrait être divisée en « Chat », « G », « PT », « is » et « great ».
Chunk. Il s’agit d’un petit fragment autonome d’un document plus grand, généralement composé de quelques phrases ou d’un paragraphe. Il est difficile d’incorporer un document entier d’un seul coup, c’est pourquoi on le divise d’abord. La segmentation en chunks accélère le traitement, améliore la précision des recherches et garantit que les résultats restent pertinents par rapport à la requête.
Embedding. Un substitut numérique pour un texte, une image ou un audio qui capture son sens. Un modèle d’apprentissage automatique, comme MiniLM, le génère. On peut considérer un embedding comme l’idée sous-jacente aux données.
Vector. La manière dont un embedding est effectivement stocké : un tableau de nombres.
[0.2, -0.5, 0.9, ...]
C’est la structure qui est conservée en mémoire ainsi que dans PostgreSQL une fois qu’une extension comme pgvector a été installée.
En résumé, l’idée est la suivante : les mots, les phrases ou les images se transforment en embeddings. Les éléments ayant des significations similaires se retrouvent avec des vecteurs proches les uns des autres, tandis que les éléments non liés se trouvent plus éloignés. Une chaîne de caractères comme « Hello world » se transforme en une longue liste de nombres flottants :
// "Hello world" turns into this embedding:
const embedding = [0.01, -0.42, 0.15, ..., 0.99]; // ← this is a vector
et cette liste est ensuite enregistrée à côté du texte original :
INSERT INTO documents (content, embedding)
VALUES ('Hello world', embedding);
Pourquoi une base de données relationnelle ne suffit pas
Un SGBDR traditionnel fonctionne avec des tables, des lignes et des colonnes. Il gère sans effort des requêtes telles que « tous les tickets avec le statut OUVERT » ou « utilisateurs créés après 2024 ». Cependant, les applications modernes traitent également des documents, des conversations, des images et de l’audio. Une recherche par correspondance exacte ne sert à peu de chose avec ces types de données.
Une base de données vectorielle stocke des embeddings aux côtés des données et trouve des éléments en fonction de leur similarité et de leur contexte. Ainsi, au lieu de demander des documents contenant le mot « erreur », on peut demander des documents sur des problèmes d’authentification, et les obtenir même si le mot « erreur » n’apparaît jamais. La comparaison se fait sur une distance mathématique entre des vecteurs, et non sur une correspondance de texte.
Le flux de travail habituel est le suivant :
- Le contenu brut arrive : documents, tickets ou tout autre élément.
- Un modèle d’incorporation le convertit en vecteurs.
- Ces vecteurs sont stockés, par exemple, dans PostgreSQL à l’aide de pgvector.
- Un index de voisin le plus proche approximatif (ANN) permet des recherches rapides.
- La requête de l’utilisateur est également convertie en vecteur.
- La base de données renvoie les vecteurs les plus proches, c’est-à-dire ceux ayant le sens le plus similaire.
Cela permet :
- Une recherche sémantique, basée sur le sens d’une requête et non seulement sur ses mots exacts.
- Une mémoire à long terme pour un LLM, en conservant des interactions, des documents ou des faits passés que le modèle peut rappeler même s’ils ne faisaient pas partie de ses données d’entraînement.
- Des recommandations, en identifiant des éléments ou des comportements similaires dans l’espace d’incorporation.
- Une recherche de similarité sur du texte, des images, de l’audio ou de la vidéo.
PostgreSQL en tant que stockage de vecteurs
Vous n’avez pas besoin d’adopter une nouvelle base de données pour tout cela. Avec pgvector, PostgreSQL peut stocker directement des embeddings, exécuter des requêtes de similarité et utiliser des index tels que IVFFlat pour rester rapide. La base de données que vous utilisez déjà acquiert simplement une nouvelle capacité, allant du stockage de données à la possibilité de comparer leur signification. La recherche cesse d’être une question de correspondance de mots pour devenir une question de correspondance de sens.
Mise en place étape par étape
1. Installez PostgreSQL et activez pgvector. Téléchargez PostgreSQL depuis la page officielle, connectez-vous et activez l’extension :
CREATE EXTENSION IF NOT EXISTS vector;
2. Créez la table des documents. Elle contient le texte brut et son embedding. Le 384 mentionné dans la définition correspond à la taille de sortie du modèle all-MiniLM-L6-v2.
-- Note: 384 is the embedding size output by all-MiniLM-L6-v2
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
embedding VECTOR(384)
);
3. Ajouter un index vectoriel. Pour maintenir l’efficacité de la recherche de similarité, utilisez un index de voisin le plus proche approximatif :
CREATE INDEX documents_embedding_idx
ON documents
USING ivfflat (embedding vector_l2_ops)
WITH (lists = 100);
Cela choisit IVFFlat pour des recherches approximatives rapides, mesure avec la distance L2 (euclidienne) et divise les vecteurs en 100 clusters.
4. Associez l’opérateur de requête à l’index. L’opérateur utilisé dans votre requête doit correspondre à la classe d’opérateurs avec laquelle vous avez construit l’index. pgvector propose plusieurs opérateurs, chacun associé à sa propre classe. L’exemple ici utilise <#> et configure vector_l2_ops. Notez que dans la documentation de pgvector, la distance L2 est généralement associée à <->, tandis que <#> représente le produit scalaire négatif ; assurez-vous donc que votre requête et votre index soient compatibles.
5. Exécutez le projet. Un exemple fonctionnel complet se trouve dans un répertoire GitHub nommé vectorgarquer/vectorized-database-embeddings. Clonez-le ou créez une branche à partir de lui, puis installez les dépendances nécessaires :
npm install
Copiez .env.example dans un fichier .env situé dans la racine du projet et remplissez les informations de connexion à PostgreSQL. Ensuite, lancez-le :
npm start
6. Observez sa recherche. Une fois lancé, le programme se connecte, insère des embeddings et effectue des recherches sémantiques sans aucune entrée supplémentaire. Vous pouvez ensuite lui fournir vos propres données et requêtes. L’exemple recherche "Qu’est-ce que l’IA générative ?", et les résultats trouvés dans la base de données s’affichent comme ceci :
Search Results:
[1] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[2] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[3] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[4] Distance: -0.7562
n be represented in another modality such as text.
2.2 A Model-, System-, and Application-Level View
of Generative AI
2.2.1 Model-Level View
A generative AI model is a type of machine learning
architecture that uses AI algorithms to create novel data
instances, drawing upon the patterns and relationships
observed in the training data. A generative AI model is of
critically central yet incomplete nature, as it requires fur-
ther fine-tuning to specific tasks thro
[5] Distance: -0.7094
assistants, personalized education and service, and digital
art. As such, generative AI has substantial implications for
BISE practitioners and scholars as an interdisciplinary
research community. In our Catchword article, we offered
a conceptualization of the principles of generative AI along
a model-, system-, and application-level view as well as a
social-technical view and described limitations of current
generative AI. Ultimately, we provided an impactful
rese
Lecture des résultats
Après une exécution, vous obtenez une liste ayant cette forme :
Search Results:
[1] Distance: -0.7562
...
[5] Distance: -0.7094
...
La distance indique à quel point un morceau stocké est proche de l’embedding de la requête. Le morceau ainsi que la requête sont transformés en vecteurs, et PostgreSQL avec pgvector mesure l’écart entre eux. La règle est simple : plus la distance est faible, plus le contenu est similaire.
Les valeurs sont négatives, par exemple :
-0.7562
Cela s’explique par le fait que l’opérateur <#> renvoie une valeur négée, de sorte qu’un tri ordinaire place en premier les lignes les plus proches. En pratique, un nombre plus négatif indique une correspondance plus proche et plus pertinente, tandis qu’un nombre plus proche de zéro indique une correspondance moins forte.
En examinant le classement, les résultats 1 à 4 ont tous la même note de -0,7562, ce qui signifie qu’ils sont également pertinents pour la requête. Le résultat 5 obtient une note de -0,7094, ce qui reste pertinent mais avec une similarité légèrement moindre.
Vous pourriez également remarquer plusieurs résultats présentant un texte presque identique. C’est normal : le document source a été divisé en blocs, les blocs voisins se chevauchent ou répètent du contenu similaire, et chacun possède sa propre représentation vectorielle.
Ce que montre le résultat
Même en l’absence de modèle de langage à grande échelle au moment de la requête, le système comprenait le sens et non seulement les mots-clés, sélectionnait les textes correspondant au contexte et les classait en fonction de leur similarité vectorielle. Les extraits retournés étaient regroupés autour des modèles d’IA générative, d’une vue au niveau du modèle et d’une architecture d’apprentissage automatique, ce qui montre clairement que la recherche fonctionnait au niveau des concepts plutôt que des mots exacts.
Le résultat est un moteur de recherche sémantique intégré à PostgreSQL. Aucun modèle de langage à grande échelle n’est nécessaire pour effectuer des requêtes, il n’y a pas de base de données vectorielle distincte, et les embeddings associés à pgvector s’occupent de tout le travail.
Lectures complémentaires
- Le cache sémantique pour les agents LLM avec Redis : réduire les coûts et la latence — Comment le cache sémantique diminue les dépenses et la latence liées aux LLM : embeddings, seuils, Redis et RedisVL, ajustement de la précision, ainsi que le stockage de sous-questions au sein des workflows des agents.
- Pourquoi les agents d’analyse IA ont besoin d’une couche sémantique plutôt que de SQL brut — La conversion texte en SQL sur des tables de entrepôt brutes crée des métriques arbitraires et ignore les règles d’accès. Découvrez comment une couche sémantique comme Cube, associée à MCP, confère aux agents un sens métier structuré.