mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
feat(r5.3): écrans IA — assistant web, corpus administrable, suggestions OT/mobile
- Contrat (76 opérations) : Document expose inCorpus/indexedAt/chunkCount,
PATCH /documents/{id}/corpus (ASSETS.edit), POST /assistant/reindex
(bilan chiffré) ; ci-contract vérifie désormais aussi le client mobile.
- Web : page /assistant (chat sourcé — extraits exacts cités, Ouvrir vers
PDF authentifié ou fiche OT, avertissement permanent ; refus honnête
chiffré avec action utile) ; Bibliothèque = corpus (bandeau 09-08,
statut d'indexation par document, interrupteur d'exclusion PDF,
Réindexer tout) ; fiche OT : « Décrire pour suggérer » (Appliquer =
geste humain, liseré « suggéré » retiré au choix manuel).
- Mobile : chips de suggestion dans la clôture (un appui = un champ
pré-rempli, « réseau requis » hors-ligne — la file R4 n'en dépend pas).
- apps/ai : seuils AI_SEUIL_* configurables par env (CI + calibrage).
- CI e2e : service siop2-ai (embeddeur déterministe, seuils calibrés sur
mesures : match 0,66 vs bruit 0,11) + parcours R5 Playwright (PDF généré
xref valide → réindexation → réponse sourcée → refus → suggestion).
- Vérifié : 16/16 Playwright, 78 tests API, 23 pytest, 17 jest-expo ;
chaîne réelle au vrai modèle ONNX (web 7/7, mobile Expo web 6/6).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,27 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-17 — Pr. Daaif (+ Claude) — R5.3 : les écrans de l'IA (web + mobile) et le corpus administrable
|
||||
|
||||
**Actions**
|
||||
|
||||
- **Contrat** (76 opérations) : `Document` expose son état de corpus (`inCorpus`, `indexedAt`, `chunkCount`), `PATCH /documents/{id}/corpus` (bascule réversible, ASSETS.edit), `POST /assistant/reindex` (bilan chiffré traduit du dialecte interne). Clients web **et mobile** régénérés — le job ci-contract vérifie désormais les deux.
|
||||
- **Web — page Assistant** (sidebar Pilotage, WORK_ORDERS.view) : chat fidèle aux écrans 1-2 de maquette-r5 — réponse avec **extraits exacts cités** (« Ouvrir » → PDF en onglet authentifié, OT → fiche), avertissement « l'IA propose, vous validez » permanent ; **refus honnête chiffré** (« j'ai cherché dans N documents et M bilans ») avec l'action utile (téléverser la notice).
|
||||
- **Web — Bibliothèque = corpus** (écran 5) : bandeau 09-08 (l'anonymisation est DITE), statut par document (indexé · date · extraits / à indexer / exclu / image non indexable), interrupteur d'exclusion (PDF seulement), « Réindexer tout » avec bilan. **Fiche OT** (écran 3) : « Décrire pour suggérer » dans la carte Bilan — suggestions justifiées (« N bilans similaires », confiance), « Appliquer » = le geste humain qui écrit (cohérent avec la carte à enregistrement direct), **liseré « suggéré »** retiré dès qu'un choix manuel reprend la main.
|
||||
- **Mobile — clôture** (écran 4) : décrire au pouce → **chips suggérées** (un appui = un champ pré-rempli, ✓), note D1 visible ; hors-ligne le bouton dit « réseau requis » (l'IA est un service serveur — la clôture en file R4 n'en dépend pas).
|
||||
- **`apps/ai`** : seuils par env (`AI_SEUIL_*`) — nécessaires à la CI et au calibrage de recette.
|
||||
- **CI** : le job e2e démarre `siop2-ai` (embeddeur déterministe) et le parcours R5 se recette en vrai : upload d'un **PDF généré avec xref valide** → réindexation → statut → réponse sourcée (extrait « 25 Nm », p. 1) → refus sur charabia → exclusion → suggestion appliquée sur un OT créé. **16/16 Playwright** (recettes R0→R5).
|
||||
- **Vérifications réelles** : seuils CI **mesurés** (vrai match 0,66 vs bruit de collisions 0,11 → pertinence 0,20 ; codes pertinents 0,14-0,16 vs parasite 0,08 → suggestion 0,10) ; chaîne complète au **vrai modèle ONNX** (réindexation, ask sourcé p. 8/p. 10, suggestions étagées avec vrais comptes de bilans) ; parcours **mobile Expo web 6/6** et **web 7/7** au vrai modèle, 0 erreur console. 78 tests API (2 nouveaux : bascule corpus gardée, reindex traduit + 403).
|
||||
|
||||
**Décisions**
|
||||
|
||||
- Sur le web, « Appliquer » écrit immédiatement (comme tout le reste de la carte Bilan R2) : le clic EST la validation humaine — l'esprit de la maquette (« rien sans vous ») est porté par le geste, pas par un bouton « Enregistrer » séparé. À montrer en revue pixel.
|
||||
- Les seuils par défaut du vrai modèle (0,30/0,35/0,55) restent **à calibrer en recette sur le corpus client réel** : mesuré ce jour sur un corpus non-métier, le multilingual-MiniLM donne des scores plats (hors-sujet 0,35-0,43 vs pertinent 0,24-0,28) — c'est exactement pourquoi ils sont configurables.
|
||||
|
||||
**Prochaine étape** : recette R5 complète (doit passer SANS clé API), durcissement production (Dockerfile `siop2-ai` + compose Dokploy), puis tag `release/r5`. Restes : recette R4 sur appareil, redéploiement Dokploy de `release/r3`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-17 — Pr. Daaif (+ Claude) — R5.2 : l'assistant au contrat + suggestion de codes de bilan
|
||||
|
||||
**Actions**
|
||||
|
||||
@@ -1096,6 +1096,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/documents/{id}/corpus": {
|
||||
"patch": {
|
||||
"operationId": "updateDocumentCorpus",
|
||||
"summary": "Inclure/exclure du corpus IA (D3 — réversible, effectif à la prochaine réindexation)",
|
||||
"tags": [
|
||||
"documents"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/DocumentCorpusUpdate"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Document mis à jour",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Document"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Inconnu"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/documents/{id}": {
|
||||
"delete": {
|
||||
"operationId": "deleteDocument",
|
||||
@@ -1243,6 +1293,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/assistant/reindex": {
|
||||
"post": {
|
||||
"operationId": "reindexAssistant",
|
||||
"summary": "Réindexer le corpus (bibliothèque PDF + bilans clôturés, anonymisés à l’ingestion — D4)",
|
||||
"tags": [
|
||||
"assistant"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Bilan d’indexation",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ReindexResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"description": "Service IA indisponible"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/search": {
|
||||
"get": {
|
||||
"operationId": "globalSearch",
|
||||
@@ -4807,6 +4886,26 @@
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
|
||||
},
|
||||
"inCorpus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"indexedAt": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"chunkCount": {
|
||||
"type": "integer",
|
||||
"minimum": -9007199254740991,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -4818,7 +4917,10 @@
|
||||
"assetReference",
|
||||
"workOrderReference",
|
||||
"uploadedByName",
|
||||
"createdAt"
|
||||
"createdAt",
|
||||
"inCorpus",
|
||||
"indexedAt",
|
||||
"chunkCount"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -4892,6 +4994,26 @@
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
|
||||
},
|
||||
"inCorpus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"indexedAt": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"chunkCount": {
|
||||
"type": "integer",
|
||||
"minimum": -9007199254740991,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -4903,7 +5025,23 @@
|
||||
"assetReference",
|
||||
"workOrderReference",
|
||||
"uploadedByName",
|
||||
"createdAt"
|
||||
"createdAt",
|
||||
"inCorpus",
|
||||
"indexedAt",
|
||||
"chunkCount"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DocumentCorpusUpdate": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inCorpus": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"inCorpus"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
@@ -5256,6 +5394,39 @@
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ReindexResult": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"documentsIndexed": {
|
||||
"type": "integer",
|
||||
"minimum": -9007199254740991,
|
||||
"maximum": 9007199254740991
|
||||
},
|
||||
"documentsSkipped": {
|
||||
"type": "integer",
|
||||
"minimum": -9007199254740991,
|
||||
"maximum": 9007199254740991
|
||||
},
|
||||
"reportsIndexed": {
|
||||
"type": "integer",
|
||||
"minimum": -9007199254740991,
|
||||
"maximum": 9007199254740991
|
||||
},
|
||||
"chunks": {
|
||||
"type": "integer",
|
||||
"minimum": -9007199254740991,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"documentsIndexed",
|
||||
"documentsSkipped",
|
||||
"reportsIndexed",
|
||||
"chunks"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"SearchResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
|
||||
Reference in New Issue
Block a user