mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
feat(r5.1): socle apps/ai — ingestion anonymisée + recherche sémantique
ADR-004 : embeddings locaux sur CPU (fastembed ONNX, paraphrase-multilingual-MiniLM-L12-v2, 384 dims — les textes du client ne quittent jamais le serveur), pgvector dans le Postgres existant (RagChunk possédé par Prisma, migration r5_ia + état de corpus sur Document), génération opt-in (mode extractif par défaut : la recette passe sans clé API), service siop2-ai jamais exposé — joint par l'API NestJS seule (X-Service-Token). apps/ai (FastAPI + uv) : pipeline PDF MinIO → texte paginé (pypdf) → anonymisation D4 (e-mails, téléphones marocains, noms connus de la base, insensible casse/accents — fonction pure testée) → découpage avec chevauchement (testé) → embeddings → RagChunk localisé (« p. 42 », « bilan du 17/07 »). Bilans codés clôturés ingérés. Exclusion de corpus (D3) appliquée à l'ingestion ET à la lecture. 14 pytest + ruff, embeddeur déterministe en CI (aucun téléchargement), job CI ai (uv), deploy en dépend. Vérifié en réel avec le vrai modèle : corpus seedé réindexé en 7 s (PDF réel → 31 extraits paginés + 3 bilans), recherche sémantique concluante, e-mails → ⟨contact⟩, 0 identité dans les chunks (contrôle SQL). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
43
docs/03-architecture/adr/ADR-004-modeles-ia.md
Normal file
43
docs/03-architecture/adr/ADR-004-modeles-ia.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# ADR-004 — Modèles et topologie du service IA (R5)
|
||||
|
||||
- **Statut** : acceptée (R5.1, 17 juillet 2026)
|
||||
- **Contexte** : les maquettes R5 et les décisions D1-D5 sont validées (l'IA propose/l'humain
|
||||
valide, sourcé ou silencieux, corpus fermé, anonymisation 09-08, voix purgée). Reste à
|
||||
trancher COMMENT : quels modèles, où tournent-ils, qui parle à qui. Contraintes : serveur
|
||||
Dokploy du partenaire (CPU, pas de GPU garanti), corpus en français, données d'un client
|
||||
marocain (loi 09-08), CI sans secret, démo qui marche sans dépendance externe.
|
||||
|
||||
## Décision
|
||||
|
||||
1. **Embeddings : locaux, sur CPU** — `fastembed` (ONNX, sans PyTorch) avec
|
||||
`sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` (384 dimensions, multilingue, ~120 Mo, registre fastembed). Les textes du
|
||||
client ne quittent JAMAIS le serveur pour l'indexation ni pour la recherche. Le modèle est
|
||||
téléchargé au build de l'image (pas au démarrage). En **tests/CI : embeddeur déterministe
|
||||
par hachage** (pas de téléchargement, pas de flottement) derrière la même interface.
|
||||
2. **Stockage vectoriel : pgvector** dans le PostgreSQL existant (extension déjà installée
|
||||
depuis R0) — table `RagChunk` gérée par la **migration Prisma** (le schéma reste la
|
||||
propriété d'`apps/api`, source unique). Pas de base vectorielle de plus à opérer.
|
||||
3. **Génération (réponses rédigées de l'assistant) : opt-in par configuration.**
|
||||
- Sans clé (`AI_GENERATION=off`, défaut) : l'assistant fonctionne en **mode extractif** —
|
||||
il montre les meilleurs extraits sourcés et une synthèse templatée, sans LLM. La démo,
|
||||
la CI et un déploiement sans budget API restent pleinement fonctionnels et honnêtes.
|
||||
- Avec clé (`AI_GENERATION=api` + `AI_API_KEY`) : rédaction par un LLM externe, sur textes
|
||||
DÉJÀ anonymisés (D4), avec l'obligation de citer les extraits fournis — jamais au-delà.
|
||||
- La **suggestion de codes de bilan** n'utilise PAS de LLM : similarité sémantique
|
||||
(embeddings locaux) entre la description et les libellés des référentiels + les bilans
|
||||
historiques du parc. Déterministe, testable, explicable (« 9 bilans similaires »).
|
||||
4. **Topologie** : `apps/ai` (FastAPI/uv, conteneur `siop2-ai`) n'est **jamais exposé** —
|
||||
réseau interne Dokploy uniquement, comme MinIO. Les utilisateurs passent par l'API NestJS
|
||||
(auth JWT + matrice de permissions réutilisées) qui proxifie vers `siop2-ai`
|
||||
(`AI_SERVICE_URL` + secret partagé `AI_SERVICE_TOKEN`). `apps/ai` lit Postgres
|
||||
(métadonnées, bilans, écriture des chunks) et MinIO (octets des PDF) en direct sur le
|
||||
réseau privé — la règle d'or ESLint d'ADR-001 concerne le code TypeScript de l'API,
|
||||
la topologie « MinIO jamais exposé » du runbook reste respectée.
|
||||
|
||||
## Conséquences
|
||||
|
||||
- L'anonymisation (D4) s'applique **à l'ingestion** — les index ne contiennent jamais
|
||||
d'identités ; le mode génératif n'envoie donc que des textes déjà nettoyés.
|
||||
- Le mode extractif est le contrat de base : toute recette R5 doit passer SANS clé API.
|
||||
- Si le partenaire veut un jour une génération 100 % locale (llama.cpp…), seul le point 3
|
||||
change — interface `Generateur` prévue pour ça.
|
||||
@@ -4,6 +4,23 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-17 — Pr. Daaif (+ Claude) — R5.1 : socle `apps/ai` — ingestion anonymisée + recherche sémantique
|
||||
|
||||
**Actions**
|
||||
|
||||
- **ADR-004 actée** : embeddings **locaux sur CPU** (fastembed ONNX, `paraphrase-multilingual-MiniLM-L12-v2`, 384 dims — les textes du client ne quittent jamais le serveur), pgvector dans le Postgres existant (table `RagChunk` **possédée par Prisma**, migration `r5_ia` + état de corpus sur Document), génération **opt-in** (`AI_GENERATION=off` par défaut : mode extractif honnête, la recette doit passer sans clé API), suggestion de bilan sans LLM (similarité sémantique, explicable). Topologie : `siop2-ai` jamais exposé, joint par l'API NestJS seule (`X-Service-Token`).
|
||||
- **`apps/ai` posé** (FastAPI + uv, python 3.11) : config validée au démarrage, `/healthz`, `/internal/reindex` (idempotent) et `/internal/search` protégés par le jeton de service. **Pipeline** : PDF MinIO → texte par page (pypdf) → **anonymisation D4** (e-mails, téléphones marocains, noms connus de la base — insensible casse/accents, fonction pure) → découpage (~900 car., chevauchement, testé) → embeddings → `RagChunk` avec localisateur (« p. 42 », « bilan du 17/07 »). Bilans codés clôturés ingérés aussi (« sur votre parc… »). L'exclusion de corpus (D3) s'applique à l'ingestion ET à la lecture.
|
||||
- **14 pytest verts + ruff** (embeddeur **déterministe** en test/CI — aucun téléchargement, même interface 384 dims) ; **job CI `ai`** (uv), deploy en dépend.
|
||||
- **Vérifié en réel avec le vrai modèle ONNX** : réindexation du corpus seedé en 7 s (1 PDF réel → 31 extraits paginés, 3 bilans), recherche sémantique concluante (PDF trouvé par le sens, bilans du parc par « frottement des guides »), e-mails du PDF remplacés par ⟨contact⟩, **0 identité dans les chunks** (contrôle SQL sur les 9 noms seedés).
|
||||
|
||||
**Décisions**
|
||||
|
||||
- Convention de test R5 : pytest unitaires purs en CI (sans DB ni réseau) ; l'intégration réelle (DB + MinIO + modèle) se vérifie en local et en recette.
|
||||
|
||||
**Prochaine étape** : R5.2 — l'assistant au contrat (proxy NestJS authentifié → `siop2-ai`, mode extractif sourcé « sourcé ou silencieux ») + suggestion de codes de bilan. Restes : recette R4 sur appareil, redéploiement Dokploy de `release/r3`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-17 — Pr. Daaif (+ Claude) — R5 ouverte : maquettes IA (design d'abord)
|
||||
|
||||
**Actions**
|
||||
@@ -13,9 +30,9 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
|
||||
|
||||
**Décisions**
|
||||
|
||||
- Aucune actée — les 5 décisions **attendent la validation du référent avec les écrans**. Aucune ligne de code `apps/ai` avant (principe n°1). L'architecture (pgvector, choix des modèles) se tranchera en **ADR-004** au lancement du socle R5.1.
|
||||
- **→ Levé le 17/07/2026 : maquettes R5 et les 5 décisions (D1-D5) VALIDÉES par le référent.** Lancement R5.1 (socle `apps/ai` + ADR-004).
|
||||
|
||||
**Prochaine étape** : validation référent (maquettes + décisions) → R5.1 socle `apps/ai` (FastAPI/uv, ingestion + ADR-004). Restes : recette R4 sur appareil, redéploiement Dokploy de `release/r3`.
|
||||
**Prochaine étape** : R5.1 — ADR-004 (embeddings/modèles), migration `r5_ia` (chunks pgvector, état de corpus), pipeline d'ingestion anonymisé et testé. Restes : recette R4 sur appareil, redéploiement Dokploy de `release/r3`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user