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>
646 lines
25 KiB
Markdown
646 lines
25 KiB
Markdown
# Design Document - Corrections Critiques RPA Vision V3
|
|
|
|
## Vue d'Ensemble
|
|
|
|
Ce document spécifie le design pour corriger les problèmes critiques identifiés dans RPA Vision V3. Les corrections visent à établir l'exécution automatique des workflows, la stabilité en production, et des performances acceptables tout en préservant la qualité et la logique des fonctionnalités existantes.
|
|
|
|
## Architecture
|
|
|
|
L'architecture des corrections suit une approche de **refactoring progressif** qui préserve les fonctionnalités existantes tout en corrigeant les problèmes structurels :
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Architecture des Corrections Critiques │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
|
|
│ │ WorkflowPipeline│ │ ActionExecutor │ │ TargetResolver│ │
|
|
│ │ Enhanced │ │ Integration │ │ Optimized │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ • execute_ │◄──►│ • Unified Error │◄──►│ • Spatial │ │
|
|
│ │ workflow_step │ │ Handling │ │ Index │ │
|
|
│ │ • Error Recovery│ │ • Target │ │ Cache │ │
|
|
│ │ • State Mgmt │ │ Resolution │ │ • Memory │ │
|
|
│ └─────────────────┘ └─────────────────┘ │ Mgmt │ │
|
|
│ │ │ └─────────────┘ │
|
|
│ │ │ │ │
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
|
|
│ │ ErrorHandler │ │ DataContracts │ │ ConfigMgr │ │
|
|
│ │ Centralized │ │ Standardized │ │ Centralized │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ • Recovery │ │ • BBox (x,y,w,h)│ │ • Validation│ │
|
|
│ │ Strategies │ │ • datetime │ │ • Security │ │
|
|
│ │ • Escalation │ │ • string IDs │ │ • Env Adapt │ │
|
|
│ │ • Audit Trail │ │ • Pydantic │ │ │ │
|
|
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Composants et Interfaces
|
|
|
|
### 1. Intégration WorkflowPipeline-ActionExecutor
|
|
|
|
**Problème :** WorkflowPipeline ne utilise pas ActionExecutor pour l'exécution automatique.
|
|
|
|
**Solution :** Extension de WorkflowPipeline avec une méthode d'exécution intégrée :
|
|
|
|
```python
|
|
class WorkflowPipeline:
|
|
def __init__(self, target_resolver: TargetResolver,
|
|
action_executor: ActionExecutor,
|
|
error_handler: ErrorHandler):
|
|
self.target_resolver = target_resolver
|
|
self.action_executor = action_executor
|
|
self.error_handler = error_handler
|
|
|
|
def execute_workflow_step(self, workflow_id: str,
|
|
current_state: ScreenState) -> ExecutionResult:
|
|
"""
|
|
Exécute une étape de workflow de bout en bout
|
|
|
|
Processus:
|
|
1. Matcher l'état actuel avec le workflow
|
|
2. Obtenir la prochaine action à exécuter
|
|
3. Résoudre la cible avec TargetResolver
|
|
4. Exécuter l'action avec ActionExecutor
|
|
5. Gérer les erreurs avec ErrorHandler
|
|
6. Retourner le résultat détaillé
|
|
"""
|
|
try:
|
|
# 1. Match current state
|
|
match_result = self.match_current_state(current_state)
|
|
if not match_result:
|
|
return ExecutionResult.no_match()
|
|
|
|
# 2. Get next action
|
|
action = self.get_next_action(workflow_id, match_result.node_id)
|
|
if not action:
|
|
return ExecutionResult.workflow_complete()
|
|
|
|
# 3. Execute with integrated error handling
|
|
result = self.action_executor.execute_action(action, current_state)
|
|
|
|
return ExecutionResult.success(result, match_result)
|
|
|
|
except Exception as e:
|
|
recovery = self.error_handler.handle_error(e, {
|
|
'workflow_id': workflow_id,
|
|
'current_state': current_state,
|
|
'action': action if 'action' in locals() else None
|
|
})
|
|
|
|
if recovery.should_retry:
|
|
return self.execute_workflow_step(workflow_id, current_state)
|
|
else:
|
|
return ExecutionResult.error(e, recovery)
|
|
```
|
|
|
|
### 2. Gestion d'Erreurs Unifiée
|
|
|
|
**Problème :** Gestion d'erreurs fragmentée, ErrorHandler non utilisé partout.
|
|
|
|
**Solution :** Centralisation avec ErrorHandler et stratégies de récupération :
|
|
|
|
```python
|
|
class ErrorHandler:
|
|
def __init__(self):
|
|
self.recovery_strategies = {
|
|
TargetNotFoundError: SpatialFallbackStrategy(),
|
|
UIElementChangedError: SemanticVariantStrategy(),
|
|
NetworkError: RetryWithBackoffStrategy(),
|
|
ValidationError: DataNormalizationStrategy()
|
|
}
|
|
|
|
def handle_error(self, error: Exception, context: Dict[str, Any]) -> RecoveryResult:
|
|
"""
|
|
Gère une erreur avec stratégie de récupération appropriée
|
|
|
|
Processus:
|
|
1. Identifier le type d'erreur
|
|
2. Sélectionner la stratégie de récupération
|
|
3. Appliquer la stratégie avec le contexte
|
|
4. Logger la tentative de récupération
|
|
5. Retourner le résultat de récupération
|
|
"""
|
|
error_type = type(error)
|
|
strategy = self.recovery_strategies.get(error_type)
|
|
|
|
if not strategy:
|
|
return RecoveryResult.escalate(error, "No recovery strategy available")
|
|
|
|
try:
|
|
recovery = strategy.recover(error, context)
|
|
self._log_recovery_attempt(error, strategy, recovery, context)
|
|
return recovery
|
|
|
|
except Exception as recovery_error:
|
|
return RecoveryResult.escalate(
|
|
error,
|
|
f"Recovery failed: {recovery_error}",
|
|
original_error=error
|
|
)
|
|
|
|
# Pattern d'utilisation dans tous les composants
|
|
def risky_operation():
|
|
try:
|
|
result = perform_operation()
|
|
return result
|
|
except Exception as e:
|
|
recovery = error_handler.handle_error(e, get_context())
|
|
if recovery.should_retry:
|
|
return risky_operation() # Retry with recovery
|
|
else:
|
|
raise ProcessingError(f"Operation failed after recovery: {e}")
|
|
```
|
|
|
|
### 3. Résolution des Imports Circulaires
|
|
|
|
**Problème :** Structure d'imports complexe avec risques de circularité.
|
|
|
|
**Solution :** Refactoring avec imports lazy et interfaces abstraites :
|
|
|
|
```python
|
|
# core/models/__init__.py - Version corrigée
|
|
from typing import TYPE_CHECKING
|
|
|
|
# Imports directs pour les types de base
|
|
from .base_models import BaseModel, BBox, Timestamp
|
|
from .ui_element import UIElement
|
|
from .screen_state import ScreenState
|
|
|
|
# Imports lazy pour éviter les cycles
|
|
if TYPE_CHECKING:
|
|
from .workflow_graph import WorkflowGraph, WorkflowNode
|
|
from .resolved_target import ResolvedTarget
|
|
|
|
def get_workflow_graph():
|
|
"""Lazy import pour WorkflowGraph"""
|
|
from .workflow_graph import WorkflowGraph
|
|
return WorkflowGraph
|
|
|
|
def get_resolved_target():
|
|
"""Lazy import pour ResolvedTarget"""
|
|
from .resolved_target import ResolvedTarget
|
|
return ResolvedTarget
|
|
|
|
# Interfaces abstraites pour découpler les composants
|
|
from abc import ABC, abstractmethod
|
|
|
|
class ITargetResolver(ABC):
|
|
@abstractmethod
|
|
def resolve_target(self, target_spec: 'TargetSpec',
|
|
ui_elements: List['UIElement']) -> Optional['ResolvedTarget']:
|
|
pass
|
|
|
|
class IActionExecutor(ABC):
|
|
@abstractmethod
|
|
def execute_action(self, action: 'Action',
|
|
screen_state: 'ScreenState') -> 'ExecutionResult':
|
|
pass
|
|
```
|
|
|
|
### 4. Standardisation des Contrats de Données
|
|
|
|
**Problème :** Formats de données incohérents entre couches.
|
|
|
|
**Solution :** Standardisation avec Pydantic et utilitaires de conversion :
|
|
|
|
```python
|
|
from pydantic import BaseModel, validator, Field
|
|
from typing import Tuple, Union
|
|
from datetime import datetime
|
|
|
|
class BBox(BaseModel):
|
|
"""Bounding box standardisée au format (x, y, width, height)"""
|
|
x: int = Field(..., ge=0, description="Position X (coin supérieur gauche)")
|
|
y: int = Field(..., ge=0, description="Position Y (coin supérieur gauche)")
|
|
width: int = Field(..., gt=0, description="Largeur")
|
|
height: int = Field(..., gt=0, description="Hauteur")
|
|
|
|
@validator('*', pre=True)
|
|
def validate_positive(cls, v):
|
|
if isinstance(v, (int, float)) and v < 0:
|
|
raise ValueError("Coordinates must be non-negative")
|
|
return int(v)
|
|
|
|
def to_tuple(self) -> Tuple[int, int, int, int]:
|
|
"""Conversion vers tuple (x, y, w, h)"""
|
|
return (self.x, self.y, self.width, self.height)
|
|
|
|
@classmethod
|
|
def from_xyxy(cls, x1: int, y1: int, x2: int, y2: int) -> 'BBox':
|
|
"""Conversion depuis format (x1, y1, x2, y2)"""
|
|
return cls(
|
|
x=min(x1, x2),
|
|
y=min(y1, y2),
|
|
width=abs(x2 - x1),
|
|
height=abs(y2 - y1)
|
|
)
|
|
|
|
class UIElement(BaseModel):
|
|
"""Élément UI avec contrats de données standardisés"""
|
|
element_id: str = Field(..., description="ID unique de l'élément")
|
|
bbox: BBox = Field(..., description="Boîte englobante")
|
|
timestamp: datetime = Field(default_factory=datetime.now, description="Timestamp de création")
|
|
confidence: float = Field(default=1.0, ge=0.0, le=1.0, description="Confiance de détection")
|
|
|
|
@validator('element_id')
|
|
def validate_element_id(cls, v):
|
|
if not isinstance(v, str) or not v.strip():
|
|
raise ValueError("element_id must be a non-empty string")
|
|
return v.strip()
|
|
|
|
# Utilitaires de conversion sûrs
|
|
class DataConverter:
|
|
@staticmethod
|
|
def ensure_bbox_xywh(bbox: Union[BBox, Tuple, List, Dict]) -> BBox:
|
|
"""Assure que bbox est au format (x, y, w, h)"""
|
|
if isinstance(bbox, BBox):
|
|
return bbox
|
|
elif isinstance(bbox, (tuple, list)) and len(bbox) == 4:
|
|
return BBox(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3])
|
|
elif isinstance(bbox, dict):
|
|
return BBox(**bbox)
|
|
else:
|
|
raise ValueError(f"Cannot convert {type(bbox)} to BBox")
|
|
|
|
@staticmethod
|
|
def ensure_datetime(timestamp: Union[datetime, str, int, float]) -> datetime:
|
|
"""Assure que timestamp est un objet datetime"""
|
|
if isinstance(timestamp, datetime):
|
|
return timestamp
|
|
elif isinstance(timestamp, str):
|
|
return datetime.fromisoformat(timestamp)
|
|
elif isinstance(timestamp, (int, float)):
|
|
return datetime.fromtimestamp(timestamp)
|
|
else:
|
|
raise ValueError(f"Cannot convert {type(timestamp)} to datetime")
|
|
```
|
|
|
|
### 5. Optimisation des Performances
|
|
|
|
**Problème :** Goulots d'étranglement dans TargetResolver et gestion des embeddings.
|
|
|
|
**Solution :** Cache intelligent et réutilisation des ressources :
|
|
|
|
```python
|
|
from functools import lru_cache
|
|
from typing import Optional, Dict, Any
|
|
import weakref
|
|
|
|
class OptimizedTargetResolver:
|
|
def __init__(self):
|
|
self._spatial_index_cache: Dict[str, Any] = {}
|
|
self._embedding_cache = weakref.WeakValueDictionary()
|
|
self._model_cache: Dict[str, Any] = {}
|
|
|
|
@lru_cache(maxsize=100)
|
|
def get_spatial_index(self, screen_signature: str) -> 'SpatialIndex':
|
|
"""Cache de l'index spatial par signature d'écran"""
|
|
if screen_signature not in self._spatial_index_cache:
|
|
self._spatial_index_cache[screen_signature] = self._build_spatial_index()
|
|
return self._spatial_index_cache[screen_signature]
|
|
|
|
def get_embedding(self, element_id: str, force_reload: bool = False) -> Optional[np.ndarray]:
|
|
"""Lazy loading des embeddings avec cache"""
|
|
if not force_reload and element_id in self._embedding_cache:
|
|
return self._embedding_cache[element_id]
|
|
|
|
embedding = self._load_embedding_from_disk(element_id)
|
|
if embedding is not None:
|
|
self._embedding_cache[element_id] = embedding
|
|
return embedding
|
|
|
|
def get_model(self, model_name: str) -> Any:
|
|
"""Cache des modèles ML chargés"""
|
|
if model_name not in self._model_cache:
|
|
self._model_cache[model_name] = self._load_model(model_name)
|
|
return self._model_cache[model_name]
|
|
|
|
def resolve_target(self, target_spec: TargetSpec,
|
|
ui_elements: List[UIElement]) -> Optional[ResolvedTarget]:
|
|
"""Résolution optimisée avec réutilisation des ressources"""
|
|
# Réutiliser l'index spatial
|
|
screen_sig = self._compute_screen_signature(ui_elements)
|
|
spatial_index = self.get_spatial_index(screen_sig)
|
|
|
|
# Éviter les calculs redondants
|
|
cache_key = self._compute_resolution_cache_key(target_spec, screen_sig)
|
|
if cache_key in self._resolution_cache:
|
|
return self._resolution_cache[cache_key]
|
|
|
|
# Résolution normale avec cache
|
|
result = self._resolve_with_cache(target_spec, ui_elements, spatial_index)
|
|
self._resolution_cache[cache_key] = result
|
|
return result
|
|
```
|
|
|
|
### 6. Gestion Mémoire Robuste
|
|
|
|
**Problème :** Fuites mémoire et consommation croissante.
|
|
|
|
**Solution :** Nettoyage automatique et limites effectives :
|
|
|
|
```python
|
|
import gc
|
|
import psutil
|
|
from threading import Timer
|
|
from typing import Dict, Any
|
|
|
|
class MemoryManager:
|
|
def __init__(self, max_memory_mb: int = 1024):
|
|
self.max_memory_mb = max_memory_mb
|
|
self.cleanup_timer: Optional[Timer] = None
|
|
self.resource_registry: Dict[str, Any] = {}
|
|
|
|
def register_resource(self, resource_id: str, resource: Any,
|
|
cleanup_func: callable = None):
|
|
"""Enregistre une ressource pour nettoyage automatique"""
|
|
self.resource_registry[resource_id] = {
|
|
'resource': resource,
|
|
'cleanup_func': cleanup_func or (lambda x: None),
|
|
'created_at': datetime.now()
|
|
}
|
|
|
|
def check_memory_usage(self) -> float:
|
|
"""Vérifie l'utilisation mémoire actuelle"""
|
|
process = psutil.Process()
|
|
memory_mb = process.memory_info().rss / 1024 / 1024
|
|
return memory_mb
|
|
|
|
def cleanup_if_needed(self):
|
|
"""Nettoyage préventif si nécessaire"""
|
|
current_memory = self.check_memory_usage()
|
|
|
|
if current_memory > self.max_memory_mb * 0.8: # 80% threshold
|
|
self._perform_cleanup()
|
|
|
|
def _perform_cleanup(self):
|
|
"""Effectue le nettoyage des ressources"""
|
|
# 1. Nettoyer les embeddings anciens
|
|
self._cleanup_old_embeddings()
|
|
|
|
# 2. Libérer les ressources GPU
|
|
self._cleanup_gpu_resources()
|
|
|
|
# 3. Vider les caches LRU
|
|
self._clear_lru_caches()
|
|
|
|
# 4. Force garbage collection
|
|
gc.collect()
|
|
|
|
def _cleanup_old_embeddings(self):
|
|
"""Nettoie les embeddings anciens"""
|
|
cutoff_time = datetime.now() - timedelta(hours=1)
|
|
to_remove = []
|
|
|
|
for resource_id, info in self.resource_registry.items():
|
|
if (info['created_at'] < cutoff_time and
|
|
'embedding' in resource_id.lower()):
|
|
info['cleanup_func'](info['resource'])
|
|
to_remove.append(resource_id)
|
|
|
|
for resource_id in to_remove:
|
|
del self.resource_registry[resource_id]
|
|
|
|
def shutdown(self):
|
|
"""Nettoyage complet à l'arrêt"""
|
|
for info in self.resource_registry.values():
|
|
try:
|
|
info['cleanup_func'](info['resource'])
|
|
except Exception as e:
|
|
logger.warning(f"Error during resource cleanup: {e}")
|
|
|
|
self.resource_registry.clear()
|
|
gc.collect()
|
|
|
|
# Cache LRU avec limite effective
|
|
class EffectiveLRUCache:
|
|
def __init__(self, maxsize: int = 128, memory_limit_mb: int = 100):
|
|
self.maxsize = maxsize
|
|
self.memory_limit_mb = memory_limit_mb
|
|
self.cache: Dict[Any, Any] = {}
|
|
self.access_order: List[Any] = []
|
|
|
|
def get(self, key: Any) -> Any:
|
|
if key in self.cache:
|
|
self.access_order.remove(key)
|
|
self.access_order.append(key)
|
|
return self.cache[key]
|
|
return None
|
|
|
|
def put(self, key: Any, value: Any):
|
|
# Vérifier les limites avant d'ajouter
|
|
self._enforce_limits()
|
|
|
|
if key in self.cache:
|
|
self.access_order.remove(key)
|
|
elif len(self.cache) >= self.maxsize:
|
|
self._evict_lru()
|
|
|
|
self.cache[key] = value
|
|
self.access_order.append(key)
|
|
|
|
def _enforce_limits(self):
|
|
"""Applique les limites de taille et mémoire"""
|
|
current_memory = self._estimate_memory_usage()
|
|
|
|
while (len(self.cache) >= self.maxsize or
|
|
current_memory > self.memory_limit_mb):
|
|
if not self.access_order:
|
|
break
|
|
self._evict_lru()
|
|
current_memory = self._estimate_memory_usage()
|
|
```
|
|
|
|
## Modèles de Données
|
|
|
|
### ExecutionResult Étendu
|
|
|
|
```python
|
|
@dataclass
|
|
class ExecutionResult:
|
|
"""Résultat d'exécution avec métadonnées complètes"""
|
|
success: bool
|
|
action_executed: Optional[str] = None
|
|
target_resolved: Optional[ResolvedTarget] = None
|
|
error: Optional[Exception] = None
|
|
recovery_applied: Optional[RecoveryResult] = None
|
|
performance_metrics: Dict[str, float] = field(default_factory=dict)
|
|
correlation_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
|
|
@classmethod
|
|
def success(cls, result: Any, match_result: Any) -> 'ExecutionResult':
|
|
return cls(
|
|
success=True,
|
|
action_executed=str(result.action),
|
|
target_resolved=result.target,
|
|
performance_metrics=result.metrics
|
|
)
|
|
|
|
@classmethod
|
|
def error(cls, error: Exception, recovery: RecoveryResult) -> 'ExecutionResult':
|
|
return cls(
|
|
success=False,
|
|
error=error,
|
|
recovery_applied=recovery
|
|
)
|
|
|
|
@dataclass
|
|
class RecoveryResult:
|
|
"""Résultat d'une tentative de récupération"""
|
|
should_retry: bool
|
|
strategy_used: str
|
|
recovery_data: Dict[str, Any] = field(default_factory=dict)
|
|
escalation_reason: Optional[str] = None
|
|
|
|
@classmethod
|
|
def retry(cls, strategy: str, data: Dict[str, Any] = None) -> 'RecoveryResult':
|
|
return cls(should_retry=True, strategy_used=strategy, recovery_data=data or {})
|
|
|
|
@classmethod
|
|
def escalate(cls, error: Exception, reason: str, **kwargs) -> 'RecoveryResult':
|
|
return cls(
|
|
should_retry=False,
|
|
strategy_used="escalation",
|
|
escalation_reason=reason,
|
|
recovery_data=kwargs
|
|
)
|
|
```
|
|
|
|
## Propriétés de Correction
|
|
|
|
*Une propriété est une caractéristique ou un comportement qui doit être vrai dans toutes les exécutions valides d'un système - essentiellement, une déclaration formelle sur ce que le système doit faire. Les propriétés servent de pont entre les spécifications lisibles par l'homme et les garanties de correction vérifiables par machine.*
|
|
|
|
### Propriété 1: Intégration WorkflowPipeline-ActionExecutor
|
|
*Pour tout* workflow traité par WorkflowPipeline, ActionExecutor doit être utilisé pour l'exécution des actions
|
|
**Valide: Exigences 1.1, 1.2**
|
|
|
|
### Propriété 2: Gestion d'erreurs unifiée
|
|
*Pour toute* erreur survenant dans le système, ErrorHandler doit être utilisé pour appliquer une stratégie de récupération appropriée
|
|
**Valide: Exigences 2.1, 2.2**
|
|
|
|
### Propriété 3: Absence d'imports circulaires
|
|
*Pour tout* module du système, l'importation ne doit pas créer de dépendances cycliques
|
|
**Valide: Exigences 3.1, 3.3**
|
|
|
|
### Propriété 4: Cohérence des contrats de données BBox
|
|
*Pour toute* coordonnée BBox utilisée dans le système, le format doit être exclusivement (x,y,w,h)
|
|
**Valide: Exigences 4.1**
|
|
|
|
### Propriété 5: Cohérence des timestamps
|
|
*Pour tout* timestamp manipulé dans le système, il doit être un objet datetime
|
|
**Valide: Exigences 4.2**
|
|
|
|
### Propriété 6: Réutilisation de l'index spatial
|
|
*Pour toute* résolution de TargetResolver avec la même signature d'écran, l'index spatial doit être réutilisé
|
|
**Valide: Exigences 5.1**
|
|
|
|
### Propriété 7: Limites effectives des caches LRU
|
|
*Pour tout* cache LRU utilisé, les limites de taille configurées doivent être respectées
|
|
**Valide: Exigences 6.1**
|
|
|
|
### Propriété 8: Libération des ressources GPU
|
|
*Pour toute* ressource GPU allouée, elle doit être libérée après utilisation
|
|
**Valide: Exigences 6.3**
|
|
|
|
### Propriété 9: Validation des inputs de sécurité
|
|
*Pour tout* input utilisateur reçu, il doit être validé contre les injections malveillantes
|
|
**Valide: Exigences 7.2**
|
|
|
|
### Propriété 10: Interfaces abstraites pour le découplage
|
|
*Pour toute* interaction entre composants, elle doit passer par des interfaces abstraites
|
|
**Valide: Exigences 8.1**
|
|
|
|
### Propriété 11: Correlation IDs uniques
|
|
*Pour toute* opération effectuée, un correlation ID unique doit être assigné
|
|
**Valide: Exigences 9.1**
|
|
|
|
### Propriété 12: Centralisation des constantes
|
|
*Pour toute* constante nécessaire au système, elle doit être centralisée dans core/config.py
|
|
**Valide: Exigences 10.1**
|
|
|
|
## Gestion d'Erreurs
|
|
|
|
### Stratégies de Récupération
|
|
|
|
1. **SpatialFallbackStrategy** : Utilise des critères spatiaux alternatifs
|
|
2. **SemanticVariantStrategy** : Essaie des variantes sémantiques du texte
|
|
3. **RetryWithBackoffStrategy** : Retry avec délai exponentiel
|
|
4. **DataNormalizationStrategy** : Normalise et convertit les données
|
|
5. **GracefulDegradationStrategy** : Continue avec fonctionnalité réduite
|
|
|
|
### Escalade d'Erreurs
|
|
|
|
```python
|
|
class ErrorEscalation:
|
|
def escalate_error(self, error: Exception, context: Dict[str, Any],
|
|
recovery_attempts: List[RecoveryResult]) -> None:
|
|
"""
|
|
Escalade une erreur après échec des récupérations
|
|
|
|
Processus:
|
|
1. Collecter le contexte complet
|
|
2. Documenter les tentatives de récupération
|
|
3. Créer un rapport d'erreur détaillé
|
|
4. Notifier les systèmes de monitoring
|
|
5. Préserver l'état pour diagnostic
|
|
"""
|
|
```
|
|
|
|
## Stratégie de Test
|
|
|
|
### Tests Unitaires
|
|
|
|
- Test d'intégration WorkflowPipeline-ActionExecutor
|
|
- Test des stratégies de récupération d'erreurs
|
|
- Test de résolution des imports circulaires
|
|
- Test de conversion des contrats de données
|
|
- Test des optimisations de performance
|
|
- Test de gestion mémoire
|
|
|
|
### Tests de Propriétés
|
|
|
|
Utilisation du framework Hypothesis avec 100+ itérations par propriété :
|
|
|
|
- **Propriété 1** : Intégration WorkflowPipeline-ActionExecutor
|
|
- **Propriété 2** : Gestion d'erreurs unifiée
|
|
- **Propriété 3** : Absence d'imports circulaires
|
|
- **Propriété 4** : Cohérence des contrats de données BBox
|
|
- **Propriété 5** : Cohérence des timestamps
|
|
- **Propriété 6** : Réutilisation de l'index spatial
|
|
- **Propriété 7** : Limites effectives des caches LRU
|
|
- **Propriété 8** : Libération des ressources GPU
|
|
- **Propriété 9** : Validation des inputs de sécurité
|
|
- **Propriété 10** : Interfaces abstraites pour le découplage
|
|
- **Propriété 11** : Correlation IDs uniques
|
|
- **Propriété 12** : Centralisation des constantes
|
|
|
|
### Tests d'Intégration
|
|
|
|
- Exécution de bout en bout des workflows
|
|
- Test de charge avec gestion mémoire
|
|
- Test de sécurité en environnement de production
|
|
- Test de performance avec métriques détaillées
|
|
- Test de récupération d'erreurs en conditions réelles
|
|
|
|
### Validation de Non-Régression
|
|
|
|
**Principe Critique :** Chaque correction doit préserver la qualité et la logique des fonctionnalités existantes.
|
|
|
|
```python
|
|
class RegressionValidator:
|
|
def validate_no_regression(self, before_state: SystemState,
|
|
after_state: SystemState) -> ValidationResult:
|
|
"""
|
|
Valide qu'aucune régression n'a été introduite
|
|
|
|
Vérifications:
|
|
1. Toutes les fonctionnalités existantes fonctionnent
|
|
2. Les performances ne se dégradent pas
|
|
3. Les contrats d'API sont préservés
|
|
4. Les tests existants continuent de passer
|
|
5. La qualité du code est maintenue ou améliorée
|
|
"""
|
|
``` |