From 731b5bcae2ffa8f82808d40eaa4c286e4795ee39 Mon Sep 17 00:00:00 2001 From: Dom Date: Fri, 8 May 2026 14:27:21 +0200 Subject: [PATCH] =?UTF-8?q?fix(replay):=20r=C3=A9activation=20pr=C3=A9-che?= =?UTF-8?q?ck=20OCR=20avec=20calibrage=20chirurgical?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Flag RPA_ENABLE_TEXT_PRECHECK défaut true (vs false pendant prépa démo) - radius_px 200 → 280 (englobe textes longs type "Synthèse Urgences") - min_token_ratio 0.60 → 0.50 (tolère onglets fragmentés par OCR) - Commentaire historique restructuré avec procédure troubleshooting - Docstring synchronisée avec valeur effective Audit complet : docs/AUDIT_CONTROLES_DEBRANCHES_2026-05-08.md Réactive contrôle #3 sur 5 identifiés (les 4 autres restent désactivés pour aujourd'hui — décision chirurgicale 1 par 1). Co-Authored-By: Claude Opus 4.7 (1M context) --- agent_v0/server_v1/api_stream.py | 26 ++++++++++++++++++-------- agent_v0/server_v1/resolve_engine.py | 6 +++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/agent_v0/server_v1/api_stream.py b/agent_v0/server_v1/api_stream.py index 7e48af8f1..9f8894983 100644 --- a/agent_v0/server_v1/api_stream.py +++ b/agent_v0/server_v1/api_stream.py @@ -4509,15 +4509,25 @@ async def resolve_target(request: ResolveTargetRequest): # mais pointant sur un autre élément (ex : clic sur "Dossier en cours" # du menu au lieu de "Synthèse Urgences" du tab plus bas). # - # 8 mai 2026 : désactivé par défaut pour la démo GHT. Calibrage du - # radius_px et min_token_ratio à finaliser post-démo (cf. rapport - # docs/E2E_TEST_RUN_2026-05-08.md). Le pré-check était trop strict - # sur les onglets à 2 tokens (Examens cliniques, Synthèse Urgences) - # → faux rejets → cascade locale Léa V1 → clic au pif. Réactivable - # via env RPA_ENABLE_TEXT_PRECHECK=true. Le code et les tests - # restent en place pour reprise post-démo. + # Pré-check OCR — RÉACTIVÉ le 8 mai 2026 + # Calibrage : radius_px=280, min_token_ratio=0.50 + # Désactivable via RPA_ENABLE_TEXT_PRECHECK=false + # + # Historique : + # - 6-7 mai 2026 : assouplissements progressifs des garde-fous + # (SoM, mémoire visuelle, exemptions drift) pendant prépa démo GHT + # - 8 mai 2026 (matin) : flag défaut "false" posé sur ce pré-check + # pour stabiliser (calibrage trop strict — faux rejets sur + # onglets à 2 tokens : "Examens cliniques", "Synthèse Urgences") + # - 8 mai 2026 (après-midi) : réactivé après calibrage chirurgical + # (radius_px 200→280, min_token_ratio 0.60→0.50) + # + # Si futurs faux rejets observés : + # - vérifier d'abord radius_px (élargir si textes longs coupés) + # - puis min_token_ratio (abaisser si OCR fragmente) + # - NE PAS désactiver sans entrée DECISIONS.md datée _text_precheck_enabled = os.environ.get( - "RPA_ENABLE_TEXT_PRECHECK", "false" + "RPA_ENABLE_TEXT_PRECHECK", "true" ).lower() in ("true", "1", "yes") if _text_precheck_enabled and result and result.get("resolved"): _by_text = (request.target_spec.get("by_text") or "").strip() diff --git a/agent_v0/server_v1/resolve_engine.py b/agent_v0/server_v1/resolve_engine.py index e8cd2507e..a89edc1ca 100644 --- a/agent_v0/server_v1/resolve_engine.py +++ b/agent_v0/server_v1/resolve_engine.py @@ -2243,10 +2243,10 @@ def _validate_text_at_position( expected_text: str, screen_width: int, screen_height: int, - radius_px: int = 200, + radius_px: int = 280, ) -> tuple: """Pré-check sémantique : OCR sur une zone autour de (x_pct, y_pct) et - vérifie que `expected_text` y est présent (substring ou fuzzy 60%). + vérifie que `expected_text` y est présent (substring ou fuzzy 50%). Retourne (is_valid: bool, observed_text: str, elapsed_ms: float). @@ -2282,7 +2282,7 @@ def _validate_text_at_position( results = reader.readtext(np.array(crop)) observed = " ".join(r[1] for r in results if r and len(r) >= 2) elapsed_ms = (time.time() - t0) * 1000 - is_valid = _text_match_fuzzy(expected_text, observed, min_token_ratio=0.60) + is_valid = _text_match_fuzzy(expected_text, observed, min_token_ratio=0.50) return is_valid, observed, elapsed_ms except Exception as e: logger.warning("[REPLAY] _validate_text_at_position erreur (%s) — pas de blocage", e)