Skip to content

Latest commit

 

History

History
1017 lines (828 loc) · 39.5 KB

File metadata and controls

1017 lines (828 loc) · 39.5 KB

🔝 Retour au Sommaire

21.2.2 IA et Machine Learning Intégrés

Introduction

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.


Partie 1 : Comprendre les Concepts Fondamentaux

1.1 Qu'est-ce qu'un Embedding ?

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                  │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

1.2 Pourquoi Stocker des Vecteurs dans PostgreSQL ?

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

1.3 La Recherche Vectorielle

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   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

1.4 Métriques de Distance

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;

Partie 2 : pgvector - L'Extension Vectorielle

2.1 Présentation de pgvector

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.

Installation

-- 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/pgvector

2.2 Le Type vector

pgvector 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
);

Dimensions Courantes

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

2.3 Opérations sur les Vecteurs

-- 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;

2.4 Recherche de Similarité

-- 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;  

2.5 Index pour la Recherche Vectorielle

Sans index, la recherche vectorielle effectue un scan complet (exact mais lent). Les index permettent une recherche approximative mais rapide.

Index IVFFlat (Inverted File Index)

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 / 1000

Options d'opérateurs :

  • vector_l2_ops : Pour distance L2
  • vector_ip_ops : Pour produit scalaire
  • vector_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;  

Index HNSW (Hierarchical Navigable Small World)

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

Comparaison IVFFlat vs HNSW

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

2.6 Bonnes Pratiques pgvector

-- 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;  

Partie 3 : Architectures RAG (Retrieval Augmented Generation)

3.1 Qu'est-ce que RAG ?

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)                                       │  │
│   │                                                                    │  │
│   └────────────────────────────────────────────────────────────────────┘  │
│                                                                           │
└───────────────────────────────────────────────────────────────────────────┘

3.2 Pourquoi RAG ?

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

3.3 Implémentation avec PostgreSQL

Schéma de Base

-- 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 de Recherche RAG

-- 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é
);

Recherche Hybride (Vecteurs + Full-Text)

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;

3.4 Stratégies de Chunking

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
);

Partie 4 : Intégration avec les Modèles d'IA

4.1 Architecture d'Intégration

┌───────────────────────────────────────────────────────────────────────┐
│                    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                             │
│                                                                       │
└───────────────────────────────────────────────────────────────────────┘

4.2 Exemple Python avec LangChain

# 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"])  

4.3 Exemple avec SQL Direct

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)  

4.4 Modèles d'Embedding Locaux

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 dimensions

Partie 5 : Extensions et Outils Complémentaires

5.1 pg_embedding (Neon)

Alternative à 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);  

5.2 pgai (Timescale)

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;

5.3 Outils de l'Écosystème

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

5.4 Plateformes Cloud avec pgvector

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

Partie 6 : Cas d'Usage Concrets

6.1 Recherche Sémantique

-- 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.

6.2 Système de Recommandation

-- 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;  

6.3 Détection de Duplicatas

-- 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;

6.4 Classification Automatique

-- 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;

Partie 7 : Bonnes Pratiques et Considérations

7.1 Performance

-- 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  

7.2 Dimensionnement

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é

7.3 Sécurité et Confidentialité

-- 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()
);

7.4 Coûts

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.


Résumé

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

Points Clés

  • 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'Avenir

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

⏭️ Cloud-native et distributed PostgreSQL