02-ce-que-je-construis/specs/ia-coeur.md

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 → exclure SCRATCH.md et inputs/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).
  • upsert par chunk ; rĂ©-indexation incrĂ©mentale par comparaison de hash (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 VECTOR et 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-vec fournit 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-vec est une extension chargeable (non incluse par dĂ©faut) → Ă  installer sur le serveur (binaire prĂ©-compilĂ© ou compilation). Le bundle la charge cĂŽtĂ© PHP via Pdo\Sqlite::loadExtension() (PHP 8.5), chemin configurable (RAG_SQLITE_VEC_PATH, sinon le nom vec0) ; 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 dans guides/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-m3 au 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 : type hors {query,passage}, ou texts vide → 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 virtuelle sqlite-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 par content_hash).
  • app:rag:reindex : rĂ©-indexation incrĂ©mentale (par hash).
  • 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 (exclut SCRATCH.md, inputs/legacy/).
  • embedding.{service_url, model, dimension, timeout, batch_size} — dĂ©fauts : intfloat/multilingual-e5-base, dimension: 768.
  • chunk.{size, overlap} — dĂ©fauts 512 / 64.
  • retrieval.k — dĂ©faut 5.
  • 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.md et inputs/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-base choisi 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.md et inputs/legacy/ ne sont jamais ingĂ©rĂ©s (par dĂ©faut).
  • Les included_paths / excluded_paths de 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 size et overlap.
  • Chaque chunk porte ses mĂ©tadonnĂ©es : path, section, anchor, position, content_hash.
  • Un document plus court que size produit 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 (type hors {query,passage} ou texts vide) → 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)

  • upsert puis search renvoie les chunks les plus proches, triĂ©s par score (cosinus).
  • La rĂ©indexation incrĂ©mentale ne rĂ©-embedde pas un chunk dont le content_hash est 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 avec score et source (path, section, anchor).
  • La borne k est respectĂ©e.

Gouvernance (intégration)

  • ingest → search sur fixtures : un passage attendu figure dans le top-k.
  • VĂ©rification de bout en bout que SCRATCH.md et inputs/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)

  1. Découpage : size=512 / overlap=64 tokens, configurables (à calibrer sur la doc FR ; estimateur de tokens heuristique, pas de tokenizer natif PHP).
  2. Métrique & dimension : cosinus, dimension 768 (multilingual-e5-base).
  3. FrontiĂšre bundle : un seul bundle « cƓur » (Telaria\Rag) ; le store est un service interchangeable derriĂšre VectorStoreInterface (on scindera si un autre store Ă©merge).
  4. Chargement sqlite-vec : cĂŽtĂ© PHP via Pdo\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 de tlr-embeddings.

TranchĂ© : l'UI de diagnostic (§6.2) est incluse dans le pĂ©rimĂštre L0. ImplĂ©mentation : bundle telaria/rag-bundle (Telaria\Rag, tag v0.1.3 signĂ© — bug PDO::PARAM_INT pour rowid/k sur vec0 corrigĂ© en v0.1.3), consommĂ© par telaria en ^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Ă©).

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 #