🔝 Retour au Sommaire
L'intelligence artificielle et le Machine Learning transforment la façon dont nous interagissons avec les données. PostgreSQL, grâce à son architecture extensible, s'est positionné comme une base de données de premier choix pour les applications d'IA modernes.
Ce chapitre explore comment PostgreSQL s'intègre dans l'écosystème IA/ML, des embeddings vectoriels aux pipelines de Machine Learning, en passant par les architectures RAG (Retrieval Augmented Generation) qui alimentent les chatbots et assistants IA actuels.
Un embedding (ou plongement vectoriel) est une représentation numérique d'une donnée (texte, image, audio) sous forme de vecteur de nombres. Cette représentation capture le sens ou les caractéristiques de la donnée.
┌─────────────────────────────────────────────────────────────────────────┐
│ Du Texte aux Vecteurs │
│ │
│ Texte original Modèle d'embedding Vecteur │
│ │
│ "Le chat dort" ───► [OpenAI / Cohere / ───► [0.12, -0.45, │
│ HuggingFace] 0.78, 0.23, │
│ ..., -0.31] │
│ (1536 dimensions)│
│ │
│ "Le félin sommeille" ─► [Même modèle] ───► [0.11, -0.44, │
│ 0.79, 0.22, │
│ ..., -0.30] │
│ (vecteur proche!)│
│ │
│ ➜ Des textes similaires produisent des vecteurs proches │
│ ➜ La distance entre vecteurs = similarité sémantique │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Traditionnellement, les vecteurs étaient stockés dans des bases spécialisées (Pinecone, Milvus, Weaviate). Mais stocker les vecteurs dans PostgreSQL offre de nombreux avantages :
| Avantage | Description |
|---|---|
| Unification | Une seule base pour les données relationnelles ET vectorielles |
| Transactions ACID | Cohérence garantie entre métadonnées et vecteurs |
| SQL familier | Pas besoin d'apprendre un nouveau langage de requête |
| Jointures | Combiner recherche vectorielle et filtres SQL |
| Écosystème | Bénéficier de l'outillage PostgreSQL existant |
| Coût | Éviter une infrastructure supplémentaire |
La recherche vectorielle trouve les éléments les plus similaires à une requête, basée sur la distance entre vecteurs.
┌─────────────────────────────────────────────────────────────────────────┐
│ Recherche par Similarité │
│ │
│ Espace vectoriel (simplifié en 2D) │
│ │
│ ▲ │
│ │ • "voiture rouge" │
│ │ • "automobile écarlate" │
│ │ ★ Requête: (très proche!) │
│ │ "véhicule rouge" │
│ │ │
│ │ │
│ │ • "maison bleue" │
│ │ • "chat noir" (éloigné) │
│ │ │
│ └──────────────────────────────────────────────────────────► │
│ │
│ La recherche retourne les K vecteurs les plus proches de la requête │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Plusieurs métriques permettent de mesurer la similarité entre vecteurs :
| Métrique | Symbole | Description | Cas d'usage |
|---|---|---|---|
| Distance L2 (Euclidienne) | <-> |
Distance géométrique directe | Usage général |
| Produit scalaire (Inner Product) | <#> |
Projection d'un vecteur sur l'autre | Modèles normalisés |
| Distance Cosinus | <=> |
Angle entre les vecteurs (0-2) | Texte, recommandations |
| Distance L1 (Manhattan) | <+> |
Somme des différences absolues | Cas spécifiques |
-- Exemples avec pgvector
-- Distance L2 (plus petit = plus proche)
SELECT * FROM documents ORDER BY embedding <-> query_vector LIMIT 5;
-- Distance Cosinus (plus petit = plus similaire)
SELECT * FROM documents ORDER BY embedding <=> query_vector LIMIT 5;
-- Produit scalaire (plus grand = plus similaire, noter le signe négatif)
SELECT * FROM documents ORDER BY embedding <#> query_vector LIMIT 5;pgvector est l'extension open-source de référence pour le stockage et la recherche de vecteurs dans PostgreSQL. Créée par Andrew Kane, elle est devenue un standard de l'industrie.
-- Installer l'extension (nécessite les droits superuser)
CREATE EXTENSION vector;
-- Vérifier l'installation
SELECT * FROM pg_extension WHERE extname = 'vector';# Installation sur Ubuntu/Debian
sudo apt install postgresql-17-pgvector
# Installation sur macOS avec Homebrew
brew install pgvector
# Avec Docker (image officielle)
docker run -e POSTGRES_PASSWORD=secret ankane/pgvectorpgvector introduit le type vector pour stocker des tableaux de nombres à virgule flottante.
-- Créer une table avec une colonne vectorielle
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
titre VARCHAR(255) NOT NULL,
contenu TEXT,
embedding vector(1536) -- Vecteur de 1536 dimensions (OpenAI)
);
-- Insérer un vecteur
INSERT INTO documents (titre, contenu, embedding)
VALUES (
'Introduction à PostgreSQL',
'PostgreSQL est un système de gestion...',
'[0.12, -0.45, 0.78, ...]'::vector -- 1536 valeurs
);
-- Ou avec la syntaxe array
INSERT INTO documents (titre, contenu, embedding)
VALUES (
'Guide SQL',
'SQL est le langage standard...',
ARRAY[0.12, -0.45, 0.78, ...]::vector
);| Modèle | Dimensions | Fournisseur |
|---|---|---|
| text-embedding-3-small | 1536 | OpenAI |
| text-embedding-3-large | 3072 | OpenAI |
| text-embedding-ada-002 | 1536 | OpenAI |
| embed-english-v3.0 | 1024 | Cohere |
| all-MiniLM-L6-v2 | 384 | HuggingFace |
| bge-large-en | 1024 | BAAI |
| nomic-embed-text | 768 | Nomic |
-- Distance L2 (Euclidienne)
SELECT embedding <-> '[0.1, 0.2, 0.3]'::vector AS distance_l2
FROM documents;
-- Distance Cosinus
SELECT embedding <=> '[0.1, 0.2, 0.3]'::vector AS distance_cosinus
FROM documents;
-- Produit scalaire (négatif pour ORDER BY ASC)
SELECT (embedding <#> '[0.1, 0.2, 0.3]'::vector) * -1 AS similarite
FROM documents;
-- Opérations mathématiques
SELECT
embedding + '[0.1, 0.2, ...]'::vector AS addition,
embedding - '[0.1, 0.2, ...]'::vector AS soustraction,
embedding * '[0.1, 0.2, ...]'::vector AS multiplication
FROM documents;
-- Fonctions utilitaires
SELECT
vector_dims(embedding) AS dimensions, -- Nombre de dimensions
vector_norm(embedding) AS norme, -- Norme L2 du vecteur
l2_distance(v1, v2) AS distance, -- Distance L2
cosine_distance(v1, v2) AS cos_dist, -- Distance cosinus
inner_product(v1, v2) AS produit -- Produit scalaire
FROM documents;-- Trouver les 5 documents les plus similaires à un vecteur de requête
SELECT
id,
titre,
embedding <=> query_embedding AS distance
FROM documents
ORDER BY embedding <=> query_embedding
LIMIT 5;
-- Recherche avec seuil de distance
SELECT id, titre, embedding <=> query_embedding AS distance
FROM documents
WHERE embedding <=> query_embedding < 0.5 -- Seuil de similarité
ORDER BY distance
LIMIT 10;
-- Combiner avec des filtres SQL classiques
SELECT id, titre, distance
FROM documents
WHERE categorie = 'technologie'
AND date_publication > '2024-01-01'
ORDER BY embedding <=> query_embedding
LIMIT 5; Sans index, la recherche vectorielle effectue un scan complet (exact mais lent). Les index permettent une recherche approximative mais rapide.
Divise l'espace en clusters et cherche dans les clusters les plus proches.
-- Créer un index IVFFlat
CREATE INDEX idx_documents_embedding_ivf
ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100); -- Nombre de clusters
-- Paramètres recommandés pour 'lists':
-- Jusqu'à 1M lignes : lists = sqrt(n_rows)
-- Plus de 1M lignes : lists = sqrt(n_rows) ou n_rows / 1000Options d'opérateurs :
vector_l2_ops: Pour distance L2vector_ip_ops: Pour produit scalairevector_cosine_ops: Pour distance cosinus
-- Contrôler le nombre de clusters à explorer (trade-off précision/vitesse)
SET ivfflat.probes = 10; -- Défaut: 1, plus = plus précis mais plus lent
-- Pour une requête spécifique
SELECT * FROM documents
ORDER BY embedding <=> query_vector
LIMIT 5; Structure de graphe hiérarchique, généralement plus performant que IVFFlat.
-- Créer un index HNSW
CREATE INDEX idx_documents_embedding_hnsw
ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Paramètres :
-- m : Nombre de connexions par nœud (défaut: 16)
-- ef_construction: Taille de la liste de candidats lors de la construction (défaut: 64)-- Contrôler la précision lors de la recherche
SET hnsw.ef_search = 40; -- Défaut: 40, plus = plus précis mais plus lent| Critère | IVFFlat | HNSW |
|---|---|---|
| Vitesse de recherche | Rapide | Très rapide |
| Précision | Bonne | Excellente |
| Temps de construction | Rapide | Lent |
| Utilisation mémoire | Faible | Élevée |
| Mises à jour | Performantes | Moins performantes |
| Cas d'usage | Données changeantes | Données statiques |
-- 1. Normaliser les vecteurs pour la distance cosinus
-- (améliore les performances avec inner product)
UPDATE documents
SET embedding = embedding / vector_norm(embedding)
WHERE vector_norm(embedding) > 0;
-- 2. Utiliser le bon type d'index selon le volume
-- < 10K lignes : Pas d'index (exact search rapide)
-- 10K - 100K : IVFFlat avec lists = sqrt(n)
-- > 100K : HNSW
-- 3. Créer l'index APRÈS l'insertion des données
INSERT INTO documents (titre, embedding) VALUES ...; -- Insertions massives
CREATE INDEX idx_embedding ON documents USING hnsw (embedding vector_cosine_ops);
-- 4. Surveiller l'utilisation de l'index
EXPLAIN ANALYZE
SELECT * FROM documents
ORDER BY embedding <=> '[...]'::vector
LIMIT 10; RAG (Retrieval Augmented Generation) est une architecture qui améliore les réponses des LLM (Large Language Models) en leur fournissant du contexte pertinent récupéré depuis une base de connaissances.
┌───────────────────────────────────────────────────────────────────────────┐
│ Architecture RAG │
│ │
│ 1. INDEXATION (préparation) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Documents ───► Chunking ───► Embedding ───► PG │ │
│ │ (PDF, Web, (découpage) (vectorisation) avec │ │
│ │ Markdown) pgvector │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ 2. REQUÊTE (utilisation) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Question ───► Embedding ───► Recherche ───► │ │
│ │ utilisateur de la vectorielle Top K │ │
│ │ question dans PG docs │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ 3. GÉNÉRATION │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Question ───► LLM ───► Réponse │ │
│ │ + (GPT-4, contextualisée │ │
│ │ Contexte Claude, et précise │ │
│ │ (Top K docs) Llama) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────┘
Les LLM ont des limitations que RAG résout :
| Problème LLM | Solution RAG |
|---|---|
| Connaissances figées | Accès à des données à jour |
| Hallucinations | Réponses basées sur des sources vérifiables |
| Données privées | Intégration de documents d'entreprise |
| Coût | Évite le fine-tuning coûteux |
| Traçabilité | Citations et sources identifiables |
-- Table principale des documents
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
titre VARCHAR(255) NOT NULL,
source VARCHAR(255), -- URL, chemin fichier, etc.
contenu_original TEXT, -- Texte complet
metadata JSONB DEFAULT '{}', -- Métadonnées flexibles
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Table des chunks (morceaux de documents)
CREATE TABLE chunks (
id SERIAL PRIMARY KEY,
document_id INTEGER REFERENCES documents(id) ON DELETE CASCADE,
contenu TEXT NOT NULL, -- Texte du chunk
embedding vector(1536), -- Vecteur du chunk
position INTEGER, -- Position dans le document original
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Index pour la recherche vectorielle
CREATE INDEX idx_chunks_embedding ON chunks
USING hnsw (embedding vector_cosine_ops);
-- Index pour les filtres courants
CREATE INDEX idx_chunks_document_id ON chunks(document_id);
CREATE INDEX idx_documents_metadata ON documents USING gin(metadata); -- Fonction pour rechercher les chunks pertinents
CREATE OR REPLACE FUNCTION recherche_rag(
query_embedding vector(1536),
limite INTEGER DEFAULT 5,
seuil_distance FLOAT DEFAULT 0.8
)
RETURNS TABLE (
chunk_id INTEGER,
document_id INTEGER,
titre VARCHAR,
contenu TEXT,
distance FLOAT,
metadata JSONB
) AS $$
BEGIN
RETURN QUERY
SELECT
c.id AS chunk_id,
c.document_id,
d.titre,
c.contenu,
(c.embedding <=> query_embedding)::FLOAT AS distance,
c.metadata || d.metadata AS metadata
FROM chunks c
JOIN documents d ON c.document_id = d.id
WHERE c.embedding <=> query_embedding < seuil_distance
ORDER BY c.embedding <=> query_embedding
LIMIT limite;
END;
$$ LANGUAGE plpgsql;
-- Utilisation
SELECT * FROM recherche_rag(
'[0.12, -0.45, ...]'::vector, -- Embedding de la question
5, -- Nombre de résultats
0.7 -- Seuil de similarité
);Combiner la recherche sémantique (vecteurs) et lexicale (full-text) améliore souvent les résultats.
-- Ajouter une colonne tsvector pour la recherche full-text
ALTER TABLE chunks ADD COLUMN contenu_tsv tsvector
GENERATED ALWAYS AS (to_tsvector('french', contenu)) STORED;
CREATE INDEX idx_chunks_tsv ON chunks USING gin(contenu_tsv);
-- Fonction de recherche hybride
CREATE OR REPLACE FUNCTION recherche_hybride(
query_text TEXT,
query_embedding vector(1536),
limite INTEGER DEFAULT 5,
poids_semantic FLOAT DEFAULT 0.7, -- Poids de la recherche vectorielle
poids_lexical FLOAT DEFAULT 0.3 -- Poids de la recherche full-text
)
RETURNS TABLE (
chunk_id INTEGER,
contenu TEXT,
score_final FLOAT
) AS $$
BEGIN
RETURN QUERY
WITH scores AS (
SELECT
c.id,
c.contenu,
-- Score sémantique (convertir distance en similarité)
1 - (c.embedding <=> query_embedding) AS score_semantic,
-- Score lexical (normalisé)
COALESCE(ts_rank_cd(c.contenu_tsv, plainto_tsquery('french', query_text)), 0) AS score_lexical
FROM chunks c
WHERE c.contenu_tsv @@ plainto_tsquery('french', query_text)
OR c.embedding <=> query_embedding < 0.8
)
SELECT
s.id,
s.contenu,
(s.score_semantic * poids_semantic + s.score_lexical * poids_lexical) AS score_final
FROM scores s
ORDER BY score_final DESC
LIMIT limite;
END;
$$ LANGUAGE plpgsql;Le découpage des documents en chunks est crucial pour la qualité du RAG.
┌────────────────────────────────────────────────────────────────────────┐
│ Stratégies de Chunking │
│ │
│ 1. TAILLE FIXE │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Chunk 1 (500 tokens) │ Chunk 2 (500 tokens) │ Chunk 3 (500)... │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ✓ Simple ✗ Peut couper en milieu de phrase │
│ │
│ 2. AVEC CHEVAUCHEMENT (Overlap) │
│ ┌───────────────────────────────┐ │
│ │ Chunk 1 (500) │ │
│ └─────────────────┬─────────────┘ │
│ │◄── Overlap (50) ──►│ │
│ ┌───────┴─────────────────────┐ │
│ │ Chunk 2 (500) │ │
│ └─────────────────────────────┘ │
│ ✓ Préserve le contexte aux frontières │
│ │
│ 3. SÉMANTIQUE (par paragraphe/section) │
│ ┌──────────────┐ ┌────────────────────┐ ┌─────────────────┐ │
│ │ Introduction │ │ Section 1 │ │ Section 2 │ │
│ │ (variable) │ │ (variable) │ │ (variable) │ │
│ └──────────────┘ └────────────────────┘ └─────────────────┘ │
│ ✓ Respecte la structure ✗ Tailles variables │
│ │
│ 4. RÉCURSIF (LangChain style) │
│ Tente de découper par: \n\n → \n → . → espace → caractère │
│ ✓ Équilibre taille et sens │
│ │
└────────────────────────────────────────────────────────────────────────┘
-- Exemple de stockage avec métadonnées de chunking
INSERT INTO chunks (document_id, contenu, position, metadata)
VALUES (
1,
'PostgreSQL est un système de gestion de bases de données...',
0,
'{
"chunk_size": 500,
"overlap": 50,
"strategy": "recursive",
"section": "introduction"
}'::jsonb
);┌───────────────────────────────────────────────────────────────────────┐
│ Intégration PostgreSQL + IA │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Application │ │
│ │ (Python, Node.js, Go, etc.) │ │
│ └───────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ API │ │ PostgreSQL │ │ LLM API │ │
│ │ Embedding │ │ + pgvector │ │ (OpenAI, │ │
│ │ (OpenAI, │ │ │ │ Anthropic, │ │
│ │ Cohere) │ │ │ │ Local) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ Flux typique : │
│ 1. Texte → API Embedding → Vecteur │
│ 2. Vecteur → PostgreSQL → Documents similaires │
│ 3. Question + Documents → LLM → Réponse │
│ │
└───────────────────────────────────────────────────────────────────────┘
# Installation: pip install langchain langchain-openai pgvector psycopg2-binary
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_postgres import PGVector
from langchain.chains import RetrievalQA
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Configuration
CONNECTION_STRING = "postgresql://user:password@localhost:5432/mydb"
COLLECTION_NAME = "documents_rag"
# 1. Initialiser les embeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 2. Connexion à PostgreSQL avec pgvector
vectorstore = PGVector(
connection=CONNECTION_STRING,
embeddings=embeddings,
collection_name=COLLECTION_NAME,
use_jsonb=True,
)
# 3. Découper et indexer des documents
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", ".", " ", ""]
)
documents = [...] # Vos documents
chunks = text_splitter.split_documents(documents)
vectorstore.add_documents(chunks)
# 4. Créer une chaîne RAG
llm = ChatOpenAI(model="gpt-4", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
return_source_documents=True
)
# 5. Poser une question
result = qa_chain.invoke({"query": "Qu'est-ce que PostgreSQL ?"})
print(result["result"])
print("Sources:", result["source_documents"]) Pour plus de contrôle, vous pouvez interagir directement avec PostgreSQL :
import psycopg2
import openai
from pgvector.psycopg2 import register_vector
# Connexion PostgreSQL
conn = psycopg2.connect("postgresql://user:password@localhost:5432/mydb")
register_vector(conn)
cur = conn.cursor()
def get_embedding(text: str) -> list:
"""Obtenir l'embedding d'un texte via OpenAI"""
response = openai.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
def recherche_similaire(question: str, limite: int = 5):
"""Rechercher les documents similaires à une question"""
# 1. Obtenir l'embedding de la question
query_embedding = get_embedding(question)
# 2. Rechercher dans PostgreSQL
cur.execute("""
SELECT
id,
contenu,
1 - (embedding <=> %s::vector) AS similarite
FROM chunks
ORDER BY embedding <=> %s::vector
LIMIT %s
""", (query_embedding, query_embedding, limite))
return cur.fetchall()
def generer_reponse(question: str, contexte: str) -> str:
"""Générer une réponse avec le LLM"""
response = openai.chat.completions.create(
model="gpt-4",
messages=[
{
"role": "system",
"content": f"""Réponds à la question en utilisant uniquement
le contexte fourni. Si tu ne peux pas répondre avec le contexte,
dis-le.
Contexte:
{contexte}"""
},
{"role": "user", "content": question}
]
)
return response.choices[0].message.content
# Pipeline RAG complet
def rag_query(question: str) -> str:
# Rechercher les documents pertinents
resultats = recherche_similaire(question)
# Construire le contexte
contexte = "\n\n".join([r[1] for r in resultats])
# Générer la réponse
reponse = generer_reponse(question, contexte)
return reponse
# Utilisation
reponse = rag_query("Comment fonctionne la réplication dans PostgreSQL ?")
print(reponse) Pour éviter les coûts des API et garder les données en local :
# Avec sentence-transformers (HuggingFace)
# pip install sentence-transformers
from sentence_transformers import SentenceTransformer
# Charger un modèle local
model = SentenceTransformer('all-MiniLM-L6-v2') # 384 dimensions, rapide
# Générer des embeddings
texts = ["PostgreSQL est génial", "J'aime les bases de données"]
embeddings = model.encode(texts)
# Insérer dans PostgreSQL
for text, embedding in zip(texts, embeddings):
cur.execute(
"INSERT INTO chunks (contenu, embedding) VALUES (%s, %s)",
(text, embedding.tolist())
)
conn.commit()# Avec Ollama (modèles locaux)
# pip install ollama
import ollama
# Générer un embedding avec un modèle local
response = ollama.embeddings(
model='nomic-embed-text',
prompt='PostgreSQL est un SGBD relationnel'
)
embedding = response['embedding'] # 768 dimensionsAlternative à pgvector développée par Neon, optimisée pour leur plateforme cloud.
-- Installation
CREATE EXTENSION embedding;
-- Utilisation similaire à pgvector
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
embedding real[] -- Utilise des arrays natifs
);
-- Index HNSW
CREATE INDEX ON documents
USING hnsw (embedding ann_cos_ops); Extension qui intègre directement les appels aux API d'IA dans PostgreSQL.
-- Installation
CREATE EXTENSION pgai;
-- Générer des embeddings directement en SQL !
SELECT openai_embed(
'text-embedding-3-small',
'PostgreSQL est fantastique'
);
-- Créer une colonne avec embedding automatique
ALTER TABLE documents
ADD COLUMN embedding vector(1536)
GENERATED ALWAYS AS (
openai_embed('text-embedding-3-small', contenu)
) STORED;| Outil | Description | Lien |
|---|---|---|
| LangChain | Framework pour applications LLM | langchain.com |
| LlamaIndex | Framework RAG spécialisé | llamaindex.ai |
| Haystack | Pipeline NLP open-source | haystack.deepset.ai |
| Unstructured | Parsing de documents | unstructured.io |
| ChromaDB | Alternative légère (dev) | trychroma.com |
| Plateforme | pgvector | Notes |
|---|---|---|
| Supabase | ✅ Inclus | Interface graphique, fonctions Edge |
| Neon | ✅ Inclus | Serverless, autoscaling |
| AWS RDS | ✅ Disponible | À activer manuellement |
| Azure Database | ✅ Disponible | Flexible Server uniquement |
| Google Cloud SQL | ✅ Disponible | À activer |
| Render | ✅ Inclus | Simple et abordable |
-- E-commerce : Rechercher des produits par description
SELECT
p.nom,
p.description,
p.prix,
p.embedding <=> query_embedding AS distance
FROM produits p
WHERE p.categorie = 'électronique'
AND p.prix BETWEEN 100 AND 500
ORDER BY p.embedding <=> query_embedding
LIMIT 10;
-- Le client cherche "appareil pour écouter de la musique sans fil"
-- Trouve : "Casque Bluetooth Sony", "Écouteurs AirPods", etc.-- Recommander des articles similaires à ceux consultés
WITH articles_consultes AS (
SELECT embedding
FROM articles
WHERE id IN (SELECT article_id FROM historique WHERE user_id = 123)
),
centroide AS (
SELECT AVG(embedding) AS embedding_moyen
FROM articles_consultes
)
SELECT
a.id,
a.titre,
a.embedding <=> c.embedding_moyen AS distance
FROM articles a, centroide c
WHERE a.id NOT IN (SELECT article_id FROM historique WHERE user_id = 123)
ORDER BY distance
LIMIT 5; -- Trouver les tickets de support potentiellement dupliqués
SELECT
t1.id AS ticket_original,
t2.id AS ticket_potentiel_doublon,
t1.titre,
t2.titre,
t1.embedding <=> t2.embedding AS similarite
FROM tickets t1
JOIN tickets t2 ON t1.id < t2.id
WHERE t1.embedding <=> t2.embedding < 0.1 -- Très similaires
AND t1.created_at > NOW() - INTERVAL '7 days'
ORDER BY similarite;-- Classifier automatiquement les documents par catégorie
WITH categories_embeddings AS (
SELECT
categorie,
AVG(embedding) AS embedding_moyen
FROM documents_labellises
GROUP BY categorie
)
SELECT
d.id,
d.titre,
(
SELECT categorie
FROM categories_embeddings
ORDER BY embedding_moyen <=> d.embedding
LIMIT 1
) AS categorie_predite
FROM documents_nouveaux d;-- 1. Indexer correctement (APRÈS les insertions massives)
-- Mauvais : créer l'index puis insérer 1M de lignes
-- Bon : insérer 1M de lignes puis créer l'index
-- 2. Utiliser des index partiels si possible
CREATE INDEX idx_chunks_recent ON chunks
USING hnsw (embedding vector_cosine_ops)
WHERE created_at > '2024-01-01';
-- 3. Monitorer les performances
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM chunks
ORDER BY embedding <=> '[...]'::vector
LIMIT 10;
-- 4. Ajuster les paramètres de recherche
SET hnsw.ef_search = 100; -- Plus précis
SET ivfflat.probes = 20; -- Plus précis | Volume de vecteurs | RAM recommandée | Type d'index |
|---|---|---|
| < 10 000 | 2 GB | Aucun (exact) |
| 10 000 - 100 000 | 4-8 GB | IVFFlat |
| 100 000 - 1 M | 16-32 GB | HNSW |
| > 1 M | 64+ GB | HNSW partitionné |
-- 1. Chiffrer les données sensibles avant embedding
-- (les embeddings peuvent révéler des informations)
-- 2. Utiliser Row-Level Security pour les données multi-tenant
ALTER TABLE chunks ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON chunks
FOR ALL
USING (tenant_id = current_setting('app.tenant_id')::integer);
-- 3. Auditer les accès aux données vectorielles
CREATE TABLE audit_recherches (
id SERIAL PRIMARY KEY,
user_id INTEGER,
query_hash TEXT, -- Hash de la requête (pas le texte brut)
resultats_ids INTEGER[],
created_at TIMESTAMP DEFAULT NOW()
);| Opération | Coût Estimé (OpenAI) |
|---|---|
| Embedding (text-embedding-3-small) | $0.02 / 1M tokens |
| Embedding (text-embedding-3-large) | $0.13 / 1M tokens |
| Stockage PostgreSQL | Variable selon hébergement |
| Requêtes LLM (GPT-4) | $30 / 1M tokens (input) |
Conseil : Pour réduire les coûts, utilisez des modèles d'embedding locaux (sentence-transformers) et cachez les embeddings fréquemment utilisés.
Ce chapitre a exploré l'intégration de l'IA et du Machine Learning dans PostgreSQL :
| Concept | Ce qu'il faut retenir |
|---|---|
| Embeddings | Représentation vectorielle du sens |
| pgvector | Extension standard pour les vecteurs |
| Index HNSW/IVFFlat | Recherche approximative rapide |
| RAG | Architecture LLM + recherche vectorielle |
| Recherche hybride | Combiner vecteurs et full-text |
- PostgreSQL + pgvector est une alternative viable aux bases vectorielles spécialisées
- La recherche vectorielle permet une recherche sémantique puissante
- RAG améliore les LLM avec des connaissances contextuelles
- Choisissez HNSW pour la performance, IVFFlat pour les données changeantes
- Considérez les modèles locaux pour la confidentialité et les coûts
L'intégration IA/PostgreSQL continue d'évoluer :
- Support natif des vecteurs dans PostgreSQL core (discussions en cours)
- Nouveaux types d'index optimisés (DiskANN, ScaNN)
- Intégration plus profonde avec les frameworks ML
- Quantification des vecteurs pour réduire le stockage