Files
rpa_vision_v3/.kiro/specs/faiss-rebuild-propre/design.md
Dom a7de6a488b 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>
2026-03-31 14:04:41 +02:00

14 KiB

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

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

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

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

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

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

@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

@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

# 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

    # Après mise à jour de prototype validée
    if post_conditions_ok and prototype_updated:
        self.trigger_faiss_rebuild()
    
  2. Batch Session Trigger

    # À la fin d'une session d'apprentissage
    def end_learning_session(self):
        self.trigger_faiss_rebuild()
        self.save_session_metrics()
    
  3. Manual Trigger

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