Files
rpa_vision_v3/core/learning/learning_manager.py
Dom a27b74cf22 v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution
- Frontend v4 accessible sur réseau local (192.168.1.40)
- Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard)
- Ollama GPU fonctionnel
- Self-healing interactif
- Dashboard confiance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:23:51 +01:00

181 lines
7.0 KiB
Python

"""Learning Manager - Manages workflow learning states and transitions"""
import logging
from typing import Dict, Optional, List
from dataclasses import dataclass, field
from datetime import datetime
from ..models.workflow_graph import LearningState, Workflow
logger = logging.getLogger(__name__)
@dataclass
class WorkflowStats:
"""Statistics for a workflow"""
workflow_id: str
learning_state: LearningState
observation_count: int = 0
execution_count: int = 0
success_count: int = 0
failure_count: int = 0
last_execution: Optional[datetime] = None
confidence_scores: List[float] = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.now)
@property
def success_rate(self) -> float:
"""Calculate success rate"""
if self.execution_count == 0:
return 0.0
return self.success_count / self.execution_count
@property
def avg_confidence(self) -> float:
"""Calculate average confidence"""
if not self.confidence_scores:
return 0.0
return sum(self.confidence_scores) / len(self.confidence_scores)
class LearningManager:
"""Manages workflow learning states and transitions"""
def __init__(self):
self.workflows: Dict[str, WorkflowStats] = {}
logger.info("LearningManager initialized")
def register_workflow(self, workflow: Workflow) -> None:
"""Register a new workflow for learning"""
wf_id = workflow.workflow_id
if wf_id not in self.workflows:
self.workflows[wf_id] = WorkflowStats(
workflow_id=wf_id,
learning_state=workflow.learning_state
)
logger.info(f"Registered workflow: {wf_id} (state={workflow.learning_state})")
def record_observation(self, workflow_id: str) -> None:
"""Record an observation of the workflow"""
if workflow_id not in self.workflows:
logger.warning(f"Unknown workflow: {workflow_id}")
return
stats = self.workflows[workflow_id]
stats.observation_count += 1
logger.debug(f"Observation recorded for {workflow_id} (count={stats.observation_count})")
self._check_state_transition(workflow_id)
def record_execution(self, workflow_id: str, success: bool, confidence: float) -> None:
"""Record an execution result"""
if workflow_id not in self.workflows:
logger.warning(f"Unknown workflow: {workflow_id}")
return
stats = self.workflows[workflow_id]
stats.execution_count += 1
stats.last_execution = datetime.now()
stats.confidence_scores.append(confidence)
if success:
stats.success_count += 1
else:
stats.failure_count += 1
logger.info(
f"Execution recorded for {workflow_id}: "
f"success={success}, confidence={confidence:.2f}, "
f"success_rate={stats.success_rate:.2f}"
)
self._check_state_transition(workflow_id)
def _check_state_transition(self, workflow_id: str) -> None:
"""Check if workflow should transition to next learning state"""
stats = self.workflows[workflow_id]
current_state = stats.learning_state
new_state = None
reason = ""
if current_state == LearningState.OBSERVATION:
if self._can_transition_to_coaching(stats):
new_state = LearningState.COACHING
reason = f"5+ observations ({stats.observation_count}), avg confidence > 0.90"
elif current_state == LearningState.COACHING:
if self._can_transition_to_auto_candidate(stats):
new_state = LearningState.AUTO_CANDIDATE
reason = f"10+ assists ({stats.execution_count}), success rate > 0.90"
elif current_state == LearningState.AUTO_CANDIDATE:
if self._can_transition_to_auto_confirmed(stats):
new_state = LearningState.AUTO_CONFIRMED
reason = f"20+ executions ({stats.execution_count}), success rate > 0.95"
elif current_state == LearningState.AUTO_CONFIRMED:
if self._should_rollback(stats):
new_state = LearningState.COACHING
reason = f"Confidence dropped below 0.90 (avg={stats.avg_confidence:.2f})"
if new_state:
self._transition_state(workflow_id, new_state, reason)
def _can_transition_to_coaching(self, stats: WorkflowStats) -> bool:
"""Check if can transition from OBSERVATION to COACHING"""
return (
stats.observation_count >= 5 and
stats.avg_confidence >= 0.90
)
def _can_transition_to_auto_candidate(self, stats: WorkflowStats) -> bool:
"""Check if can transition from COACHING to AUTO_CANDIDATE"""
return (
stats.execution_count >= 10 and
stats.success_rate >= 0.90
)
def _can_transition_to_auto_confirmed(self, stats: WorkflowStats) -> bool:
"""Check if can transition from AUTO_CANDIDATE to AUTO_CONFIRMED"""
return (
stats.execution_count >= 20 and
stats.success_rate >= 0.95
)
def _should_rollback(self, stats: WorkflowStats) -> bool:
"""Check if should rollback from AUTO_CONFIRMED to COACHING"""
recent_scores = stats.confidence_scores[-10:] if len(stats.confidence_scores) >= 10 else stats.confidence_scores
if not recent_scores:
return False
recent_avg = sum(recent_scores) / len(recent_scores)
return recent_avg < 0.90
def _transition_state(self, workflow_id: str, new_state: LearningState, reason: str) -> None:
"""Transition workflow to new learning state"""
stats = self.workflows[workflow_id]
old_state = stats.learning_state
stats.learning_state = new_state
logger.info(
f"State transition for {workflow_id}: "
f"{old_state.value}{new_state.value} "
f"(reason: {reason})"
)
def get_workflow_state(self, workflow_id: str) -> Optional[LearningState]:
"""Get current learning state of workflow"""
if workflow_id in self.workflows:
return self.workflows[workflow_id].learning_state
return None
def get_workflow_stats(self, workflow_id: str) -> Optional[WorkflowStats]:
"""Get statistics for workflow"""
return self.workflows.get(workflow_id)
def should_execute_automatically(self, workflow_id: str) -> bool:
"""Check if workflow should execute automatically"""
state = self.get_workflow_state(workflow_id)
return state in [LearningState.AUTO_CANDIDATE, LearningState.AUTO_CONFIRMED]
def should_ask_confirmation(self, workflow_id: str) -> bool:
"""Check if should ask user confirmation before execution"""
state = self.get_workflow_state(workflow_id)
return state == LearningState.COACHING