feat: premier replay E2E + mode apprentissage supervisé
Premier replay fonctionnel de bout en bout (Bloc-notes, Chrome). Corrections critiques : - Fix double-lancement agent (Lea.bat start /b + verrou PID) - Sérialisation replay (threading.Lock dans poll_and_execute) - Garde UIA bbox >50% écran (rejet conteneurs "Bureau") - Filtre fenêtres bruit système (systray overflow) - Auto-nettoyage replays bloqués (paused_need_help) Cascade visuelle complète dans session_cleaner : - UIA local (10ms) → template matching (100ms) → serveur docTR/VLM - Nettoyage bureau pré-replay (clic "Afficher le bureau") - Crops 80x80 + vlm_description pour chaque clic Grounding contraint à la fenêtre active : - Capture croppée à la fenêtre au lieu de l'écran entier - Conversion coordonnées fenêtre → écran - Élimine les faux positifs taskbar/systray Mode apprentissage supervisé (SUPERVISE → capture humaine) : - Léa passe en mode capture quand elle est perdue - Capture mini-workflow humain (clics + frappes + combos) - Fin par Ctrl+Shift+L ou timeout inactivité 10s - Correction stockée dans target_memory.db via serveur Deploy Windows complet (grounding.py, policy.py, uia_helper.py). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -488,6 +488,8 @@ class ReplayResultReport(BaseModel):
|
||||
# Champs enrichis pour target_not_found (pause supervisée)
|
||||
target_description: Optional[str] = None # Description humaine de la cible
|
||||
target_spec: Optional[Dict[str, Any]] = None # Spec complete de la cible
|
||||
# Correction humaine (mode apprentissage supervisé)
|
||||
correction: Optional[Dict[str, Any]] = None # {x_pct, y_pct, uia_snapshot, crop_b64}
|
||||
|
||||
|
||||
class ErrorCallbackConfig(BaseModel):
|
||||
@@ -1883,6 +1885,26 @@ async def start_raw_replay(request: RawReplayRequest):
|
||||
resolved_machine_id = target_machine_id or (session_obj.machine_id if session_obj else "default")
|
||||
|
||||
with _replay_lock:
|
||||
# ── Nettoyage : annuler les replays bloqués pour cette machine ──
|
||||
# Un replay en paused_need_help bloque tous les suivants.
|
||||
# Quand on lance un nouveau replay, les anciens sont obsolètes.
|
||||
stale_ids = [
|
||||
rid for rid, state in _replay_states.items()
|
||||
if state.get("machine_id") == resolved_machine_id
|
||||
and state["status"] in ("paused_need_help", "running")
|
||||
]
|
||||
for rid in stale_ids:
|
||||
old_state = _replay_states[rid]
|
||||
old_sid = old_state.get("session_id", "")
|
||||
old_state["status"] = "cancelled"
|
||||
# Vider la queue associée
|
||||
if old_sid in _replay_queues:
|
||||
_replay_queues.pop(old_sid, None)
|
||||
logger.info(
|
||||
f"Replay {rid} annulé (remplacé par {replay_id}) — "
|
||||
f"était {old_state.get('completed_actions', 0)}/{old_state.get('total_actions', 0)}"
|
||||
)
|
||||
|
||||
_replay_queues[session_id] = list(actions)
|
||||
_replay_states[replay_id] = _create_replay_state(
|
||||
replay_id=replay_id,
|
||||
@@ -3032,6 +3054,26 @@ async def report_action_result(report: ReplayResultReport):
|
||||
except Exception as e:
|
||||
logger.debug(f"Learning: échec enregistrement: {e}")
|
||||
|
||||
# === Correction humaine (mode apprentissage supervisé) ===
|
||||
# L'humain a montré à Léa où cliquer. On stocke cette correction
|
||||
# dans target_memory pour que la prochaine fois, Léa sache toute seule.
|
||||
if report.correction and original_action:
|
||||
try:
|
||||
corr = report.correction
|
||||
target_spec = original_action.get("target_spec", {})
|
||||
logger.info(
|
||||
f"[APPRENTISSAGE] Correction humaine reçue : "
|
||||
f"({corr.get('x_pct', 0):.4f}, {corr.get('y_pct', 0):.4f}) "
|
||||
f"pour '{target_spec.get('by_text', '?')}'"
|
||||
)
|
||||
_replay_learner.record_human_correction(
|
||||
session_id=session_id,
|
||||
action=original_action,
|
||||
correction=corr,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Learning: échec stockage correction humaine: {e}")
|
||||
|
||||
# === Audit Trail : traçabilité complète pour conformité hospitalière ===
|
||||
try:
|
||||
_action = original_action or {"action_id": action_id, "type": "unknown"}
|
||||
|
||||
Reference in New Issue
Block a user