Lot 0 â CĆur RAG : spĂ©cification
SpĂ©cification dĂ©taillĂ©e du socle commun (« cĆur ») de l'initiative Vitrine IA : ingestion â dĂ©coupage â embeddings â index vectoriel â rĂ©cupĂ©ration. RattachĂ© au cadre :
specs/ia-vitrine.md. Statut : spec alignĂ©e sur l'implĂ©mentation (telaria/rag-bundle v0.1.3).> Ătat d'implĂ©mentation : L0 cĆur RAG validĂ© en prod sur VPS le 2026-05-26 (181 documents / 2430 chunks indexĂ©s, recherche fonctionnelle).
1. Objectif et périmÚtre
Le cĆur est la fondation rĂ©utilisĂ©e par les trois surfaces (MCP, chatbot, veille). Il transforme une arborescence de documents Markdown en une base interrogeable par similaritĂ© sĂ©mantique.
- Headless par conception : pas d'interface utilisateur propre. Il s'expose via une CLI (commandes Symfony) et une API de service (pour les surfaces).
- RĂ©utilisable : packagĂ© en bundle Symfony, avec une source paramĂ©trable (racine de documents) â exploitable pour Codexia comme pour d'autres projets.
- IndĂ©pendant du LLM : le cĆur fait du retrieval. La gĂ©nĂ©ration (Claude) est consommĂ©e par les surfaces, pas par le cĆur.
PérimÚtre L0 : ingestion de la doc Codexia, découpage, embeddings (via microservice Python), index sqlite-vec, service de récupération, CLI, UI de diagnostic minimale.
Hors L0 : génération LLM, UI de chat (L2), outils MCP (L1), sources externes / veille (L3), ré-indexation temps réel.
2. Vue d'ensemble du pipeline
Indexation (hors-ligne, batch)
.md (source) âââ¶ Ingestion âââ¶ DĂ©coupage âââ¶ Embeddings âââ¶ Index
(walk+filtre) (chunks + (microservice (sqlite-vec)
métadonnées) Python)
RequĂȘte (Ă la demande)
question âââ¶ Embeddings(query) âââ¶ kNN (index) âââ¶ top-k chunks + scores + sources
â
âââ¶ contexte fourni aux surfaces
3. Composants
3.1 Ingestion
- Parcours récursif d'une racine source (paramétrable ; par défaut, le dépÎt doc).
- Filtres obligatoires : respecter
.aiignoreâ exclureSCRATCH.mdetinputs/legacy/de l'indexation. Exclure les fichiers binaires/ressources non textuelles. - Lecture UTF-8 ; extraction du front-matter et de la structure de titres (H1/H2/H3).
3.2 Découpage (chunking)
- Découpage guidé par la structure (sections de titres), borné par une taille cible (en tokens) avec chevauchement léger pour ne pas couper le sens.
- Chaque chunk porte ses métadonnées : chemin du fichier, titre du document, section (ancre), position, empreinte (
hash) pour la ré-indexation incrémentale.
3.3 Embeddings (microservice Python)
- Vectorisation déléguée à un microservice Python (FastAPI + sentence-transformers), modÚle
intfloat/multilingual-e5-base. - Convention e5 : préfixes
query:/passage:gĂ©rĂ©s cĂŽtĂ© service selon le type demandĂ©. - Appels par lots Ă l'indexation ; appel unitaire rapide Ă la requĂȘte.
3.4 Index vectoriel (SQLite + sqlite-vec)
- Stockage embarqué : SQLite + extension
sqlite-vec(table virtuelle de vecteurs + recherche kNN). upsertpar chunk ; ré-indexation incrémentale par comparaison dehash(ne ré-embedde que ce qui a changé).
DĂ©cision (figĂ©e) & rationale â pourquoi pas la base MySQL existante :
- L'index vectoriel est un artefact dérivé et reconstructible (régénérable depuis les
.md), pas de la donnée métier : on le découple de la base métier, comme on le ferait d'un index de recherche. MySQL reste la base métier. - MySQL 8.4 LTS n'a aucun support vectoriel natif : le type
VECTORet les fonctions de distance sont apparus en MySQL 9.0 (branche Innovation, pas LTS). L'utiliser imposerait un calcul de similaritĂ© en force brute cĂŽtĂ© PHP â viable Ă petite Ă©chelle, mais artisanal. sqlite-vecfournit le kNN prĂȘt Ă l'emploi, sans serveur supplĂ©mentaire, et sans contraindre la version de MySQL.- Alternatives Ă©cartĂ©es : pgvector (impose un serveur PostgreSQL) ; MySQL 9.x
VECTOR(release Innovation, à rebours du choix LTS « technologies éprouvées »).
Prérequis serveur & chargement :
sqlite-vecest une extension chargeable (non incluse par dĂ©faut) â Ă installer sur le serveur (binaire prĂ©-compilĂ© ou compilation). Le bundle la charge cĂŽtĂ© PHP viaPdo\Sqlite::loadExtension()(PHP 8.5), chemin configurable (RAG_SQLITE_VEC_PATH, sinon le nomvec0) ; chargement paresseux (exception claire si indisponible, sans bloquer le boot).â ïž Nuance importante (corrige une approximation antĂ©rieure) : la recherche kNN (table virtuelle
vec0) exige l'extension chargĂ©e dans PHP â c'est intrinsĂšque Ăsqlite-vec. Le repli « indexation par l'outillage » (CLI/Python) ne couvre donc que l'Ă©criture de l'index ; si PHP ne peut pas charger l'extension, il faut aussi dĂ©porter la recherche hors PHP (p. ex. vers le service Python) â sinon l'application ne peut pas interroger l'index. Ă tester en prioritĂ© sur le VPS (open_basedir, build PHP), et Ă rĂ©percuter dansguides/deployment.md.â ValidĂ© sur VPS le 2026-05-26 :
Pdo\Sqlite::loadExtension()fonctionne sur le build PHP 8.4 du VPS â le chemin nominal (recherche en PHP) est le cas principal. Le repli « dĂ©port hors PHP » devient un plan B thĂ©orique, Ă n'invoquer que si un autre hĂŽte bloque le chargement.
3.5 Service de récupération (retrieval)
- EntrĂ©e : une requĂȘte en langage naturel (+
k, filtres optionnels par chemin/section). - Sortie : top-k chunks avec score de similarité et source (chemin, section, ancre).
- Ăvolution prĂ©vue (hors L0) : retrieval hybride (dense + lexical) â d'oĂč l'option
bge-m3au cadre.
4. Contrats d'interface
4.1 Microservice Python (HTTP)
GET /health â { "status": "ok", "model": "...", "dim": 768 }
POST /embed â vectorise un lot
requĂȘte : { "type": "query"|"passage", "texts": ["..."] }
réponse : { "model": "...", "dim": 768, "vectors": [[...], ...] }
- Validation stricte des entrées :
typehors {query,passage}, outextsvide â HTTP 422 (la forme des rĂ©ponses 200 est inchangĂ©e). Pas de fallback silencieux (qui produirait un mauvais prĂ©fixe e5). Le client (cĆur RAG) traite un 422 comme une erreur d'entrĂ©e. - Service stateless, dĂ©ployable sĂ©parĂ©ment ; URL injectĂ©e en configuration cĂŽtĂ© Symfony.
4.2 CÎté Symfony (bundle Telaria\Rag)
Signatures réelles, figées en v0.1.x :
ChunkerInterface::chunk(Document $document): Chunk[]EmbeddingClientInterface::embed(string $type, string[] $texts): float[][]+health(): array{status,model,dim}VectorStoreInterface:initialize(): void;upsertDocument(Document $doc, Chunk[] $chunks, float[][] $vectors): void(remplacement transactionnel du document + ses chunks/vecteurs) ;documentHash(string $path): ?string(réindexation incrémentale au niveau document) ;search(float[] $vector, int $k): Hit[];stats(): array{documents,chunks,last_indexed_at}RetrievalService::retrieve(string $query, ?int $k = null): Hit[]IngestionService::ingest(?string $sourceRoot = null, bool $incremental = true): IngestReport- ModÚles :
Chunk{documentPath, documentTitle, section, anchor, position, content, tokenCount, contentHash};Hit{chunk, score}avec score =1 - distance_cosinus(â ~[0,1]).
5. ModÚle de données (schéma SQLite)
document:id,path,title,content_hash,indexed_at.chunk:id,document_id,section,anchor,position,content,token_count,content_hash.chunk_vector: table virtuellesqlite-vec(chunk_idâ vecteur, dimension du modĂšle).ingest_run:id,source_root,started_at,finished_at,docs,chunks,status(traçabilitĂ©).
6. Interface (réponse à la question « interface graphique »)
Le cĆur Ă©tant headless, son interface de pilotage est double :
6.1 CLI (interface primaire, L0)
app:rag:ingest [--source=PATH] [--full]: indexe une racine (--full= rĂ©indexation complĂšte ; par dĂ©faut incrĂ©mentale parcontent_hash).app:rag:reindex: rĂ©-indexation incrĂ©mentale (parhash).app:rag:search "question" [--k=5]: teste une requĂȘte, affiche chunks + scores + sources.app:rag:stats: nb documents/chunks, date de derniĂšre indexation, santĂ© du microservice.
6.2 UI de diagnostic â â
implémentée (/admin/rag)
ImplĂ©mentĂ©e dans telaria : route /admin/rag (ROLE_ADMIN), dĂ©grade proprement (banniĂšre) si sqlite-vec/microservice absents. Elle rend visible le travail « invisible » du retrieval â fort atout vitrine :
- Statistiques d'index (documents, chunks, derniÚre indexation, état du microservice).
- Formulaire de requĂȘte de test â affichage des passages rĂ©cupĂ©rĂ©s avec score de similaritĂ© et lien vers la source.
- Rendu serveur, sans JavaScript requis (honore l'amélioration progressive), derriÚre
ROLE_ADMIN. - Aligné sur le design system existant (
specs/design.md,specs/ui.md,specs/bootstrap.md) et conforme RGAA 4.1 AA.
6.3 OĂč vit la « vraie » interface utilisateur
- Chatbot (L2) : c'est l'interface end-user riche (Q/R + citations). MĂȘme design system, RGAA AA, amĂ©lioration progressive (le streaming ajoute du JS, mais doit dĂ©grader proprement).
- MCP (L1) : pas d'UI Ă construire â l'« interface » est le client agent (Claude Desktop, CursorâŠ) ; la dĂ©mo se fait cĂŽtĂ© client.
7. Configuration
Réglages dans config/packages/telaria_rag.yaml (telaria_rag.*) :
source_root,included_paths,excluded_paths(exclutSCRATCH.md,inputs/legacy/).embedding.{service_url, model, dimension, timeout, batch_size}â dĂ©fauts :intfloat/multilingual-e5-base,dimension: 768.chunk.{size, overlap}â dĂ©fauts512/64.retrieval.kâ dĂ©faut5.store.{database_path, extension_path}.
Variables d'environnement (hĂŽte) â surchargent les chemins/URL sensibles :
| Variable | Mappe vers | Défaut |
|---|---|---|
RAG_EMBEDDING_URL |
embedding.service_url |
http://127.0.0.1:8001 |
RAG_DB_PATH |
store.database_path (via env(resolve:)) |
%kernel.project_dir%/var/rag/index.sqlite |
RAG_SQLITE_VEC_PATH |
store.extension_path |
(sinon vec0) |
8. Gouvernance et garde-fous
- Respect de
.aiignore:SCRATCH.mdetinputs/legacy/ne sont jamais indexĂ©s par dĂ©faut (cohĂ©rent avec la confidentialitĂ© du bac Ă sable). - Sources traçables : chaque chunk conserve sa provenance â toute rĂ©ponse de surface pourra citer ses sources (exigence
AGENTS.md). - Pas de données personnelles dans la doc Codexia (risque RGPD faible) ; à réévaluer si des sources externes en introduisent.
9. Contraintes et performance
- VPS CPU-only : l'indexation (coĂ»teuse) tourne en batch / hors-ligne ; l'embedding d'une requĂȘte est lĂ©ger Ă la demande.
- ModĂšle
e5-basechoisi pour rester fluide sur CPU (cf. décision du cadre). - Empreinte mémoire du microservice à surveiller ; un seul modÚle chargé.
10. Tests unitaires (cas concrets)
Ingestion
- Une racine de fixtures est parcourue ; les fichiers non-Markdown sont ignorés.
SCRATCH.mdetinputs/legacy/ne sont jamais ingérés (par défaut).- Les
included_paths/excluded_pathsde la configuration sont respectés. - Le front-matter et la hiérarchie de titres (H1/H2/H3) sont extraits.
Découpage (chunking)
- Un document est découpé selon sa structure, en respectant
sizeetoverlap. - Chaque chunk porte ses métadonnées :
path,section,anchor,position,content_hash. - Un document plus court que
sizeproduit un seul chunk. - Une modification du contenu change le
content_hash(base de la réindexation incrémentale).
Client d'embeddings (microservice mocké)
- L'appel envoie
{type, texts}Ă/embed; les vecteurs de la rĂ©ponse sont retournĂ©s tels quels. - Une entrĂ©e invalide (
typehors {query,passage} outextsvide) â le service renvoie 422 ; le client propage l'erreur (pas d'embedding silencieux). - Une erreur/timeout du service est propagĂ©e proprement (l'index n'est pas corrompu).
Index vectoriel (VectorStoreInterface, impl. sqlite-vec)
upsertpuissearchrenvoie les chunks les plus proches, triés par score (cosinus).- La réindexation incrémentale ne ré-embedde pas un chunk dont le
content_hashest inchangé. - Un index vide renvoie un résultat vide (sans erreur).
Récupération (RetrievalService)
- Une requĂȘte est vectorisĂ©e en
type=query, puis renvoie le top-k avecscoreet source (path,section,anchor). - La borne
kest respectée.
Gouvernance (intégration)
ingestâsearchsur fixtures : un passage attendu figure dans le top-k.- VĂ©rification de bout en bout que
SCRATCH.mdetinputs/legacy/n'apparaissent jamais dans les résultats.
11. Production documentaire d'accompagnement (doctrine)
Ce lot introduit des concepts qui doivent ĂȘtre expliquĂ©s (cf. cadre §11) :
| Concept introduit | Livrable | Emplacement |
|---|---|---|
| Embeddings & similarité vectorielle | Fiche (synthÚse) | agents/ (module Fondements) |
| Microservice Python d'inférence (FastAPI + sentence-transformers) | Tuto pas-à -pas | tutos/ia/ |
Index vectoriel SQLite (sqlite-vec) |
Tuto/fiche | tutos/ia/ |
| RAG de bout en bout (assemblage du cĆur) | Tuto (s'appuie sur la fiche 4.3) | tutos/ia/ |
Tous produits : fiche 2.6 Embeddings, tuto microservice Python, tuto sqlite-vec, tuto RAG bout-en-bout.
12. Décisions (tranchées avec l'instance codexia)
- Découpage :
size=512/overlap=64tokens, configurables (à calibrer sur la doc FR ; estimateur de tokens heuristique, pas de tokenizer natif PHP). - Métrique & dimension : cosinus, dimension 768 (
multilingual-e5-base). - FrontiĂšre bundle : un seul bundle « cĆur » (
Telaria\Rag) ; le store est un service interchangeable derriÚreVectorStoreInterface(on scindera si un autre store émerge). - Chargement
sqlite-vec: cĂŽtĂ© PHP viaPdo\Sqlite::loadExtension(RAG_SQLITE_VEC_PATH ?? 'vec0'), paresseux (cf. nuance §3.4 : la recherche exige l'extension en PHP) â Ă valider sur le VPS. Packaging du microservice : du ressort detlr-embeddings.
Tranché : l'UI de diagnostic (§6.2) est incluse dans le périmÚtre L0. Implémentation : bundle
telaria/rag-bundle(Telaria\Rag, tagv0.1.3signĂ© â bugPDO::PARAM_INTpourrowid/ksurvec0corrigĂ© en v0.1.3), consommĂ© partelariaen^0.1; UI/admin/rag(ROLE_ADMIN). Validation bout-en-bout sur VPS le 2026-05-26 : 181 documents / 2430 chunks indexĂ©s, top-1 pertinent (cosinus ~0.86) sur requĂȘtes test.
Documents liés
- Cadre :
specs/ia-vitrine.md - Veille « IA dans Codexia » (embeddings, vector store) :
pilotage/veille/README.md - Fiche RAG (prompt engineering avancé) :
agents/4-interagir-avec-l-ia/4-3-le-prompt-engineering-niveau-avance.md - ModÚles de déploiement :
agents/2-fondements-techniques/2-5-les-modeles-de-deploiement.md
Implémentation
| Aspect | Localisation |
|---|---|
| Bundle principal | telaria/rag-bundle (Telaria\Rag) â tag v0.1.3 |
| Interfaces clés | ChunkerInterface, EmbeddingClientInterface, VectorStoreInterface, RetrievalService, IngestionService |
| Index vectoriel | SQLite + sqlite-vec dans /var/www/telaria/var/rag/index.sqlite |
| Extension SQLite | vec0.so dans /usr/local/lib/sqlite-vec/ â chargĂ© via Pdo\Sqlite::loadExtension() |
| Commandes CLI | app:rag:ingest, app:rag:reindex, app:rag:search, app:rag:stats |
| UI diagnostic | Route /admin/rag (ROLE_ADMIN) dans telaria-app |
| Config | config/packages/telaria_rag.yaml dans telaria-app |
| Variables d'env | RAG_EMBEDDING_URL, RAG_DB_PATH, RAG_SQLITE_VEC_PATH |
| Microservice embeddings | tlr-embeddings (Python) sur 127.0.0.1:8001 |
Historique des décisions
| Version | Date | Décision |
|---|---|---|
| 1.0 | 2026-06-14 | Version initiale â premiĂšre formalisation du versioning des specs. |
| â | 2026-05-26 | L0 validĂ© en prod sur VPS : 181 documents / 2430 chunks indexĂ©s, top-1 pertinent cosinus ~0.86. |
| â | 2026-05-29 | Taille chunks figĂ©e : size=512 / overlap=64 tokens, configurables. MĂ©trique cosinus, dimension 768. Un seul bundle Telaria\Rag avec store interchangeable derriĂšre VectorStoreInterface. |
| â | 2026-05-29 | sqlite-vec validĂ© sur VPS PHP 8.4 : Pdo\Sqlite::loadExtension() fonctionne. Repli « dĂ©port hors PHP » = plan B thĂ©orique. |
| â | 2026-05-29 | UI diagnostic /admin/rag incluse dans le pĂ©rimĂštre L0 (actĂ©). |