Tableau de décision -- Merge incrémental¶
Référence pour le
MergeEngine(domaine pur,backend/internal/domain/chapter/merge.go). Chaque règle est exécutée dans l'ordre de priorité. La première règle qui matche est appliquée.
Métriques utilisées¶
| Métrique | Définition | Calcul |
|---|---|---|
| CanonicalKey match | Les deux items ont le même CanonicalKey(term, item_type, pack_id) |
identity.go : lowercase + NFD accent removal + trim |
| Jaccard(keywords) | Coefficient de Jaccard entre les ensembles de keywords | |A ∩ B| / |A ∪ B| |
| Confidence delta | Différence de confiance entre le nouvel item et l'existant | new.confidence - existing.confidence |
| Text similarity | Similarité textuelle entre les ocr_text des blocs sources |
Jaccard sur les mots normalisés (après stopwords removal) |
Tableau de décision¶
| # | Condition | Action | Automatique ? | Domain Event | Impact Mastery | Référence AC |
|---|---|---|---|---|---|---|
| R1 | CanonicalKey match ET Jaccard(keywords) > 0.85 | ARCHIVE_DUP : archiver l'item de plus faible confiance | Oui | ItemArchived |
Transfert Mastery vers survivant (le plus avancé) | Z3-AC11 |
| R2 | CanonicalKey match ET Jaccard(keywords) 0.50-0.85 ET new.confidence > existing.confidence + 0.15 |
REPLACE : remplacer l'ancien par le nouveau (meilleure extraction) | Oui | ItemReplaced |
Transfert Mastery vers nouveau | Z2-AC14 |
| R3 | CanonicalKey match ET Jaccard(keywords) 0.50-0.85 ET confidence delta < 0.15 | ENRICH : fusionner les keywords du nouveau dans l'existant, ajouter source_refs |
Oui | ItemsEnriched |
Inchangé | Z2-AC14 |
| R4 | CanonicalKey match ET Jaccard(keywords) < 0.50 | FLAG_CONFLICT : possible contradiction sémantique (même terme, contenu très différent) | Non (HITL) | ConflictDetected |
Gelé (pas de transition) | Z3-AC11 |
| R5 | CanonicalKey miss ET Jaccard(keywords) > 0.70 ET termes proches (edit distance < 3) | FLAG_CONFLICT : probable doublon avec formulation différente | Non (HITL si confiance < 0.7) | ConflictDetected |
Gelé | Z3-AC11 |
| R6 | CanonicalKey miss ET Jaccard(keywords) 0.50-0.70 | CREATE_ALT : créer comme item alternatif en attente de review | Non (HITL) | Aucun | Aucun (pas de Mastery créé tant que non validé) | Z3-AC11 |
| R7 | CanonicalKey miss ET Jaccard(keywords) < 0.50 | APPEND : nouvel item, nouvelle connaissance | Oui | ItemsGenerated |
Nouveau Mastery UNKNOWN | Z2-AC14 |
Diagramme de décision¶
Nouvel item extrait
│
▼
CanonicalKey match avec un item existant ?
│
┌────┴────┐
│ OUI │ NON
▼ ▼
Jaccard Jaccard(keywords) avec tous les existants
keywords? │
│ ┌───┼───┐───────┐
│ │ │ │ │
│ >0.70 │ 0.50-0.70 │ <0.50
│ │ │ │ │
│ ▼ │ ▼ ▼
│ R5: │ R6: R7:
│ FLAG │ CREATE APPEND
│ CONFLICT │ ALT │
│ │ │ │ ItemsGenerated
│ │ │ │ Mastery UNKNOWN
│ │ │ │
├───────┤ │ │
│ │ │ │
│ >0.85 │ 0.50-0.85 <0.50
│ │ │ │
▼ ▼ ▼ ▼
R1: ∆conf R4:
ARCHIVE > 0.15? FLAG
DUP │ CONFLICT
│ ┌──┴──┐
│ OUI NON
│ │ │
▼ ▼ ▼
R1 R2: R3:
REPLACE ENRICH
Détails par action¶
R1 : ARCHIVE_DUP (doublon exact)¶
Quand : même terme normalisé, même type, et keywords quasi-identiques.
Algorithme :
1. Comparer CanonicalKey(new) == CanonicalKey(existing).
2. Calculer Jaccard(new.keywords, existing.keywords).
3. Si Jaccard > 0.85, c'est un doublon.
4. Archiver celui avec la confiance la plus basse (item.Archive(now)).
5. Si confiances égales (delta < 0.05), archiver le plus récent (conserver l'original).
Exemple :
- Existant : term="Chloroplaste", keywords=["organite", "cellule végétale", "chlorophylle"], confidence=0.90
- Nouveau : term="chloroplaste", keywords=["organite", "cellule", "chlorophylle", "photosynthèse"], confidence=0.85
- CanonicalKey match : oui (chloroplaste|KNOWLEDGE|SVT-PHOTO)
- Jaccard = |{organite, chlorophylle}| / |{organite, cellule végétale, cellule, chlorophylle, photosynthèse}| = 2/5 = 0.40
- Jaccard < 0.85 donc pas R1, on passe à R3 (ENRICH).
Contre-exemple (vrai doublon) : - Existant : keywords=["organite", "cellule végétale", "chlorophylle", "photosynthèse"] - Nouveau : keywords=["organite", "cellule végétale", "chlorophylle"] - Jaccard = 3/4 = 0.75 -- toujours pas > 0.85. - Pour atteindre R1, il faut vraiment que les keywords soient quasi-identiques : Jaccard > 0.85 signifie que le nouveau n'apporte essentiellement rien de neuf.
R2 : REPLACE (meilleure extraction)¶
Quand : même terme, keywords partiellement similaires, mais la nouvelle extraction est significativement meilleure.
Algorithme :
1. CanonicalKey match.
2. Jaccard(keywords) entre 0.50 et 0.85.
3. new.confidence - existing.confidence > 0.15.
4. L'ancien item est archivé, le nouveau prend sa place.
5. Le Mastery est transféré.
Cas typique : l'élève re-photographie une page floue (confiance 0.55) avec une meilleure photo (confiance 0.88). Delta = 0.33 > 0.15 donc REPLACE.
Seuil de 0.15 : calibré pour éviter les remplacements sur du bruit. Un delta de 0.05-0.10 peut être dû à la variance OCR sur la même page.
R3 : ENRICH (enrichissement)¶
Quand : même terme, keywords partiellement similaires, confiances proches.
Algorithme :
1. CanonicalKey match.
2. Jaccard(keywords) entre 0.50 et 0.85.
3. Delta confiance <= 0.15.
4. Les keywords du nouveau qui ne sont pas dans l'existant sont ajoutés.
5. Les source_refs du nouveau sont ajoutés à l'existant.
6. La confiance est mise à jour : max(existing.confidence, new.confidence).
Exemple :
- Existant : term="Stomate", keywords=["orifice", "feuille", "échanges gazeux"]
- Nouveau (depuis correction d'exercice) : term="Stomate", keywords=["orifice", "feuille", "CO₂", "O₂", "face inférieure"]
- Jaccard = |{orifice, feuille}| / |{orifice, feuille, échanges gazeux, CO₂, O₂, face inférieure}| = 2/6 = 0.33
- Jaccard < 0.50, donc c'est en fait R4 (FLAG_CONFLICT), pas R3.
- Mais les CanonicalKeys matchent et le contenu n'est pas contradictoire (il enrichit).
- Correction : quand le CanonicalKey matche, si les keywords sont complémentaires (pas contradictoires) et que le delta de confiance est faible, on fait ENRICH même avec un Jaccard < 0.50. La règle R4 ne s'applique que quand il y a une contradiction sémantique détectée (via le CoherenceChecker LLM), pas juste un faible Jaccard.
R3 ajusté : ENRICH (version corrigée)¶
Condition réelle : 1. CanonicalKey match. 2. Pas de contradiction détectée par le CoherenceChecker. 3. Delta confiance <= 0.15. 4. -> ENRICH (fusionner keywords + source_refs).
Condition R4 réelle : 1. CanonicalKey match. 2. Contradiction détectée par le CoherenceChecker (contenu sémantiquement incompatible). 3. -> FLAG_CONFLICT.
Le Jaccard sert de pré-filtre pour décider s'il faut appeler le CoherenceChecker LLM (coûteux). Si Jaccard > 0.85, pas besoin de checker (doublon évident). Si Jaccard < 0.50 avec CanonicalKey match, le CoherenceChecker est appelé pour distinguer enrichissement de contradiction.
R4 : FLAG_CONFLICT (contradiction)¶
Quand : même terme, mais le CoherenceChecker LLM détecte une contradiction sémantique.
Exemple :
- Existant : term="Densité", definition: "masse divisée par le volume" (correct)
- Nouveau : term="Densité", definition: "volume divisé par la masse" (incorrect)
- CanonicalKey match.
- CoherenceChecker retourne : contradiction, severity=0.9.
- Les deux items sont flaggés coherence_flag='contradiction', validation_required=true.
- ValidationTask créée (Z3-AC11).
R5 : FLAG_CONFLICT (doublon avec formulation différente)¶
Quand : termes différents mais keywords très proches.
Exemple :
- Existant : term="Masse volumique", keywords=["masse", "volume", "rho", "kg/m³"]
- Nouveau : term="Densité", keywords=["masse", "volume", "rho", "eau"]
- CanonicalKey miss (termes différents).
- Jaccard = |{masse, volume, rho}| / |{masse, volume, rho, kg/m³, eau}| = 3/5 = 0.60
- Wait, 0.60 < 0.70 donc c'est R6, pas R5.
- Pour R5, il faudrait Jaccard > 0.70 : par exemple deux formulations d'un même concept avec les mêmes keywords.
R6 : CREATE_ALT (alternative en attente)¶
Quand : pas de match exact, mais similarité modérée. Possible enrichissement ou possible concept distinct.
Algorithme :
1. Créer le nouvel item avec merge_status='alternative'.
2. Ne pas créer de Mastery (item non validé).
3. Si confiance >= 0.70, pas de HITL (l'alternative est simplement conservée).
4. Si confiance < 0.70, créer une ValidationTask pour que l'élève/parent décide.
R7 : APPEND (nouveau concept)¶
Quand : aucune similarité significative avec les items existants.
Algorithme :
1. Créer l'item avec merge_status='original'.
2. Rattacher à une Notion existante (par nom) ou créer une nouvelle Notion.
3. Émettre ItemsGenerated.
4. Créer un Mastery UNKNOWN pour l'utilisateur.
Ordre d'évaluation¶
Les règles sont évaluées dans l'ordre R1-R7 pour chaque item entrant. La première règle qui matche est appliquée.
Pour chaque item entrant, le MergeEngine : 1. Calcule le CanonicalKey du nouvel item. 2. Cherche un item existant avec le même CanonicalKey. 3. Si trouvé : calcule Jaccard, applique R1/R2/R3/R4. 4. Si non trouvé : calcule Jaccard avec tous les existants, applique R5/R6/R7.
Complexité : O(n*m) où n = items entrants, m = items existants. Pour un chapitre typique (5-20 items), c'est négligeable.
Seuils récapitulatifs¶
| Seuil | Valeur | Justification |
|---|---|---|
| Jaccard doublon | > 0.85 | Très peu de keywords différents = même contenu |
| Jaccard enrichissement | 0.50-0.85 | Keywords complémentaires = enrichissement probable |
| Jaccard matching cross-term | > 0.70 | Même concept, formulation différente |
| Jaccard modéré cross-term | 0.50-0.70 | Zone grise, nécessite review |
| Jaccard pas de match | < 0.50 | Concepts distincts |
| Confidence delta replace | > 0.15 | Écart significatif, pas du bruit OCR |
| Confidence HITL obligatoire | < 0.50 | Extraction trop incertaine |
| Confidence pas de HITL | >= 0.85 | Extraction fiable (Z3-AC09) |
| OCR confidence floue | < 0.30 | Page floue (Z2-AC03) |
| Text similarity re-photo | > 0.80 | Même page re-photographiée |
| Edit distance termes proches | < 3 | Typo ou variante orthographique |
Cas limites¶
Cas 1 : Item PROCEDURE avec même terme mais étapes différentes¶
Un item PROCEDURE "Calcul de la densité" peut avoir des étapes différentes selon la source (méthode du prof vs. méthode du manuel). Dans ce cas :
- Le CanonicalKey matche.
- Les keywords matchent (Jaccard > 0.50).
- Mais les steps divergent.
Décision : le CoherenceChecker compare les steps. Si les steps sont compatibles (un est un sous-ensemble de l'autre), c'est un ENRICH. Si les steps sont contradictoires, c'est un FLAG_CONFLICT.
Cas 2 : Item DOCUMENT (schéma) re-extrait¶
Un schéma re-photographié produit un nouvel item DOCUMENT avec un visual_block_id différent mais le même term. Dans ce cas :
- CanonicalKey matche.
- On compare les ocr_confidence des blocs sources.
- Si le nouveau crop est de meilleure qualité (confidence > old + 0.15), REPLACE.
- Sinon, on garde l'existant (le premier crop suffit).
Cas 3 : Fiche de correction qui enrichit des items existants¶
Une correction d'exercice (Session 3 dans l'exemple photosynthèse) reprend des définitions déjà extraites. Dans ce cas :
- Les CanonicalKeys matchent pour les items repris.
- La correction confirme les définitions existantes (pas de contradiction).
- Action = ENRICH : on ajoute la page de correction comme source_ref supplémentaire.
- La confiance peut augmenter (cross-validation par une source supplémentaire).