Files
rpa_vision_v3/docs/PLAN_APPRENTISSAGE_LEA.md
Dom b92cb9db03 feat: Phase 1 apprentissage — greffe TargetMemoryStore sur V4
Greffe minimale du mécanisme d'apprentissage persistant (Fiche #18,
target_memory_store.py) sur le pipeline streaming V4 sans toucher à V3.

Architecture (docs/PLAN_APPRENTISSAGE_LEA.md) :
- Lookup mémoire AVANT la cascade résolution coûteuse OCR/template/VLM
  dans _resolve_target_sync → hit = <10ms, miss = overhead zéro
- Record APRÈS validation post-condition (title_match strict)
  dans /replay/result → 2 succès → cristallisation par répétition
- Single source of truth : l'agent remplit report.actual_position avec
  les coords effectivement cliquées, le serveur les lit directement.
  Pas de cache intermédiaire (option C du plan).

Signature écran V4 : sha256(normalize(window_title))[:16]. Robuste aux
données variables, faux positifs rattrapés par le post-cond qui
décrémente la fiabilité via record_failure().

Fichiers :
- agent_v0/server_v1/replay_memory.py : nouveau wrapper 316 lignes
  exposant compute_screen_sig/memory_lookup/record_success/failure,
  lazy-init du store, normalisation texte stable, garde sanity coords
- agent_v0/server_v1/resolve_engine.py : lookup mémoire en tête de
  _resolve_target_sync (30 lignes)
- agent_v0/server_v1/replay_engine.py : _create_replay_state stocke
  une copie slim des actions (sans anchor base64) pour retrouver le
  target_spec par current_action_index
- agent_v0/server_v1/api_stream.py : 4 callers passent actions=...,
  record success/failure dans /replay/result lit actual_position
  du rapport (click-only), correction du commentaire Pydantic
- agent_v0/agent_v1/core/executor.py : remplit result["actual_position"]
  après self._click(), transmis dans le report de poll_and_execute

Tests : 56 E2E + Phase0 passent, zéro régression. Cycle Phase 1 validé
en simulation : miss → record → miss → record → HIT au 3ème passage.

Le deploy copy executor.py a une divergence pré-existante de 1302
lignes non committées — traité séparément lors du cleanup prochain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 21:08:14 +02:00

11 KiB

Plan Apprentissage Léa — Phase 1 / 2 / 3

Date : 10 avril 2026 Auteur : Dom + Claude (session cartographie target_resolver) Statut : Plan validé par Dom, implémentation non commencée


Contexte

Après deux semaines à debugger le replay sur Windows et avoir écrit du code (V4 : surface_classifier, UIA, execution_plan, executor strict) qui dupliquait sans le savoir des concepts déjà présents dans le V3 legacy, une cartographie exhaustive a été lancée.

Fichiers lus en profondeur :

  • core/execution/target_resolver.py (3495 lignes)
  • core/learning/target_memory_store.py (545 lignes — Fiche #18)
  • core/models/workflow_graph.py (TargetSpec — 570-640)
  • core/detection/spatial_analyzer.py (595 lignes)

Découverte critique

Les pipelines V3 et V4 sont complètement découplés au runtime de replay.

REPLAY V4 (actif aujourd'hui)           LEGACY V3 (dormant au replay)
=============================           =============================
stream_processor                        workflow_pipeline
  ↓                                       ↓
execution_plan_runner                   execution_loop
  ↓                                       ↓
agent_v1/core/executor.py               action_executor
  ↓                                       ↓
OCR + template + VLM direct             target_resolver
                                          ↓
                                        target_memory_store (Fiche #18)
                                          ↓
                                        SpatialAnalyzer

Vérifié par grep "from core.execution" agent_v0/zéro import.

Callers V3 encore vivants (mais pas sur le chemin de replay critique) :

  • agent_chat/app.py
  • visual_workflow_builder/backend/api/workflows.py
  • core/evaluation/*

Modules dormants à valeur immédiate

TargetMemoryStore — le Crystallizer qu'on pensait devoir écrire

  • SQLite data/learning/target_memory.db + JSONL audit data/learning/events/YYYY-MM-DD/*.jsonl
  • API propre et testée :
    • record_success(screen_sig, target_spec, fingerprint, strategy, confidence)
    • record_failure(screen_sig, target_spec, error)
    • lookup(screen_sig, target_spec, min_success_count=2, max_fail_ratio=0.3) → fingerprint ou None
  • Clé unique : (screen_signature, target_spec_hash)
  • Fingerprint : (element_id, bbox, role, etype, label, confidence)
  • Critère de fiabilité : au moins 2 succès et < 30% d'échecs → c'est ça la "cristallisation par répétition"

TargetSpec — vocabulaire déjà riche

Dans core/models/workflow_graph.py:572 :

  • context_hints : near_text, below_text, right_of_text, same_row_as_text, within_region, exclude_near_text
  • hard_constraints : within_container_text, min_area
  • weights : proximity, alignment, container, roi_iou

ResolutionStrategy V4 — vocabulaire pauvre (à enrichir)

Dans core/workflow/execution_plan.py:27 :

  • target_text, anchor_b64, zone, vlm_description, uia_*, dom_*
  • Pas de context_hints, pas de hard_constraints → trou dans l'expressivité

Décision validée

Léa = stagiaire qui apprend de la répétition. La mémoire précède la généralisation. Mais le raisonnement spatial reste indispensable comme filet de sécurité quand la mémoire ne suffit pas (décalages de layout, premier replay sur nouvel écran, généralisation entre écrans similaires).

Plan séquencé

Phase 1 — Mémoire sur V4 (≈1 jour, ~150 lignes)

Objectif : greffer TargetMemoryStore directement sur le resolve V4, sans passer par target_resolver ni UIElement.

Lookup avant OCR/template/VLM

fp = memory.lookup(screen_sig, target_spec)
if fp:
    # On a vu ce clic réussir ≥2 fois sur cet écran
    return fp.bbox  # clic direct, <10ms

Record après validation post-condition (déjà en place — title_match strict)

if post_condition_passed:
    memory.record_success(screen_sig, target_spec, fingerprint, "v4_ocr", confidence)
else:
    memory.record_failure(screen_sig, target_spec, reason)

À construire

  • screen_signature(screenshot) → hash stable. Piste : window_title + tokens OCR dominants, ou réutiliser core/execution/screen_signature.py si compatible.
  • Fingerprint léger : (x, y, w, h, method). Pas besoin de role/type/label en V4.
  • Point de branchement exact à confirmer avant implémentation :
    • Côté serveur dans resolve_engine (si resolve serveur)
    • Côté agent dans agent_v1/core/executor.py (si resolve local)

Bénéfice observable

  • 3ème passage d'un workflow sur même écran : 10-15s VLM remplacés par <10ms lookup
  • Léa apprend vraiment — pas parce qu'on a écrit un Crystallizer, parce qu'on a consommé celui qui dort depuis mars

Tests de validation

  • Rejouer un workflow 3 fois, mesurer le temps du 3ème passage
  • Vérifier que data/learning/target_memory.db se remplit
  • Vérifier que les événements JSONL s'écrivent

Phase 2 light — Raisonnement spatial OCR-only (≈3-5 jours, ~300-400 lignes)

Principe clé : pur pixel/OCR. Pas d'UIElement, pas de role/type, pas de parser UI. On évite le piège "ressusciter V3 complet".

À l'enregistrement (IRBuilder, côté serveur)

  1. Pour chaque clic (x, y) dans la trace
  2. OCR la zone autour (±300px)
  3. Identifier les 3-5 textes les plus proches avec direction (left/right/above/below) et distance
  4. Populer ResolutionStrategy.context_hints :
    {
        "right_of_text": "Nom du patient",   # 60px à gauche du clic
        "below_text": "Identité",            # 120px au-dessus
        "near_text": "Enregistrer",          # le texte du clic lui-même
    }
    

Au replay (resolve_engine), en cascade :

  1. Lookup mémoire (Phase 1) → si hit, clic direct
  2. Sinon : OCR de l'écran actuel
  3. Trouver les ancres de context_hints via OCR (normalisation accents + fuzzy Fiche #8)
  4. Calculer la zone candidate par intersection des contraintes spatiales
  5. Cliquer
  6. Si post-cond échoue : retombée VLM (exception handler)

Logique à porter depuis target_resolver.py

  • _apply_context_hints_to_candidates (lignes 2601-2803) — adaptée à "candidats = zones OCR" au lieu de "candidats = UIElement"
  • _find_element_by_text + normalisation (_norm_text, _fuzzy_ratio) lignes 211-235
  • Healing profile (ligne 395) pour relaxation progressive

Décision tranchée

  • OCR côté serveur Linux (docTR déjà présent via SomEngine)
  • Zéro changement sur le client Windows
  • Le serveur reçoit le screenshot au moment du build IR, extrait les context_hints, les intègre dans ResolutionStrategy

Enrichissement de ResolutionStrategy (execution_plan.py) Ajouter au dataclass :

context_hints: Dict[str, Any] = field(default_factory=dict)

Et dans execution_plan_runner._strategy_to_target_spec : propager context_hints dans target_spec.

Tests de validation

  • Enregistrer un workflow, vérifier que le plan contient des context_hints cohérents
  • Modifier la résolution de la VM (1920→1280), rejouer, vérifier que les clics atteignent la bonne cible
  • Ajouter un champ au-dessus de la cible, rejouer, vérifier robustesse

Phase 3 — Spatial V3 complet (pas maintenant)

Correction 10 avril 2026 : une version précédente de ce document affirmait qu'OmniParser avait été retiré. C'était faux. OmniParser est toujours présent :

  • core/detection/omniparser_adapter.py — 429 lignes
  • agent_v0/server_v1/resolve_engine.py:254_get_omniparser() singleton thread-safe, lazy-load
  • agent_v0/server_v1/resolve_engine.py:293_resolve_by_yolo() défini et importé dans api_stream.py

Ce qui est vrai : _resolve_by_yolo n'est jamais appelé dans la cascade V4 (_resolve_target_sync ne l'invoque pas). C'est du code dormant, pas supprimé.

Conséquence pour Phase 3 : on a potentiellement déjà un parser UI utilisable. Deux pistes :

  1. Ré-activer _resolve_by_yolo dans la cascade V4 (injecter un appel dans _resolve_target_sync comme fallback après OCR/template/VLM). Il produit déjà une liste d'éléments détectés avec bbox et role approximatif.
  2. Pont _resolve_by_yolo → List[UIElement] : adapter la sortie YOLO pour alimenter target_resolver V3. Un pont d'une centaine de lignes devrait suffire.

Avant de lancer Phase 3, vérifier :

  • Les modèles YOLO sont-ils toujours sur disque ? (omniparser.detect() lazy-loads)
  • Quelle qualité de détection sur des écrans Citrix/DPI réels ?
  • Les tests tests/integration/test_auto_healing_integration.py et tests/unit/test_fiche11_* passent-ils encore ?

Tant qu'on n'a pas fait cette vérification, Phase 3 reste pending.

Ce qu'on ne fait PAS

Tentation Pourquoi on résiste
Refactorer target_resolver.py pour le rendre V4-compatible 3495 lignes couplées à UIElement disparu — plus économique de le laisser dormir et recoder l'essentiel minimal dans V4
Brancher action_executor sur le streaming replay 2000 lignes de pipeline pour un bénéfice qu'on a en 150 lignes avec TargetMemoryStore seul
Ressusciter SpatialAnalyzer maintenant Zéro valeur sans UIElement riches en amont
Faire Phase 2 avant Phase 1 Léa raisonnerait à chaque clic, lent et coûteux — pas un "stagiaire qui apprend", juste un agent qui réfléchit en boucle

Suivi d'avancement

Phase 1 — Mémoire sur V4

  • Identifier le point de branchement exact (serveur vs agent)
  • Définir screen_signature stable pour V4
  • Définir le format fingerprint léger
  • Brancher memory.lookup() avant cascade OCR/template/VLM
  • Brancher memory.record_success() après post-cond validée
  • Brancher memory.record_failure() sur échec
  • Test : workflow rejoué 3 fois, 3ème en <100ms sur le resolve
  • Vérifier remplissage de data/learning/target_memory.db

Phase 2 light — Spatial OCR-only

  • Enrichir ResolutionStrategy avec context_hints
  • IRBuilder : extraire context_hints via OCR au build
  • execution_plan_runner : propager context_hints dans target_spec
  • resolve_engine : implémenter fallback spatial OCR
  • Porter _apply_context_hints_to_candidates adapté
  • Porter normalisation texte (_norm_text, _fuzzy_ratio)
  • Test : résolution VM modifiée, clic atteint toujours la cible
  • Test : champ ajouté dans le formulaire, robustesse préservée

Phase 3 — Spatial V3 complet

  • BLOQUÉ jusqu'à ce qu'un parser UI produise des UIElement

Liens

  • Code de référence : core/execution/target_resolver.py, core/learning/target_memory_store.py
  • Architecture V4 : core/workflow/execution_plan.py, core/workflow/execution_compiler.py, agent_v0/server_v1/execution_plan_runner.py
  • Replay runtime : agent_v0/agent_v1/core/executor.py