feat: replay E2E fonctionnel — 25/25 actions, 0 retries, SomEngine via serveur

Validé sur PC Windows (DESKTOP-58D5CAC, 2560x1600) :
- 8 clics résolus visuellement (1 anchor_template, 1 som_text_match, 6 som_vlm)
- Score moyen 0.75, temps moyen 1.6s
- Texte tapé correctement (bonjour, test word, date, email)
- 0 retries, 2 actions non vérifiées (OK)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-03-31 14:04:41 +02:00
parent 5e0b53cfd1
commit a7de6a488b
79542 changed files with 6091757 additions and 1 deletions

View File

@@ -0,0 +1,410 @@
# Design Document - FAISS Rebuild Propre
**Auteur :** Dom, Alice Kiro - 22 décembre 2025
## Overview
Le système FAISS Rebuild Propre résout le problème de pollution d'index causé par l'accumulation de vecteurs obsolètes lors des mises à jour de prototypes. La stratégie adoptée est simple et sûre : **clear + reindex complet** depuis les derniers prototypes validés.
## Architecture
### Composants Principaux
```
┌─────────────────────────────────────────────────────────────┐
│ FAISS Rebuild Propre │
├─────────────────────────────────────────────────────────────┤
│ │
│ FAISSManager Enhanced ←→ WorkflowPipeline Enhanced │
│ ✅ clear() amélioré ✅ _extract_node_vector() │
│ ✅ reindex() nouveau ✅ _index_workflow_embeddings │
│ │
│ Test Suite ←→ Trigger Logic │
│ ✅ test_faiss_reindex.py ✅ Post-validation trigger │
│ ✅ Batch session trigger │
│ │
└─────────────────────────────────────────────────────────────┘
```
## Components and Interfaces
### 1. FAISSManager Enhanced
#### Interface Améliorée
```python
class FAISSManager:
def clear(self) -> None:
"""Vider complètement l'index + reset état d'entraînement."""
def reindex(self, items: Iterable[Tuple[str, np.ndarray, dict]],
force_train_ivf: bool = True) -> int:
"""Reconstruit l'index à partir d'une source canonique."""
def _create_index(self) -> faiss.Index:
"""Créer un nouvel index selon la configuration."""
```
#### Méthode clear() Améliorée
```python
def clear(self) -> None:
"""Vider complètement l'index + reset état d'entraînement."""
self.index = self._create_index()
self.metadata_store.clear()
self.next_id = 0
# IMPORTANT: reset IVF training state
self.training_vectors.clear()
self.is_trained = (self.index_type == "Flat")
```
**Améliorations :**
- Reset complet de l'état IVF training
- Réinitialisation des training_vectors
- Gestion correcte du flag is_trained selon le type d'index
#### Méthode reindex() Nouvelle
```python
def reindex(self, items: Iterable[Tuple[str, np.ndarray, dict]],
force_train_ivf: bool = True) -> int:
"""
Reconstruit l'index à partir d'une source canonique (vecteurs).
items: Iterable[(embedding_id: str, vector: np.ndarray, metadata: dict)]
"""
self.clear()
count = 0
for embedding_id, vector, metadata in items:
if vector is None:
continue
self.add_embedding(embedding_id, vector, metadata or {})
count += 1
# Si IVF + petit volume, add_embedding ne déclenche pas forcément l'entraînement
if (self.index_type == "IVF" and force_train_ivf and
(not self.is_trained) and self.training_vectors):
self._train_ivf_index()
return count
```
**Fonctionnalités :**
- Clear complet avant reconstruction
- Ajout sécurisé avec validation des vecteurs
- Force training IVF même pour petits volumes
- Retour du nombre d'éléments indexés
### 2. WorkflowPipeline Enhanced
#### Extraction de Vecteurs Multi-Version
```python
def _extract_node_vector(self, node) -> Optional[np.ndarray]:
"""
Récupérer le prototype vecteur d'un node, compatible avec plusieurs versions de modèle.
Retourne np.ndarray ou None.
"""
import numpy as np
# v1: prototype stocké en liste directement
tpl = getattr(node, "template", None)
if tpl is not None:
proto_list = getattr(tpl, "embedding_prototype", None)
if isinstance(proto_list, list):
v = np.array(proto_list, dtype=np.float32)
return v
# v2: prototype stocké sur disque via EmbeddingPrototype.vector_id
emb = getattr(tpl, "embedding", None)
if emb is not None:
vector_id = getattr(emb, "vector_id", None)
if vector_id:
try:
return np.load(vector_id).astype(np.float32)
except Exception:
return None
# fallback (ancienne nomenclature)
st = getattr(node, "screen_template", None)
if st is not None:
p = getattr(st, "embedding_prototype_path", None)
if p:
try:
return np.load(p).astype(np.float32)
except Exception:
return None
return None
```
**Support Multi-Version :**
- v1: embedding_prototype en liste directe
- v2: embedding.vector_id avec fichier sur disque
- Fallback: screen_template legacy
- Gestion robuste des erreurs
#### Indexation Workflow Améliorée
```python
def _index_workflow_embeddings(self, workflow: Workflow) -> None:
"""
Indexer les embeddings des nodes dans FAISS (rebuild propre).
"""
if not self.faiss_manager:
return
items = []
for node in workflow.nodes:
vec = self._extract_node_vector(node)
if vec is None:
continue
items.append((
node.node_id,
vec,
{
"workflow_id": workflow.workflow_id,
"node_id": node.node_id,
"node_name": getattr(node, "name", "")
}
))
n = self.faiss_manager.reindex(items, force_train_ivf=True)
logger.info(f"FAISS reindexed: {n} node prototypes (workflow={workflow.workflow_id})")
```
**Avantages :**
- Construction de liste canonique avant reindex
- Métadonnées enrichies (workflow_id, node_id, node_name)
- Force training IVF pour cohérence
- Logging informatif
## Data Models
### Item de Reindex
```python
@dataclass
class ReindexItem:
embedding_id: str
vector: np.ndarray
metadata: Dict[str, Any]
def __post_init__(self):
if self.vector is not None:
self.vector = self.vector.astype(np.float32)
```
### Résultat de Reindex
```python
@dataclass
class ReindexResult:
success: bool
items_processed: int
items_skipped: int
training_performed: bool
duration_ms: float
error_message: Optional[str] = None
```
## Correctness Properties
*Une propriété est une caractéristique ou un comportement qui doit être vrai pour toutes les exécutions valides d'un système - essentiellement, une déclaration formelle sur ce que le système devrait faire.*
### Property 1: Clear Operation Completeness
*Pour tout* FAISSManager, après un appel à clear(), l'index doit être vide (ntotal=0), les métadonnées vides, next_id remis à 0, training_vectors vidé, et is_trained défini selon le type d'index
**Validates: Requirements 1.2, 1.3, 1.4, 1.5**
### Property 2: Embedding Removal Completeness
*Pour tout* embedding ajouté puis supprimé via remove_embedding(), ni les métadonnées ni le vecteur ne doivent être présents dans l'index
**Validates: Requirements 1.1**
### Property 3: Reindex Consistency
*Pour tout* ensemble d'items valides, reindex() doit produire un index contenant exactement ces items, sans doublons ni éléments manquants, et retourner le bon nombre d'items traités
**Validates: Requirements 2.1, 2.2, 2.4**
### Property 4: Invalid Vector Handling
*Pour tout* ensemble d'items contenant des vecteurs invalides (None), reindex() doit les ignorer et continuer le traitement des vecteurs valides
**Validates: Requirements 2.5**
### Property 5: IVF Training Consistency
*Pour tout* reindex avec force_train_ivf=True sur un index IVF contenant des vecteurs, l'index résultant doit être dans l'état is_trained=True
**Validates: Requirements 2.3**
### Property 6: Vector Extraction Multi-Format
*Pour tout* node contenant un vecteur prototype dans n'importe quel format supporté (template.embedding_prototype, embedding.vector_id, ou screen_template legacy), _extract_node_vector() doit retourner un np.ndarray de type float32
**Validates: Requirements 3.1, 3.2, 3.3, 3.4**
### Property 7: Vector Extraction Graceful Failure
*Pour tout* node sans vecteur prototype valide, _extract_node_vector() doit retourner None sans lever d'exception
**Validates: Requirements 3.5**
### Property 8: Workflow Indexing Completeness
*Pour tout* workflow contenant des nodes avec vecteurs valides, _index_workflow_embeddings() doit extraire tous les vecteurs valides et les indexer avec les métadonnées appropriées (workflow_id, node_id)
**Validates: Requirements 4.1, 4.2**
### Property 9: IVF Training Force in Pipeline
*Pour tout* workflow indexé avec un FAISSManager IVF, le système doit forcer l'entraînement IVF pour assurer la cohérence
**Validates: Requirements 4.3**
### Property 10: Trigger Logic Validation
*Pour tout* prototype mis à jour avec post-conditions OK, le système doit déclencher un rebuild FAISS
**Validates: Requirements 5.1**
### Property 11: Batch Session Trigger
*Pour toute* session d'apprentissage par batch qui se termine, le système doit déclencher un rebuild FAISS
**Validates: Requirements 5.2**
### Property 12: Frame Update Restraint
*Pour toute* série de mises à jour de frames, le système ne doit pas déclencher de rebuild à chaque frame
**Validates: Requirements 5.3**
### Property 13: Rebuild Failure Resilience
*Pour tout* échec de rebuild, le système doit maintenir l'index existant dans un état valide sans corruption
**Validates: Requirements 5.5**
### Property 14: Flat Index Reindex Cleanup
*Pour tout* index Flat, après reindex avec de nouveaux items, les anciens items doivent être complètement supprimés
**Validates: Requirements 6.1**
### Property 15: IVF Small Dataset Training
*Pour tout* index IVF, même avec un petit dataset, reindex() avec force_train_ivf=True doit déclencher l'entraînement
**Validates: Requirements 6.2**
### Property 16: Metadata Consistency After Reindex
*Pour tout* reindex, les métadonnées de chaque item doivent être préservées et cohérentes après l'opération
**Validates: Requirements 6.3**
### Property 17: Search Accuracy After Reindex
*Pour tout* reindex suivi d'une recherche, les résultats doivent être corrects et correspondre aux vecteurs indexés
**Validates: Requirements 6.4**
### Property 18: Index State Validation
*Pour tout* reindex, l'état de l'index (ntotal, is_trained) doit être cohérent avec le contenu effectivement indexé
**Validates: Requirements 6.5**
## Error Handling
### Stratégies de Gestion d'Erreurs
1. **Vector Loading Errors**
- Try/catch sur np.load() avec fallback gracieux
- Log warning pour vecteurs non chargeables
- Continue processing avec autres vecteurs
2. **FAISS Training Errors**
- Validation du nombre minimum de vecteurs pour IVF
- Retry avec paramètres ajustés si échec
- Fallback vers index Flat si IVF impossible
3. **Memory Errors**
- Monitoring de l'utilisation mémoire pendant reindex
- Batch processing pour gros volumes
- Cleanup automatique en cas d'échec
### Logging Strategy
```python
# Niveaux de logging
logger.info(f"FAISS reindex started: {len(items)} items")
logger.debug(f"Processing item {embedding_id}: vector shape {vector.shape}")
logger.warning(f"Skipped invalid vector for {embedding_id}")
logger.error(f"FAISS reindex failed: {error}")
logger.info(f"FAISS reindex completed: {count} items in {duration}ms")
```
## Testing Strategy
### Tests Unitaires
1. **test_faiss_clear_resets_state**
- Valide reset complet après clear()
- Vérifie état IVF training
2. **test_faiss_reindex_flat_removes_old_entries**
- Valide suppression des anciens vecteurs
- Vérifie cohérence métadonnées
3. **test_faiss_reindex_ivf_trains_even_small**
- Valide training forcé sur petits datasets
- Vérifie état is_trained
4. **test_extract_node_vector_multi_version**
- Teste support des différents formats
- Valide fallback gracieux
### Tests d'Intégration
1. **test_workflow_pipeline_reindex_integration**
- Test complet pipeline → FAISS
- Validation end-to-end
2. **test_reindex_performance_large_dataset**
- Test performance sur gros volumes
- Validation mémoire
### Tests de Propriétés
Chaque propriété de correctness sera implémentée comme un test property-based avec minimum 100 itérations.
## Trigger Logic
### Moments de Déclenchement
1. **Post-Validation Trigger**
```python
# Après mise à jour de prototype validée
if post_conditions_ok and prototype_updated:
self.trigger_faiss_rebuild()
```
2. **Batch Session Trigger**
```python
# À la fin d'une session d'apprentissage
def end_learning_session(self):
self.trigger_faiss_rebuild()
self.save_session_metrics()
```
3. **Manual Trigger**
```python
# Commande administrative
def admin_rebuild_faiss(self):
return self.faiss_manager.reindex(self.get_all_prototypes())
```
### Éviter les Triggers Excessifs
- **PAS** de rebuild à chaque frame
- **PAS** de rebuild sur échecs temporaires
- **OUI** rebuild après validation réussie
- **OUI** rebuild en fin de batch
## Performance Considerations
### Optimisations
1. **Batch Processing**
- Grouper les updates avant rebuild
- Éviter les rebuilds fréquents
2. **Memory Management**
- Clear explicite des anciens vecteurs
- Monitoring utilisation mémoire
3. **IVF Training**
- Force training même petits volumes
- Paramètres optimisés par défaut
### Métriques
- Temps de rebuild par nombre de vecteurs
- Utilisation mémoire pendant rebuild
- Taux de réussite des training IVF
- Fréquence des rebuilds déclenchés
## Conclusion
Le système FAISS Rebuild Propre garantit la cohérence et la propreté de l'index FAISS en éliminant la pollution par vecteurs obsolètes. L'approche "clear + reindex complet" est simple, sûre et efficace pour maintenir la qualité de l'apprentissage du système RPA Vision V3.