# Document de Design - Workflow Graph Implementation ## Vue d'Ensemble Ce document décrit le design détaillé de l'implémentation de l'architecture Workflow Graph pour RPA Vision V2. Le système transforme des captures d'écran brutes en workflows sémantiques appris à travers 5 couches d'abstraction progressive. **Philosophie** : "Observer → Comprendre → Apprendre → Agir" Le système ne travaille PAS avec des coordonnées de clics, mais avec une compréhension sémantique des interfaces : types d'éléments, rôles, contexte visuel et textuel. **Architecture en 5 Couches** : ``` RawSession (Couche 0) → ScreenState (Couche 1) → UIElement Detection (Couche 2) → State Embedding (Couche 3) → Workflow Graph (Couche 4) ``` **Référence** : Ce design s'appuie sur `docs/reference/ARCHITECTURE_VISION_COMPLETE.md` et `docs/reference/ARCHITECTURE_ENRICHISSEMENTS.md`. ## Architecture ### Architecture Globale ``` ┌─────────────────────────────────────────────────────────────┐ │ Couche 4 : Workflow Graph │ │ WorkflowNode + WorkflowEdge + Learning States │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Graph Builder│ │ Node Matcher │ │ Action Executor │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────────────────────────┐ │ Couche 3 : State Embedding │ │ Fusion Multi-Modale (Image + Text + UI + Context) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Fusion Engine│ │ FAISS Index │ │ Similarity Comp │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────────────────────────┐ │ Couche 2 : UIElement Detection │ │ Détection Sémantique (Type + Rôle + Embeddings) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ VLM Detector │ │ Classifier │ │ Embedding Gen │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────────────────────────┐ │ Couche 1 : ScreenState │ │ Analyse Multi-Modale (4 Niveaux) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Raw Capture │ │ Perception │ │ Semantic + Ctx │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────────────────────────┐ │ Couche 0 : RawSession │ │ Capture Brute (Events + Screenshots) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Event Logger │ │ Screenshot │ │ Session Manager │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ### Structure des Répertoires ``` geniusia2/ ├── core/ │ ├── models/ │ │ ├── raw_session.py # Couche 0 : RawSession │ │ ├── screen_state.py # Couche 1 : ScreenState │ │ ├── ui_element.py # Couche 2 : UIElement │ │ ├── state_embedding.py # Couche 3 : State Embedding │ │ └── workflow_graph.py # Couche 4 : Workflow Graph │ ├── capture/ │ │ ├── event_capture.py # Capture événements │ │ └── screenshot_capture.py # Capture screenshots │ ├── detection/ │ │ ├── ui_detector.py # Détection UI avec VLM │ │ └── text_detector.py # Détection texte │ ├── embedding/ │ │ ├── fusion_engine.py # Fusion multi-modale │ │ ├── faiss_manager.py # Gestion index FAISS │ │ └── similarity.py # Calculs de similarité │ ├── graph/ │ │ ├── graph_builder.py # Construction graphes │ │ ├── node_matcher.py # Matching nodes │ │ ├── action_executor.py # Exécution actions │ │ └── learning_manager.py # Gestion états apprentissage │ └── persistence/ │ ├── json_serializer.py # Sérialisation JSON │ └── storage_manager.py # Gestion stockage └── data/ ├── sessions/ # RawSessions ├── screen_states/ # ScreenStates ├── embeddings/ # Embeddings (.npy) ├── faiss_index/ # Index FAISS └── workflows/ # Workflow Graphs ``` ## Composants et Interfaces ### Couche 0 : RawSession **Responsabilité** : Capture fidèle de tous les événements utilisateur avec screenshots. **Classe Principale** : `RawSession` ```python @dataclass class RawSession: schema_version: str = "rawsession_v1" session_id: str agent_version: str environment: Dict[str, Any] user: Dict[str, str] context: Dict[str, str] started_at: datetime ended_at: Optional[datetime] events: List[Event] screenshots: List[Screenshot] def add_event(self, event: Event) -> None: """Ajouter un événement à la session""" def add_screenshot(self, screenshot: Screenshot) -> None: """Ajouter un screenshot à la session""" def to_json(self) -> Dict[str, Any]: """Sérialiser en JSON""" @classmethod def from_json(cls, data: Dict[str, Any]) -> 'RawSession': """Désérialiser depuis JSON""" ``` **Classe Event** : ```python @dataclass class Event: t: float # Timestamp relatif en secondes type: str # mouse_click, key_press, etc. window: WindowContext screenshot_id: Optional[str] # Champs spécifiques selon type data: Dict[str, Any] ``` ### Couche 1 : ScreenState **Responsabilité** : Représentation structurée d'un écran à 4 niveaux d'abstraction. **Classe Principale** : `ScreenState` ```python @dataclass class ScreenState: screen_state_id: str timestamp: datetime session_id: str window: WindowContext # Niveau 1 : Raw raw: RawLevel # Niveau 2 : Perception perception: PerceptionLevel # Niveau 3 : Sémantique UI ui_elements: List[UIElement] # Niveau 4 : Contexte Métier context: ContextLevel metadata: Dict[str, Any] def to_json(self) -> Dict[str, Any]: """Sérialiser en JSON""" @classmethod def from_json(cls, data: Dict[str, Any]) -> 'ScreenState': """Désérialiser depuis JSON""" ``` **Niveaux** : ```python @dataclass class RawLevel: screenshot_path: str capture_method: str file_size_bytes: int @dataclass class PerceptionLevel: embedding: EmbeddingRef detected_text: List[str] text_detection_method: str confidence_avg: float @dataclass class ContextLevel: current_workflow_candidate: Optional[str] workflow_step: Optional[int] user_id: str tags: List[str] business_variables: Dict[str, Any] ``` ### Couche 2 : UIElement Detection **Responsabilité** : Détection sémantique des éléments UI avec types, rôles et embeddings. **Classe Principale** : `UIElement` ```python @dataclass class UIElement: element_id: str type: str # button, text_input, checkbox, etc. role: str # primary_action, cancel, form_input, etc. bbox: Tuple[int, int, int, int] center: Tuple[int, int] label: str label_confidence: float embeddings: UIElementEmbeddings visual_features: VisualFeatures tags: List[str] confidence: float metadata: Dict[str, Any] def to_json(self) -> Dict[str, Any]: """Sérialiser en JSON""" ``` **Embeddings Duaux** : ```python @dataclass class UIElementEmbeddings: image: EmbeddingRef # Embedding de l'image croppée text: EmbeddingRef # Embedding du texte détecté ``` **Détecteur UI** : ```python class UIDetector: def __init__(self, vlm_model: str, clip_model: str): self.vlm = VLMClient(vlm_model) self.clip = CLIPEmbedder(clip_model) def detect_elements(self, screenshot: np.ndarray, window_context: WindowContext) -> List[UIElement]: """Détecter tous les éléments UI dans un screenshot""" # 1. Proposer régions d'intérêt avec VLM # 2. Caractériser chaque élément (crop, OCR, embeddings) # 3. Classifier type et rôle # 4. Retourner liste d'UIElements ``` ### Couche 3 : State Embedding **Responsabilité** : Fusion multi-modale en vecteur unique (fingerprint d'écran). **Classe Principale** : `StateEmbedding` ```python @dataclass class StateEmbedding: embedding_id: str vector_id: str # Chemin vers .npy dimensions: int fusion_method: str # "weighted" ou "concat_projection" components: Dict[str, EmbeddingComponent] metadata: Dict[str, Any] def get_vector(self) -> np.ndarray: """Charger le vecteur depuis le fichier""" def compute_similarity(self, other: 'StateEmbedding') -> float: """Calculer similarité cosinus avec autre embedding""" ``` **Moteur de Fusion** : ```python class FusionEngine: def __init__(self, method: str = "weighted", weights: Optional[Dict[str, float]] = None): self.method = method self.weights = weights or { "image": 0.5, "text": 0.3, "title": 0.1, "ui": 0.1 } def fuse(self, img_emb: np.ndarray, text_emb: np.ndarray, title_emb: np.ndarray, ui_emb: np.ndarray) -> np.ndarray: """Fusionner tous les embeddings en un seul vecteur""" if self.method == "weighted": return self._weighted_fusion(img_emb, text_emb, title_emb, ui_emb) elif self.method == "concat_projection": return self._concat_projection(img_emb, text_emb, title_emb, ui_emb) def _weighted_fusion(self, img_emb, text_emb, title_emb, ui_emb) -> np.ndarray: """Fusion pondérée simple""" fused = ( self.weights["image"] * normalize(img_emb) + self.weights["text"] * normalize(text_emb) + self.weights["title"] * normalize(title_emb) + self.weights["ui"] * normalize(ui_emb) ) return normalize(fused) ``` **Gestionnaire FAISS** : ```python class FAISSManager: def __init__(self, index_path: str, dimension: int = 512): self.index_path = index_path self.dimension = dimension self.index = self._load_or_create_index() self.metadata_store: Dict[int, Dict[str, Any]] = {} def add_embedding(self, embedding: np.ndarray, metadata: Dict[str, Any]) -> int: """Ajouter un embedding à l'index""" idx = self.index.ntotal self.index.add(embedding.reshape(1, -1)) self.metadata_store[idx] = metadata return idx def search_similar(self, query: np.ndarray, k: int = 5) -> List[SearchResult]: """Chercher les k plus proches voisins""" distances, indices = self.index.search(query.reshape(1, -1), k) results = [] for dist, idx in zip(distances[0], indices[0]): results.append(SearchResult( id=int(idx), distance=float(dist), similarity=1.0 - float(dist), # Cosine similarity metadata=self.metadata_store.get(int(idx), {}) )) return results ``` ### Couche 4 : Workflow Graph **Responsabilité** : Modélisation des workflows en graphes avec apprentissage progressif. **Classe WorkflowNode** : ```python @dataclass class WorkflowNode: node_id: str label: str description: str screen_template: ScreenTemplate metadata: Dict[str, Any] def matches(self, screen_state: ScreenState, state_embedding: StateEmbedding) -> Tuple[bool, float]: """Vérifier si un ScreenState correspond à ce node""" # 1. Vérifier contraintes fenêtre # 2. Vérifier texte requis # 3. Vérifier éléments UI requis # 4. Vérifier similarité embedding # Retourner (match, confidence) @dataclass class ScreenTemplate: window: WindowConstraints required_text_any: List[str] required_ui_elements: List[UIElementConstraint] embedding_prototype: EmbeddingPrototype optional_elements: List[UIElementConstraint] @dataclass class EmbeddingPrototype: provider: str vector_id: str min_cosine_similarity: float sample_count: int ``` **Classe WorkflowEdge** : ```python @dataclass class WorkflowEdge: edge_id: str from_node: str to_node: str action: Action constraints: EdgeConstraints post_conditions: PostConditions stats: EdgeStats metadata: Dict[str, Any] def can_execute(self, current_state: ScreenState) -> Tuple[bool, str]: """Vérifier si l'edge peut être exécuté""" # Vérifier pre-conditions def execute(self, executor: ActionExecutor) -> ExecutionResult: """Exécuter l'action de cet edge""" @dataclass class Action: type: str # mouse_click, key_press, text_input, compound target: TargetSpec parameters: Dict[str, Any] @dataclass class TargetSpec: role: str # Rôle sémantique de l'élément cible selection_policy: str # first, last, by_similarity fallback_strategy: str # visual_similarity, position ``` **Classe Workflow** : ```python @dataclass class Workflow: workflow_id: str name: str description: str version: int learning_state: str # OBSERVATION, COACHING, AUTO_CANDIDATE, AUTO_CONFIRMÉ created_at: datetime updated_at: datetime entry_nodes: List[str] end_nodes: List[str] nodes: List[WorkflowNode] edges: List[WorkflowEdge] safety_rules: SafetyRules stats: WorkflowStats learning: LearningConfig metadata: Dict[str, Any] def get_node(self, node_id: str) -> Optional[WorkflowNode]: """Récupérer un node par ID""" def get_outgoing_edges(self, node_id: str) -> List[WorkflowEdge]: """Récupérer tous les edges sortants d'un node""" def to_json(self) -> Dict[str, Any]: """Sérialiser en JSON""" ``` ### Graph Builder **Responsabilité** : Construire automatiquement des Workflow Graphs depuis des RawSessions. ```python class GraphBuilder: def __init__(self, faiss_manager: FAISSManager, fusion_engine: FusionEngine, ui_detector: UIDetector): self.faiss = faiss_manager self.fusion = fusion_engine self.ui_detector = ui_detector def build_from_session(self, session: RawSession) -> Optional[Workflow]: """Construire un workflow depuis une session""" # 1. Créer ScreenStates pour tous les screenshots screen_states = self._create_screen_states(session) # 2. Calculer State Embeddings embeddings = self._compute_embeddings(screen_states) # 3. Détecter patterns répétés patterns = self._detect_patterns(screen_states, embeddings) # 4. Construire nodes et edges if patterns: return self._build_workflow(patterns) return None def _detect_patterns(self, screen_states: List[ScreenState], embeddings: List[StateEmbedding]) -> List[Pattern]: """Détecter séquences répétées""" # Utiliser clustering sur embeddings # Identifier transitions récurrentes # Retourner patterns détectés ``` ### Node Matcher **Responsabilité** : Matcher un ScreenState actuel contre les WorkflowNodes existants. ```python class NodeMatcher: def __init__(self, faiss_manager: FAISSManager, fusion_engine: FusionEngine): self.faiss = faiss_manager self.fusion = fusion_engine def match(self, screen_state: ScreenState, workflow: Workflow) -> Optional[NodeMatch]: """Trouver le node correspondant au ScreenState actuel""" # 1. Calculer State Embedding state_emb = self._compute_state_embedding(screen_state) # 2. Chercher dans FAISS les prototypes similaires candidates = self.faiss.search_similar(state_emb.get_vector(), k=5) # 3. Filtrer par workflow_id candidates = [c for c in candidates if c.metadata.get('workflow_id') == workflow.workflow_id] # 4. Valider contraintes pour chaque candidat for candidate in candidates: node = workflow.get_node(candidate.metadata['node_id']) if node: matches, confidence = node.matches(screen_state, state_emb) if matches: return NodeMatch( node=node, confidence=confidence, embedding_similarity=candidate.similarity ) return None @dataclass class NodeMatch: node: WorkflowNode confidence: float embedding_similarity: float ``` ### Action Executor **Responsabilité** : Exécuter les actions définies dans WorkflowEdges. ```python class ActionExecutor: def __init__(self, input_controller): self.input = input_controller def execute_edge(self, edge: WorkflowEdge, current_state: ScreenState) -> ExecutionResult: """Exécuter l'action d'un edge""" # 1. Vérifier pre-conditions can_execute, reason = edge.can_execute(current_state) if not can_execute: return ExecutionResult(success=False, reason=reason) # 2. Trouver élément cible par rôle target_element = self._find_target_element( edge.action.target, current_state ) if not target_element: return ExecutionResult(success=False, reason="Target element not found") # 3. Exécuter action if edge.action.type == "mouse_click": self._execute_click(target_element, edge.action.parameters) elif edge.action.type == "text_input": self._execute_text_input(target_element, edge.action.parameters) elif edge.action.type == "compound": self._execute_compound(edge.action, current_state) # 4. Attendre post-conditions success = self._wait_for_postconditions(edge.post_conditions) return ExecutionResult(success=success) def _find_target_element(self, target: TargetSpec, state: ScreenState) -> Optional[UIElement]: """Trouver élément UI par rôle sémantique""" candidates = [el for el in state.ui_elements if el.role == target.role] if not candidates: return None if target.selection_policy == "first": return candidates[0] elif target.selection_policy == "last": return candidates[-1] elif target.selection_policy == "by_similarity": # Utiliser embedding similarity return self._select_by_similarity(candidates, target) return candidates[0] @dataclass class ExecutionResult: success: bool reason: Optional[str] = None execution_time_ms: Optional[float] = None ``` ### Learning Manager **Responsabilité** : Gérer les états d'apprentissage et transitions. ```python class LearningManager: def __init__(self): self.workflows: Dict[str, Workflow] = {} def update_workflow_stats(self, workflow_id: str, execution_result: ExecutionResult) -> None: """Mettre à jour statistiques après exécution""" workflow = self.workflows[workflow_id] workflow.stats.total_executions += 1 if execution_result.success: workflow.stats.success_count += 1 else: workflow.stats.failure_count += 1 # Vérifier si transition d'état nécessaire self._check_state_transition(workflow) def _check_state_transition(self, workflow: Workflow) -> None: """Vérifier et effectuer transitions d'état si nécessaire""" current_state = workflow.learning_state if current_state == "OBSERVATION": if self._can_transition_to_coaching(workflow): self._transition_to(workflow, "COACHING") elif current_state == "COACHING": if self._can_transition_to_auto_candidate(workflow): self._transition_to(workflow, "AUTO_CANDIDATE") elif current_state == "AUTO_CANDIDATE": if self._can_transition_to_auto_confirmed(workflow): # Nécessite validation utilisateur self._request_user_approval(workflow) elif current_state == "AUTO_CONFIRMÉ": if self._should_rollback(workflow): self._transition_to(workflow, "COACHING") def _can_transition_to_coaching(self, workflow: Workflow) -> bool: """Vérifier critères pour OBSERVATION → COACHING""" return (workflow.stats.observed_runs >= 5 and workflow.stats.avg_similarity >= 0.90) def _can_transition_to_auto_candidate(self, workflow: Workflow) -> bool: """Vérifier critères pour COACHING → AUTO_CANDIDATE""" return (workflow.stats.assist_runs >= 10 and workflow.stats.success_rate >= 0.90) def _can_transition_to_auto_confirmed(self, workflow: Workflow) -> bool: """Vérifier critères pour AUTO_CANDIDATE → AUTO_CONFIRMÉ""" return (workflow.stats.auto_candidate_runs >= 20 and workflow.stats.success_rate >= 0.95) def _should_rollback(self, workflow: Workflow) -> bool: """Vérifier si rollback nécessaire""" return workflow.stats.recent_confidence < 0.90 ``` ## Modèles de Données ### Format JSON Complet **RawSession** : ```json { "schema_version": "rawsession_v1", "session_id": "sess_2025-11-22T10-15-00_user1", "agent_version": "0.2.0", "environment": { "platform": "linux", "hostname": "dev-machine", "screen": {"primary_resolution": [1920, 1080]} }, "user": {"id": "user1", "label": "Developer User"}, "context": {"customer": "Demo", "training_label": "workflow_test"}, "started_at": "2025-11-22T10:15:00Z", "ended_at": "2025-11-22T10:30:00Z", "events": [ { "t": 0.523, "type": "mouse_click", "button": "left", "pos": [800, 400], "window": {"title": "App", "app_name": "app.exe"}, "screenshot_id": "shot_0001" } ], "screenshots": [ { "screenshot_id": "shot_0001", "relative_path": "shots/shot_0001.png", "captured_at": "2025-11-22T10:15:00.523Z" } ] } ``` **ScreenState** : ```json { "screen_state_id": "screen_2025-11-22T10-15-32.123Z", "timestamp": "2025-11-22T10:15:32.123Z", "session_id": "sess_2025-11-22T10-15-00_user1", "window": { "app_name": "app", "window_title": "Main Window", "screen_resolution": [1920, 1080] }, "raw": { "screenshot_path": "data/screens/2025-11-22/10-15-32.png", "capture_method": "mss", "file_size_bytes": 245678 }, "perception": { "embedding": { "provider": "openclip_ViT-B-32", "vector_id": "data/embeddings/screens/screen_2025-11-22T10-15-32.123Z.npy", "dimensions": 512 }, "detected_text": ["Button", "Input", "Submit"], "text_detection_method": "qwen_vl", "confidence_avg": 0.92 }, "ui_elements": [ { "element_id": "el_btn_001", "type": "button", "role": "primary_action", "bbox": [100, 200, 200, 240], "center": [150, 220], "label": "Submit", "label_confidence": 0.96, "embeddings": { "image": { "provider": "openclip_ViT-B-32", "vector_id": "data/embeddings/elements/el_btn_001_img.npy", "dimensions": 512 }, "text": { "provider": "openclip_ViT-B-32", "vector_id": "data/embeddings/elements/el_btn_001_txt.npy", "dimensions": 512 } }, "visual_features": { "dominant_color": "#4CAF50", "has_icon": false, "shape": "rectangle", "size_category": "medium" }, "tags": ["action", "primary"], "confidence": 0.94 } ], "context": { "current_workflow_candidate": null, "workflow_step": null, "user_id": "user1", "tags": ["demo"], "business_variables": {} }, "metadata": { "processing_time_ms": 245, "ui_elements_count": 5 } } ``` **Workflow Graph** : ```json { "workflow_id": "WF_demo_workflow", "name": "Demo Workflow", "description": "Simple demo workflow for testing", "version": 1, "learning_state": "OBSERVATION", "created_at": "2025-11-22T10:45:00Z", "updated_at": "2025-11-22T10:45:00Z", "entry_nodes": ["N1_start"], "end_nodes": ["N3_end"], "nodes": [ { "node_id": "N1_start", "label": "Start Screen", "description": "Initial screen with form", "screen_template": { "window": { "app_name_any_of": ["app"], "title_contains_any_of": ["Main"] }, "required_text_any": ["Submit", "Input"], "required_ui_elements": [ { "role": "primary_action", "type_any_of": ["button"], "min_count": 1 } ], "embedding_prototype": { "provider": "openclip_ViT-B-32", "vector_id": "data/embeddings/workflows/WF_demo/N1_prototype.npy", "min_cosine_similarity": 0.85, "sample_count": 5 } }, "metadata": { "created_at": "2025-11-22T10:45:00Z", "observation_count": 5 } } ], "edges": [ { "edge_id": "E1_submit", "from_node": "N1_start", "to_node": "N2_processing", "action": { "type": "mouse_click", "target": { "role": "primary_action", "selection_policy": "first", "fallback_strategy": "visual_similarity" }, "parameters": { "click_offset": [0, 0], "wait_after_ms": 500 } }, "constraints": { "max_delay_seconds": 5, "pre_conditions": ["element:primary_action_visible"], "post_conditions": ["window_title_changed"] }, "post_conditions": { "expected_node": "N2_processing", "min_similarity": 0.85, "timeout_seconds": 5 }, "stats": { "manual_executions": 5, "assist_executions": 0, "auto_executions": 0, "success_count": 5, "failure_count": 0, "avg_execution_time_ms": 1200 } } ], "safety_rules": { "forbidden_text_clicks": ["Delete", "Remove"], "forbidden_roles": ["delete_action"], "require_confirmation_for": ["irreversible_action"] }, "stats": { "observed_runs": 5, "assist_runs": 0, "auto_candidate_runs": 0, "auto_confirmed_runs": 0, "success_rate_overall": 1.0, "avg_duration_seconds": 30.5 }, "learning": { "state": "OBSERVATION", "thresholds": { "min_observed_runs_for_coaching": 5, "min_assist_runs_for_auto_candidate": 10, "min_auto_candidate_runs_for_auto_confirmed": 20 } } } ``` ## Gestion des Erreurs ### Échecs de Matching **Scénario** : Aucun node ne correspond au ScreenState actuel. **Gestion** : 1. Logger le ScreenState non matché avec screenshot 2. Calculer similarité avec tous les nodes existants 3. Si similarité proche (0.75-0.84), suggérer mise à jour du node 4. Si similarité faible (<0.75), suggérer création d'un nouveau node 5. Notifier l'utilisateur et mettre en pause l'exécution ### Échecs de Détection UI **Scénario** : Élément cible non trouvé par rôle. **Gestion** : 1. Logger l'échec avec contexte (node, edge, rôle recherché) 2. Essayer stratégie de fallback (visual similarity) 3. Si fallback échoue, essayer position approximative 4. Si tout échoue, notifier utilisateur et demander correction 5. Mettre à jour le template du node avec feedback ### Violations de Post-Conditions **Scénario** : Post-conditions non satisfaites après exécution. **Gestion** : 1. Logger la violation avec détails (attendu vs réel) 2. Attendre timeout configuré 3. Si toujours pas satisfait, marquer exécution comme échec 4. Incrémenter compteur d'échecs pour cet edge 5. Si échecs répétés (>3), marquer edge comme problématique ### Changements d'UI Détectés **Scénario** : Similarité d'embedding chute significativement. **Gestion** : 1. Détecter changement (similarité < 0.70 vs prototype) 2. Capturer nouveau screenshot pour analyse 3. Mettre en pause l'exécution automatique 4. Notifier utilisateur du changement détecté 5. Proposer ré-apprentissage du node affecté ## Stratégie de Test ### Tests Unitaires **Composants à Tester** : 1. **RawSession** : - Sérialisation/désérialisation JSON - Ajout d'événements et screenshots - Validation de schéma 2. **ScreenState** : - Création des 4 niveaux - Sérialisation/désérialisation JSON - Validation de structure 3. **UIElement** : - Détection de types et rôles - Génération d'embeddings duaux - Calcul de features visuelles 4. **StateEmbedding** : - Fusion pondérée - Fusion par concaténation - Calcul de similarité cosinus 5. **FAISSManager** : - Ajout d'embeddings - Recherche de similarité - Sauvegarde/chargement d'index 6. **WorkflowNode** : - Matching avec ScreenState - Validation de contraintes - Calcul de confiance 7. **WorkflowEdge** : - Validation de pre-conditions - Exécution d'actions - Vérification de post-conditions 8. **LearningManager** : - Transitions d'états - Calcul de métriques - Détection de rollback **Framework** : pytest avec fixtures ### Tests d'Intégration **Scénarios** : 1. **Pipeline Complet RawSession → Workflow** : - Capturer session - Créer ScreenStates - Détecter UI elements - Calculer embeddings - Construire workflow graph - Vérifier structure du graphe 2. **Matching et Exécution** : - Charger workflow existant - Matcher ScreenState actuel - Trouver edge sortant - Exécuter action - Vérifier transition 3. **Apprentissage Progressif** : - Simuler 5 observations → COACHING - Simuler 10 assistances → AUTO_CANDIDATE - Simuler 20 exécutions → AUTO_CONFIRMÉ - Vérifier transitions 4. **Gestion d'Erreurs** : - Simuler échec de matching - Simuler élément non trouvé - Simuler violation post-conditions - Vérifier récupération ### Tests de Performance **Métriques Cibles** : | Opération | Temps Cible | Méthode de Test | |-----------|-------------|-----------------| | Compute State Embedding | < 100ms | pytest-benchmark | | FAISS Search | < 50ms | pytest-benchmark | | UI Detection | < 200ms | pytest-benchmark | | Action Execution | < 50ms | pytest-benchmark | | End-to-End Processing | < 400ms | pytest-benchmark | **Outils** : pytest-benchmark, cProfile ### Tests de Bout en Bout **Workflow de Test** : 1. **Apprentissage Complet** : - Capturer 5 sessions d'un workflow simple - Vérifier construction automatique du graphe - Vérifier transition OBSERVATION → COACHING - Exécuter en mode assisté 10 fois - Vérifier transition COACHING → AUTO_CANDIDATE 2. **Robustesse UI** : - Apprendre workflow sur UI version 1 - Modifier légèrement UI (couleurs, positions) - Vérifier que matching fonctionne toujours - Modifier significativement UI - Vérifier détection de changement 3. **Multi-Workflows** : - Apprendre 3 workflows différents - Vérifier isolation des workflows - Vérifier matching correct pour chaque workflow - Vérifier pas de confusion entre workflows ## Correctness Properties *A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* ### Property 1: RawSession Serialization Round Trip *For any* valid RawSession object, serializing to JSON then deserializing should produce an equivalent RawSession with all events and screenshots preserved. **Validates: Requirements 1.4, 1.5** ### Property 2: ScreenState Multi-Level Consistency *For any* ScreenState, all 4 levels (Raw, Perception, Sémantique UI, Contexte) should reference the same screenshot and timestamp. **Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5** ### Property 3: UIElement Detection Confidence Bounds *For any* detected UIElement, the confidence score should be between 0.0 and 1.0, and elements with confidence below threshold should not be included in results. **Validates: Requirements 3.6** ### Property 4: State Embedding Normalization *For any* State Embedding vector, the L2 norm should be 1.0 (normalized vector). **Validates: Requirements 4.6** ### Property 5: State Embedding Similarity Symmetry *For any* two State Embeddings A and B, similarity(A, B) should equal similarity(B, A). **Validates: Requirements 4.7** ### Property 6: State Embedding Similarity Bounds *For any* two State Embeddings, the cosine similarity should be between -1.0 and 1.0. **Validates: Requirements 4.7** ### Property 7: WorkflowNode Matching Consistency *For any* WorkflowNode and ScreenState, if the node matches with confidence C1, then matching again immediately should return the same confidence C1 (deterministic). **Validates: Requirements 9.1, 9.2, 9.3, 9.4, 9.5, 9.6** ### Property 8: WorkflowEdge Pre-Condition Validation *For any* WorkflowEdge, if pre-conditions are not satisfied, execution should not proceed and should return failure. **Validates: Requirements 10.5** ### Property 9: Learning State Monotonic Progression *For any* Workflow in learning state S, transitioning to state S' should only happen if S' is the next state in the progression (OBSERVATION → COACHING → AUTO_CANDIDATE → AUTO_CONFIRMÉ), except for rollback to COACHING. **Validates: Requirements 8.1, 8.2, 8.3, 8.4** ### Property 10: Learning State Rollback Condition *For any* Workflow in AUTO_CONFIRMÉ state, if confidence drops below 0.90, the system should rollback to COACHING state. **Validates: Requirements 8.6** ### Property 11: FAISS Index Consistency *For any* embedding added to FAISS index with metadata M, searching for that exact embedding should return it as the top result with metadata M. **Validates: Requirements 4.8, 12.3, 12.6** ### Property 12: Workflow Graph Structural Validity *For any* Workflow Graph, all edges should reference existing nodes (no dangling references). **Validates: Requirements 7.2** ### Property 13: UIElement Role Uniqueness Per Type *For any* ScreenState, if multiple UIElements have the same role, they should have different element_ids. **Validates: Requirements 3.3** ### Property 14: Embedding Prototype Sample Count *For any* WorkflowNode with embedding prototype, the sample_count should be at least 1 and the prototype vector should exist. **Validates: Requirements 5.4** ### Property 15: Action Execution Timeout *For any* WorkflowEdge execution, if post-conditions are not satisfied within timeout_seconds, the execution should be marked as failed. **Validates: Requirements 10.6** ### Property 16: Pattern Detection Minimum Repetitions *For any* detected workflow pattern, it should have been observed at least 3 times before being proposed as a Workflow Graph. **Validates: Requirements 11.7** ### Property 17: State Embedding Component Weights Sum *For any* weighted fusion configuration, the sum of all component weights (image + text + title + ui) should equal 1.0. **Validates: Requirements 4.5** ### Property 18: Workflow JSON Serialization Round Trip *For any* valid Workflow Graph, serializing to JSON then deserializing should produce an equivalent Workflow with all nodes and edges preserved. **Validates: Requirements 7.6, 12.4, 12.5** ### Property 19: Performance Constraint - State Embedding *For any* ScreenState, computing the State Embedding should complete in less than 100ms. **Validates: Requirements 15.1** ### Property 20: Performance Constraint - End-to-End *For any* ScreenState processing (detection + embedding + matching), the total time should be less than 400ms. **Validates: Requirements 15.5** ## Considérations de Sécurité ### Validation des Données - Tous les JSON chargés doivent être validés contre leur schéma - Les embeddings chargés doivent avoir les dimensions attendues - Les workflow_ids et node_ids doivent être validés (format, unicité) ### Isolation des Workflows - Chaque workflow doit avoir son propre espace dans FAISS - Les embeddings de différents workflows ne doivent pas interférer - Les métadonnées doivent inclure workflow_id pour filtrage ### Safety Rules - Les actions interdites (forbidden_text_clicks, forbidden_roles) doivent être bloquées - Les actions irréversibles doivent demander confirmation - Les rollbacks doivent être possibles pour les 3 dernières actions ### Logging et Audit - Toutes les transitions d'état doivent être loggées - Tous les échecs d'exécution doivent être loggées avec contexte - Les changements d'UI détectés doivent être loggées avec screenshots ## Optimisation des Performances ### Embeddings - Utiliser batch processing pour calculer plusieurs embeddings - Mettre en cache les embeddings de prototypes - Utiliser quantification FP16 pour modèles CLIP ### FAISS - Utiliser index IVF pour grands ensembles (>10k embeddings) - Optimiser périodiquement l'index (compactage) - Utiliser GPU si disponible pour recherche ### UI Detection - Limiter la résolution des screenshots (max 1920x1080) - Utiliser ROI detection pour réduire zone de traitement - Mettre en cache les résultats de détection pour frames similaires ### Workflow Matching - Pré-filtrer les candidats par window context - Utiliser early stopping si confiance très élevée (>0.95) - Mettre en cache le dernier node matché ## Considérations de Déploiement ### Dépendances - Python 3.9+ - PyTorch 2.0+ - OpenCLIP - FAISS (CPU ou GPU) - Transformers (Hugging Face) - NumPy, Pillow - pytest, pytest-benchmark ### Structure de Données ``` data/ ├── sessions/ │ └── YYYY-MM-DD/ │ └── sess_*.json ├── screen_states/ │ └── YYYY-MM-DD/ │ └── screen_*.json ├── embeddings/ │ ├── screens/ │ │ └── *.npy │ ├── elements/ │ │ └── *.npy │ └── states/ │ └── *.npy ├── faiss_index/ │ ├── index.faiss │ └── metadata.json └── workflows/ └── WF_*/ ├── workflow.json └── prototypes/ └── *.npy ``` ### Configuration ```python CONFIG = { "models": { "clip": "ViT-B-32", "vlm": "qwen2.5-vl:3b" }, "embedding": { "dimension": 512, "fusion_method": "weighted", "weights": { "image": 0.5, "text": 0.3, "title": 0.1, "ui": 0.1 } }, "matching": { "min_similarity": 0.85, "faiss_k": 5 }, "learning": { "observation_threshold": 5, "coaching_threshold": 10, "auto_candidate_threshold": 20, "min_success_rate": 0.90, "rollback_confidence": 0.90 }, "performance": { "max_embedding_time_ms": 100, "max_detection_time_ms": 200, "max_total_time_ms": 400 } } ``` ## Plan d'Implémentation ### Phase 1 : Fondations (Semaines 1-2) **Objectif** : Structures de données et sérialisation - Implémenter classes de base (RawSession, ScreenState, UIElement, etc.) - Implémenter sérialisation/désérialisation JSON - Tests unitaires sur structures - Validation de schémas **Livrables** : - `geniusia2/core/models/*.py` - Tests unitaires complets ### Phase 2 : Embeddings et FAISS (Semaines 3-4) **Objectif** : Système d'embeddings fonctionnel - Implémenter FusionEngine - Implémenter FAISSManager - Implémenter calculs de similarité - Tests de performance **Livrables** : - `geniusia2/core/embedding/*.py` - Benchmarks de performance ### Phase 3 : UI Detection (Semaines 5-6) **Objectif** : Détection sémantique d'éléments UI - Intégrer VLM pour détection - Implémenter classification type/rôle - Générer embeddings duaux - Tests avec screenshots réels **Livrables** : - `geniusia2/core/detection/*.py` - Dataset de test avec screenshots ### Phase 4 : Workflow Graph (Semaines 7-9) **Objectif** : Construction et matching de graphes - Implémenter WorkflowNode et WorkflowEdge - Implémenter GraphBuilder - Implémenter NodeMatcher - Tests d'intégration **Livrables** : - `geniusia2/core/graph/*.py` - Tests d'intégration complets ### Phase 5 : Exécution et Apprentissage (Semaines 10-12) **Objectif** : Exécution d'actions et états d'apprentissage - Implémenter ActionExecutor - Implémenter LearningManager - Implémenter transitions d'états - Tests end-to-end **Livrables** : - `geniusia2/core/graph/action_executor.py` - `geniusia2/core/graph/learning_manager.py` - Tests end-to-end ### Phase 6 : Optimisation et Production (Semaines 13-14) **Objectif** : Optimisation et déploiement - Optimiser performances (caching, batching) - Ajouter monitoring et logging - Documentation complète - Tests de charge **Livrables** : - Système optimisé - Documentation utilisateur - Guide de déploiement