# Architecture Réelle du Système d'Anonymisation

## Vue d'Ensemble

Le système d'anonymisation combine **5 couches de détection** pour garantir une couverture maximale des PII dans les documents médicaux PDF.

## 1. Extraction de Texte (5 passes)

### Méthodes d'extraction (ordre de priorité)

1. **pdfplumber** : Extraction textuelle native (PDF textuels)
2. **pdfminer** : Extraction alternative avec LAParams
3. **PyMuPDF** : Fallback si artefacts `(cid:xx)` détectés
4. **docTR OCR** : OCR deep learning pour PDF scannés (parallèle avec tesseract)
5. **tesseract OCR** : OCR complémentaire

### OCR Word Map

Pour les PDF scannés, génération d'une carte de mots avec coordonnées normalisées (0→1) :
```python
OcrWordMap = Dict[int, List[Tuple[str, float, float, float, float]]]
# {page_idx: [(word_text, x0_norm, y0_norm, x1_norm, y1_norm), ...]}
```

Utilisée pour :
- Matching flou des identifiants numériques manuscrits (VLM)
- Redaction raster précise

---

## 2. Détection PII (5 couches)

### 2.1 Regex (Couche 1)

**Fichier** : `anonymizer_core_refactored_onnx.py`

**PII détectés** :
- EMAIL, TEL (formats fragmentés), IBAN, NIR (avec validation clé modulo 97)
- IPP, FINESS, RPPS, OGC
- Dates (multiples formats), dates de naissance
- Adresses postales, codes postaux, BP
- Âges, numéros de dossier, épisodes
- Établissements de santé, services hospitaliers

**Patterns contextuels** :
- `Dr. NOM`, `Patient: NOM`, `Rédigé par NOM`
- Listes virgulées : `Dr. X, Y, DUPONT`
- Champs structurés Trackare

**Configuration** : `config/dictionnaires.yml`
- Listes blanches (sections_titres, noms_maj_excepts)
- Force-mask (termes/regex à masquer systématiquement)
- Regex overrides personnalisées

### 2.2 VLM - Vision Language Model (Couche 2)

**Fichier** : `vlm_manager.py`

**Modèle** : Ollama avec qwen2.5vl:7b (ou qwen3-vl:8b)

**Fonctionnement** :
1. Conversion de chaque page PDF en image (150 DPI)
2. Redimensionnement si > 2048px (côté le plus long)
3. Envoi à Ollama avec prompt structuré
4. Parsing de la réponse JSON (avec réparation si tronquée)

**20+ catégories détectées** :
- Identité : NOM, PRENOM, DATE_NAISSANCE, NIR, AGE
- Contact : ADRESSE, TELEPHONE, EMAIL, CODE_POSTAL, VILLE
- Identifiants médicaux : IPP, NDA, RPPS, NUMERO_PATIENT, NUMERO_LOT, NUMERO_ORDONNANCE, NUMERO_SEJOUR
- Contexte : ETABLISSEMENT, SERVICE, DATE

**Gestion des pages manuscrites** :
- Si < 100 mots OCR détectés → masquage total de la page
- Évite les hallucinations du VLM sur pages complexes

**Matching flou** :
- Pour identifiants numériques manuscrits
- Compare séquences de chiffres (ratio ≥ 0.7)
- Utilise l'OCR word map pour localisation

**Configuration** :
```python
@dataclass
class VlmConfig:
    base_url: str = "http://localhost:11434"
    model: str = "qwen2.5vl:7b"
    timeout: int = 180
    max_image_size: int = 2048
    temperature: float = 0.1
    num_predict: int = 8192
    min_confidence: float = 0.5
```

**Prompt système** :
```
Tu identifies les données personnelles et identifiants traçables dans les documents 
médicaux français. Réponds uniquement en JSON.
```

**Dégradation gracieuse** : Si Ollama indisponible, le pipeline continue sans VLM.

### 2.3 NER - Named Entity Recognition (Couche 3)

#### Option A : EDS-Pseudo (recommandé)

**Fichier** : `eds_pseudo_manager.py`

**Modèle** : AP-HP/eds-pseudo-public via edsnlp

**Performance** : F1=0.97 sur données cliniques AP-HP (11M documents)

**13 labels détectés** :
- NOM, PRENOM, MAIL, TEL, SECU (NIR)
- ADRESSE, ZIP, VILLE, HOPITAL
- DATE, DATE_NAISSANCE, IPP, NDA

**Mapping vers PLACEHOLDERS** :
```python
EDS_LABEL_MAP = {
    "NOM": "NOM",
    "PRENOM": "NOM",
    "MAIL": "EMAIL",
    "TEL": "TEL",
    "SECU": "NIR",
    # ...
}
```

#### Option B : CamemBERT-NER (fallback)

**Fichier** : `ner_manager_onnx.py`

**Modèles** :
- `cmarkea/distilcamembert-base-ner` (rapide, quantifié)
- `Jean-Baptiste/camembert-ner` (robuste)

**Runtime** : ONNX avec optimum.onnxruntime

**Tags supportés** : PER, LOC, ORG, DATE

**Seuils de confiance** :
```python
@dataclass
class NerThresholds:
    per: float = 0.90
    org: float = 0.90
    loc: float = 0.90
    date: float = 0.85
```

### 2.4 Extraction Trackare (Couche 4)

**Fonction** : `_extract_trackare_identity()`

**Champs structurés extraits** :
- Nom de naissance, Nom et Prénom
- Lieu de naissance, Ville de résidence
- Contacts (Conjoint, Parent, Époux, etc.)
- Personnel médical (Rédigé par, Aide:, IDE:, etc.)
- Pattern multi-lignes : `Prenom\nNOM`

**Regex spécialisées** :
- `RE_EXTRACT_PATIENT`, `RE_EXTRACT_NOM_NAISSANCE`
- `RE_EXTRACT_CONTACT`, `RE_EXTRACT_STAFF_ROLE`
- `RE_EXTRACT_DR_DEST`, `RE_EXTRACT_PR`

### 2.5 Détection Contextuelle (Couche 5)

**Patterns** :
- Noms après titres : `Dr. DUPONT`, `Pr. MARTIN`
- Noms en MAJUSCULES (hors stopwords médicaux)
- Listes virgulées : `le Dr X, Y, LAZARO`

**Stopwords médicaux** : ~4000 termes
- Médicaments (edsnlp drugs.json)
- Termes médicaux, anatomie, pathologies
- Abréviations, services hospitaliers
- Mots courants français

---

## 3. Consolidation Globale

**Fonction** : `_apply_global_pii_tokens()`

### Propagation des PII

Chaque PII détecté est propagé sur toutes les pages du document :

**Types propagés globalement** :
- `NOM_GLOBAL` : tous les tokens de noms
- `TEL_GLOBAL`, `EMAIL_GLOBAL`, `ADRESSE_GLOBAL`
- `CODE_POSTAL_GLOBAL`, `VILLE_GLOBAL`, `ETAB_GLOBAL`
- `VLM_SERVICE`, `VLM_ETAB`, `DATE_NAISSANCE`

### Noms compagnons

Détection de mots en MAJUSCULES adjacents à un nom connu :
```python
# Si "DUPONT" est détecté, et "JEAN-PIERRE DUPONT" apparaît
# → "JEAN-PIERRE" est ajouté comme nom compagnon
```

### Noms composés

Traitement en bloc : `JEAN-PIERRE`, `CAZELLES-BOUDIER`

### Rescan sélectif

Rescan des PII critiques ayant pu échapper au premier passage :
- EMAIL, TEL, IBAN, NIR
- Téléphones fragmentés sur plusieurs lignes
- Codes postaux orphelins

---

## 4. Redaction PDF

### 4.1 Vector Redaction (PDF textuels)

**Méthode** : `page.search_for()` (PyMuPDF)

1. Pour chaque PII dans l'audit
2. Recherche du texte exact dans la page
3. Si non trouvé et PII avec espaces → recherche mot par mot
4. Génération de rectangles de redaction
5. Application des rectangles (texte remplacé par noir)

### 4.2 Raster Redaction (PDF scannés)

**Méthode** : Conversion en image + redaction pixel

1. Conversion de chaque page en pixmap (DPI configurable)
2. Matching des PII via OCR word map
3. **Matching flou pour VLM** : identifiants numériques manuscrits
4. Dessin de rectangles noirs sur les zones PII
5. Reconstruction du PDF depuis les images

**Matching flou VLM** :
```python
def _search_ocr_words_fuzzy_digits(ocr_words, token, page_rect, min_ratio=0.7):
    """Compare les séquences de chiffres entre le token VLM et les mots OCR.
    Accepte une correspondance si ≥ min_ratio des chiffres matchent."""
```

Appliqué aux catégories :
- `VLM_NUM_PATIENT`, `VLM_NUM_LOT`, `VLM_NUM_ORD`
- `VLM_NDA`, `VLM_NIR`, `VLM_IPP`, `VLM_RPPS`

---

## 5. Audit Trail

**Format** : `.audit.jsonl` (JSON Lines)

**Structure** :
```python
@dataclass
class PiiHit:
    page: int
    kind: str  # Type de PII (EMAIL, NOM, VLM_TEL, etc.)
    original: str  # Texte original détecté
    placeholder: str  # Placeholder de remplacement
    bbox_hint: Optional[Tuple[float, float, float, float]]  # Coordonnées (optionnel)
```

**Traçabilité** :
- Chaque PII détecté est enregistré avec sa méthode de détection
- Permet l'analyse post-traitement
- Utilisé pour la validation post-anonymisation

---

## 6. Matériel et Performance

### Configuration actuelle

- **CPU** : AMD Ryzen 9 9950X (16 cœurs / 32 threads)
- **RAM** : 128 GB
- **GPU** : NVIDIA RTX 5070 (12 GB VRAM)
- **CUDA** : PyTorch 2.10.0 avec support CUDA

### Temps de traitement

**PDF textuels** :
- Extraction : < 1s
- Détection (Regex + NER GPU) : 2-5s
- Redaction : 1-2s
- **Total** : < 10s par PDF

**PDF scannés** :
- Extraction + OCR : 5-15s
- VLM (Ollama) : 10-30s par page (dépend du modèle)
- Détection (Regex + NER GPU) : 2-5s
- Redaction raster : 3-10s
- **Total** : 20-60s par PDF (selon nombre de pages)

### Optimisations possibles

1. **VLM** : Vérifier support GPU Ollama (CUDA)
2. **NER** : Batch processing optimisé (12 GB VRAM)
3. **Parallélisation** : 8-12 workers sur 16 cœurs
4. **Cache** : Résultats VLM par hash d'image

---

## 7. Points d'Attention

### Hallucinations VLM

Le VLM peut détecter des PII inexistants, surtout sur :
- Pages manuscrites complexes
- Documents mal orientés
- Tableaux denses

**Mitigation actuelle** :
- Masquage total si < 100 mots OCR
- Seuil de confiance (0.5)

**Amélioration proposée** :
- Validation croisée VLM ↔ NER
- Prompt amélioré avec exemples négatifs
- Rejet des détections non confirmées

### Faux Positifs NER

EDS-Pseudo peut détecter comme NOM :
- Médicaments (ex: "Eliquis", "Trulicity")
- Termes médicaux (ex: "Diabétique", "Cutanée")
- Mots courants (ex: "Toilette", "Repas")

**Mitigation** : Stopwords médicaux (~4000 termes)

### Performance VLM

Ollama en local peut être lent (10-30s par page).

**Solutions** :
- Vérifier support GPU
- Réduire `max_image_size` (trade-off qualité/vitesse)
- Cache des résultats
- Traitement parallèle (attention : charge GPU)

---

## 8. Dépendances Complètes

```
# Core
python>=3.12
pyyaml

# PDF
pymupdf
pdfplumber
pdfminer.six
pillow

# OCR
doctr[torch]
pytesseract

# NER
edsnlp[ml]>=0.12.0  # EDS-Pseudo
transformers
optimum
onnxruntime  # ou onnxruntime-gpu
sentencepiece

# VLM
# Aucune dépendance Python (utilise urllib)
# Nécessite Ollama installé localement : https://ollama.ai/

# Tests & Qualité (nouveaux)
pytest
pytest-cov
pydantic
structlog
jinja2
matplotlib
```

---

## 9. Configuration Ollama

### Installation

```bash
# Linux
curl -fsSL https://ollama.ai/install.sh | sh

# macOS
brew install ollama

# Windows
# Télécharger depuis https://ollama.ai/download
```

### Téléchargement du modèle

```bash
ollama pull qwen2.5vl:7b
# ou
ollama pull qwen3-vl:8b
```

### Vérification

```bash
# Lister les modèles disponibles
ollama list

# Tester le modèle
ollama run qwen2.5vl:7b
```

### Configuration GPU (optionnel)

Ollama détecte automatiquement le GPU CUDA si disponible.

Vérifier dans les logs :
```bash
ollama serve
# Chercher : "GPU detected: NVIDIA GeForce RTX 5070"
```

---

## 10. Workflow Complet

```
1. Chargement PDF
   ↓
2. Extraction texte (5 passes)
   ↓
3. Détection Regex
   ↓
4. VLM (si PDF scanné)
   ↓
5. NER (EDS-Pseudo ou CamemBERT)
   ↓
6. Extraction Trackare
   ↓
7. Détection contextuelle
   ↓
8. Consolidation globale
   ↓
9. Rescan sélectif
   ↓
10. Redaction PDF (vector ou raster)
    ↓
11. Génération audit.jsonl
    ↓
12. Validation post-anonymisation (à implémenter)
    ↓
13. Certificat de conformité (à implémenter)
```

---

## Conclusion

Le système actuel est **très sophistiqué** avec 5 couches de détection complémentaires. Les principaux axes d'amélioration sont :

1. **Métriques de qualité** : Créer un dataset annoté et mesurer Précision/Rappel/F1
2. **Validation VLM** : Réduire les hallucinations via validation croisée
3. **Optimisation GPU** : Accélérer NER et vérifier support GPU Ollama
4. **Tests de régression** : Suite automatique pour éviter les régressions
5. **Validation post-anonymisation** : Scanner de fuite automatique

La spécification mise à jour reflète maintenant cette architecture réelle.
