# 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.