03-comment-je-travaille/vps/09-ia-embeddings.md

IA — microservice embeddings, RAG, workers Messenger

Ce document couvre la couche IA de Telaria : l'indexation vectorielle (RAG L0) et les workers asynchrones. Pour le serveur MCP (L1) : 10-mcp.md.


1. Architecture IA — vue d'ensemble

Documents Markdown (telaria-doc)
    │
    ▌
app:rag:ingest (CLI Symfony)
    │  chunks + appels embeddings
    ▌
tlr-embeddings (Python / FastAPI)   ← port 8001
    │  vecteurs float32 (768 dimensions)
    ▌
sqlite-vec (extension SQLite)
    │  index kNN persistĂ© dans var/rag/index.sqlite
    ▌
app:rag:search ou interface Chat
    │  requĂȘte → k voisins les plus proches
    ▌
Génération Claude (L2, clé API Anthropic)

Deux composants sont externes Ă  l'application Symfony :

  • tlr-embeddings : microservice Python qui convertit du texte en vecteurs
  • sqlite-vec : extension SQLite (.so natif) qui effectue la recherche kNN

2. tlr-embeddings — microservice Python

RĂŽle

Expose une API HTTP simple :

  • POST /embed → convertit une liste de textes en vecteurs float32 (768 dim)
  • GET /health → statut du service

ModĂšle utilisĂ© : multilingual-e5-base (Hugging Face) — supporte le français et l'anglais.

Installation

# Répertoire d'installation
sudo mkdir -p /opt/tlr-embeddings
sudo chown ubuntu:ubuntu /opt/tlr-embeddings

git clone git@github.com:<owner>/tlr-embeddings.git /opt/tlr-embeddings
cd /opt/tlr-embeddings

# Environnement virtuel Python
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
# requirements.txt pine torch en version CPU-only (pas de GPU requis)
# ⚠ Premier pip install : tĂ©lĂ©charge ~1,1 Go (modĂšle Hugging Face)

Le requirements.lock doit ĂȘtre gĂ©nĂ©rĂ© sur le serveur Linux (pas sur Windows/macOS) — les wheels torch sont spĂ©cifiques Ă  l'OS et Ă  l'architecture.

Service systemd — /etc/systemd/system/tlr-embeddings.service

[Unit]
Description=Telaria Embeddings Service
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/opt/tlr-embeddings
ExecStart=/opt/tlr-embeddings/.venv/bin/python app.py

# Hugging Face stocke le modĂšle en dehors de /tmp (persistant entre reboots)
Environment=HF_HOME=/var/lib/tlr-embeddings/hf

# Premier dĂ©marrage : tĂ©lĂ©charge le modĂšle (~1,1 Go) — 10 min timeout
TimeoutStartSec=600

# Durcissement
ProtectSystem=strict
ReadWritePaths=/var/lib/tlr-embeddings
NoNewPrivileges=yes
PrivateTmp=yes

Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo mkdir -p /var/lib/tlr-embeddings/hf
sudo chown ubuntu:ubuntu /var/lib/tlr-embeddings

sudo systemctl daemon-reload
sudo systemctl enable --now tlr-embeddings

# Vérifier (attendre le chargement du modÚle)
curl http://127.0.0.1:8001/health
# {"status":"ok","model":"multilingual-e5-base","dim":768}

Empreinte mémoire

  • Premier dĂ©marrage : ~1,1 Go tĂ©lĂ©chargĂ©s, mis en cache dans HF_HOME
  • Runtime : ~440 Mo RAM une fois le modĂšle chargĂ©
  • DĂ©marrages suivants : modĂšle chargĂ© depuis le cache local (~30 secondes)

Contrat d'interface /embed

POST http://127.0.0.1:8001/embed
Content-Type: application/json

{
  "type": "query",
  "texts": ["Comment déployer Telaria sur un VPS ?"]
}

type peut ĂȘtre "query" (recherche) ou "passage" (indexation). Tout autre valeur → HTTP 422.


3. sqlite-vec — recherche kNN dans SQLite

Pourquoi SQLite et non pgvector ou Pinecone ?

Sur un VPS sans PostgreSQL, SQLite suffit pour le volume de telaria-doc (~2400 chunks). ZĂ©ro infrastructure supplĂ©mentaire, zĂ©ro coĂ»t, rĂ©sultats cohĂ©rents avec cosinus > 0.8 sur des requĂȘtes en français.

Installation de l'extension

sqlite-vec est une extension SQLite native (.so) qui n'est pas incluse par défaut.

# Télécharger la release loadable (x86-64 Linux)
# Voir https://github.com/asg017/sqlite-vec/releases
wget https://github.com/asg017/sqlite-vec/releases/download/v0.1.x/\
sqlite-vec-0.1.x-loadable-linux-x86_64.tar.gz

tar xzf sqlite-vec-0.1.x-loadable-linux-x86_64.tar.gz
sudo mkdir -p /usr/local/lib/sqlite-vec
sudo mv vec0.so /usr/local/lib/sqlite-vec/
sudo chmod 755 /usr/local/lib/sqlite-vec/vec0.so

Configurer le chemin dans .env.local :

RAG_SQLITE_VEC_PATH=/usr/local/lib/sqlite-vec/vec0.so

Chargement par PHP 8.5

Le bundle tlr-rag charge l'extension via :

$pdo = new \PDO('sqlite:' . $dbPath);
$pdo->loadExtension($vecPath);   // \Pdo\Sqlite::loadExtension() en PHP 8.5

⚠ Si open_basedir est activĂ© dans PHP-FPM, le chemin de vec0.so doit y ĂȘtre inclus. Sur Telaria, open_basedir est dĂ©sactivĂ© en prod pour Ă©viter cette contrainte.


4. Index RAG — ingestion des documents

Structure de l'index

var/rag/
├── index.sqlite         ← base SQLite principale (chunks + mĂ©tadonnĂ©es)
├── index.sqlite-wal     ← Write-Ahead Log (SQLite en Ă©criture)
└── index.sqlite-shm     ← Shared Memory

Les fichiers -wal et -shm sont créés dynamiquement par SQLite lors des Ă©critures. Le rĂ©pertoire doit ĂȘtre accessible en Ă©criture par www-data (lecture/requĂȘtes web) et par l'utilisateur qui lance l'ingest.

Droits — setgid pour hĂ©ritage de groupe

sudo chgrp -R www-data /var/www/telaria/var/rag
sudo chmod -R g+rwX /var/www/telaria/var/rag
sudo chmod g+s /var/www/telaria/var/rag
# setgid : tout nouveau fichier créé dans ce répertoire hérite du groupe www-data

Configuration du bundle RAG

# config/packages/telaria_rag.yaml
telaria_rag:
    source_root: '%kernel.project_dir%/docs'  # clone de telaria-doc

    # Chemins exclus de l'indexation (toujours)
    excluded_paths:
        - 'SCRATCH.md'
        - 'inputs/legacy/'   # contenu brouillon, jamais dans le corpus RAG

    embedding:
        service_url: '%env(RAG_EMBEDDING_URL)%'   # http://127.0.0.1:8001
        model: 'intfloat/multilingual-e5-base'
        dimension: 768
        batch_size: 32

    chunk:
        size: 512     # tokens cible par chunk
        overlap: 64   # chevauchement pour ne pas couper le sens

    retrieval:
        k: 5          # top-k résultats par défaut

    store:
        database_path: '%env(resolve:RAG_DB_PATH)%'
        extension_path: '%env(RAG_SQLITE_VEC_PATH)%'

.aiignore Ă  la racine du corpus remplit le mĂȘme rĂŽle que .gitignore pour l'indexation : tout fichier listĂ© est exclu de l'ingest, mĂȘme s'il est Markdown. inputs/legacy/ y figure par dĂ©faut — contenu de rĂ©fĂ©rence technique non destinĂ© au RAG.

Lancer l'ingestion

# PremiĂšre ingestion (indexation complĂšte)
sudo -u www-data php bin/console app:rag:ingest --full

# Ingestion incrĂ©mentale (nouveaux documents uniquement — compare les content_hash)
sudo -u www-data php bin/console app:rag:ingest

# Ré-indexation incrémentale explicite
sudo -u www-data php bin/console app:rag:reindex

# Statistiques aprĂšs ingestion
php bin/console app:rag:stats
# Attendu : ~180 documents / ~2400 chunks

# Test de recherche
php bin/console app:rag:search "comment déployer Telaria ?" --k=5
# Attendu : top-5 chunks pertinents avec score cosinus > 0.8

Corpus source

Le bundle tlr-rag lit les fichiers Markdown depuis rag.source_root (configuré dans config/packages/telaria_rag.yaml). Par défaut : %kernel.project_dir%/docs/ (un clone de telaria-doc).

# Cloner telaria-doc comme source du corpus
git clone git@github-telaria-doc:<owner>/telaria-doc.git /var/www/telaria/docs

Schéma SQLite de l'index

document     : id, path, title, content_hash, indexed_at
chunk        : id, document_id, section, anchor, position, content, token_count, content_hash
chunk_vector : table virtuelle sqlite-vec (chunk_id ↔ vecteur float32[768])
ingest_run   : id, source_root, started_at, finished_at, docs, chunks, status

chunk_vector est la table virtuelle créée par sqlite-vec. Elle stocke les vecteurs et permet la recherche kNN (SELECT ... FROM chunk_vector WHERE knn_search(...)).

UI de diagnostic /admin/rag

L'interface d'administration expose le travail "invisible" du RAG :

  • AccĂšs : https://telaria.dev/admin/rag (requiert ROLE_ADMIN)
  • Statistiques en temps rĂ©el : nb documents, chunks, date derniĂšre indexation, santĂ© du microservice
  • Formulaire de requĂȘte de test → affichage des chunks rĂ©cupĂ©rĂ©s avec score de similaritĂ© et lien vers la source
  • DĂ©grade proprement si sqlite-vec ou le microservice sont absents (banniĂšre d'avertissement, pas d'erreur 500)

C'est l'artefact "vitrine" du RAG — dĂ©montrer au recruteur ou client que le retrieval fonctionne, avec les scores et les sources.


5. Workers Symfony Messenger — tñches asynchrones

Les tùches longues (ingestion RAG, veille agentique) sont traitées de maniÚre asynchrone via Symfony Messenger. Les workers consomment la file de messages en arriÚre-plan.

Configuration Messenger

# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                # doctrine://default?queue_name=default (BDD comme transport)

        routing:
            'App\Message\IngestDocuments': async
            'Tlr\Codexia\Message\ProcessVeille': async

Service systemd worker — /etc/systemd/system/telaria-worker.service

[Unit]
Description=Telaria Symfony Messenger Worker
After=network.target mysql.service

[Service]
User=www-data
WorkingDirectory=/var/www/telaria
ExecStart=php /var/www/telaria/bin/console messenger:consume async --time-limit=3600
Restart=always
RestartSec=30

; Recharger aprÚs un déploiement (le code a changé)
KillSignal=SIGTERM

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now telaria-worker
sudo systemctl status telaria-worker

Redémarrer les workers aprÚs un déploiement

AprÚs un git pull, les workers tournent avec l'ancien code. Il faut les redémarrer :

sudo systemctl restart telaria-worker

Le Symfony Messenger supporte le --time-limit : le worker s'arrĂȘte proprement aprĂšs 1h et systemd le relance. Évite les fuites mĂ©moire sur les workers longue durĂ©e.


Étape suivante : 10-mcp.md

Assistant documentaire

Posez une question sur la documentation. Les rĂ©ponses citent leurs sources — un clic ouvre le document Ă  gauche.

Loading…
Loading the web debug toolbar…
Attempt #