Files
Geniusia_v2/.kiro/specs/workflow-graph-implementation/design.md
2026-03-05 00:20:25 +01:00

1420 lines
44 KiB
Markdown

# 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