Files
rpa_vision_v3/docs/PLAN_AGENT_RUST.md
Dom 792cc2aa9a docs: plan de migration agent Windows Python → Rust
Étude de faisabilité complète : 100% faisable, 0 bloqueur.
Crates identifiées pour les 8 fonctionnalités clés.
Migration en 5 phases sur 6-10 semaines.
Gains : exe unique 10MB, démarrage 200ms, RAM 30MB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 19:35:41 +01:00

817 lines
37 KiB
Markdown

# Plan de réécriture de l'Agent Windows en Rust
**Date** : 18 mars 2026
**Statut** : Proposition technique (phase recherche)
**Auteur** : Claude Code + Dom
---
## 1. Contexte et motivation
### Situation actuelle (Python)
L'agent Windows (`agent_v0/agent_v1/`) est écrit en Python et nécessite :
- Python 3.12 installé sur le PC cible
- Un virtualenv avec ~10 dépendances directes (mss, pynput, Pillow, requests, pystray, plyer, pywebview, pywin32, psutil)
- ~30 dépendances transitives
- Un script d'installation (`setup_v1.bat`) fragile
- ~200 Mo d'espace disque (Python + venv + site-packages)
- Temps de démarrage ~3-5 secondes
### Objectif (Rust)
Produire un **exécutable unique** (`lea-agent.exe`, ~5-10 Mo) avec :
- Zéro dépendance externe (pas de runtime, pas de DLL, pas d'installation)
- Démarrage quasi-instantané (<500ms)
- Empreinte mémoire réduite (~15-30 Mo vs ~80-120 Mo en Python)
- Distribution par simple copie du .exe
### Périmètre fonctionnel à réécrire
| Module Python | Fichier | LOC | Responsabilité |
|---|---|---|---|
| `main.py` | Orchestrateur | ~390 | Boucles parallèles (heartbeat, replay, watchdog) |
| `config.py` | Configuration | ~55 | Paramètres, chemins, endpoints serveur |
| `core/captor.py` | Capture d'événements | ~330 | Souris, clavier, focus fenêtre, buffer texte |
| `core/executor.py` | Exécuteur d'actions | ~525 | Replay visuel et aveugle, polling serveur |
| `network/streamer.py` | Streaming réseau | ~400 | Queue, retry, health-check, compression JPEG |
| `vision/capturer.py` | Capture d'écran | ~95 | Screenshots, crops, dédup par hash |
| `vision/blur_sensitive.py` | Floutage données | ~205 | Détection champs de saisie, Gaussian blur |
| `ui/smart_tray.py` | Icône systray | ~780 | Menu, workflows, état, hotkey Ctrl+Shift+L |
| `ui/chat_window.py` | Fenêtre de chat | ~1150 | Chat Léa en tkinter, messages, file upload |
| `ui/notifications.py` | Notifications toast | ~220 | Toasts natifs Windows, rate limiting |
| `ui/shared_state.py` | État partagé | ~190 | Thread-safe state, observer pattern |
| `ui/capture_server.py` | Mini serveur HTTP | ~380 | Port 5006, screenshot à la demande, actions fichiers |
| `session/storage.py` | Stockage local | ~75 | Sessions, nettoyage par âge/taille |
| `window_info_crossplatform.py` | Info fenêtre | ~180 | Titre + process de la fenêtre active (Win32) |
| **Total** | | **~4975** | |
---
## 2. Crates Rust pour chaque fonctionnalité
### 2.1 Capture d'écran (remplace `mss`)
| Crate | Version | Statut | Support Windows | Notes |
|---|---|---|---|---|
| **`xcap`** | 0.8.x | Actif | Oui (DXGI) | Cross-platform (Linux, macOS, Windows). Successeur de `screenshots`. Video recording WIP. Problèmes de build docs.rs sur dernières versions. |
| **`windows-capture`** | 1.5.x | Actif | Oui (Graphics Capture API) | Le plus performant sur Windows. Utilise l'API Microsoft moderne. Windows 10+ uniquement. |
| `win-screenshot` | 4.x | Mature | Oui | Windows only, simple, stable. |
| `scap` | - | Actif | Oui | Par CapSoftware, haute performance, cross-platform. |
**Recommandation** : **`xcap`** pour la cross-compatibilité et la simplicité API. Fallback vers `windows-capture` si des performances extrêmes sont nécessaires (capture continue pour le heartbeat).
**Justification** : Notre agent capture un screenshot toutes les 5 secondes (heartbeat) et à chaque clic. xcap suffit largement. windows-capture serait un over-engineering pour notre cas d'usage.
### 2.2 Capture et simulation souris/clavier (remplace `pynput`)
C'est la fonctionnalité la plus critique car elle couvre **deux besoins distincts** :
- **Écoute globale** (capture) : intercepter les clics, frappes et mouvements sans focus
- **Simulation** (replay) : générer des clics, frappes et combos de touches
| Crate | Version | Rôle | Support Windows | Notes |
|---|---|---|---|---|
| **`rdev`** | 0.5.x | Écoute globale | Oui (Low-Level Hooks) | Listen + grab + simulate. Le plus complet pour notre usage. Cross-platform. |
| **`enigo`** | 0.6.x | Simulation | Oui (SendInput) | Mature, 62K downloads/mois. Mouse + Keyboard simulation. Serde support. |
| `willhook` | 0.4.x | Écoute (Windows) | Windows only | Lecture seule, pas de simulation. Safe hooks. |
| `inputbot` | 0.6.x | Hotkeys + simulation | Oui | Global hotkeys + simulation, mais moins mature. |
| `mki` | - | Listen + simulate | Windows + Linux | Registering global input hooks and simulating. |
**Recommandation** : **`rdev`** pour la capture (listen) + **`enigo`** pour la simulation (replay). rdev fournit un listener global qui intercepte souris + clavier sans focus fenêtre, exactement comme pynput. enigo fournit une API propre pour simuler les clics et frappes pendant le replay.
**Risque** : Le buffer de saisie texte (accumulation des frappes avec flush après 500ms d'inactivité) est une logique métier complexe. Elle devra être réimplémentée en Rust avec des timers async — faisable mais demande de l'attention.
### 2.3 Icône systray (remplace `pystray`)
| Crate | Version | Support Windows | Notes |
|---|---|---|---|
| **`tray-icon`** | 0.21.x | Oui | Par l'équipe Tauri. Sous-menus, événements, tooltip. Le standard actuel. |
| `tray-item` | 0.10.x | Oui | API minimaliste, cross-platform. |
| `trayicon` | 0.4.x | Oui | Windows + KDE. Menu popup, clic, double-clic. |
**Recommandation** : **`tray-icon`** (crate Tauri). C'est le standard de l'écosystème Rust pour les icônes systray. Maintenu activement (v0.21 avec Tauri 2.10). Supporte les sous-menus (nécessaire pour « Mes tâches »), les icônes dynamiques (nécessaire pour les états idle/recording/replay), et les événements clic/double-clic.
### 2.4 Client HTTP (remplace `requests`)
| Crate | Version | Notes |
|---|---|---|
| **`reqwest`** | 0.12.x | Standard de facto. Async (tokio) ou blocking. Multipart uploads. JSON. TLS natif. |
| `ureq` | 2.x | Synchrone, léger, pas de tokio. Bon pour les cas simples. |
**Recommandation** : **`reqwest`** avec le feature `blocking` pour les threads de polling, et le feature `multipart` pour l'upload d'images. reqwest est le standard absolu en Rust pour HTTP client.
**Configuration Cargo.toml** :
```toml
reqwest = { version = "0.12", features = ["json", "multipart", "blocking"] }
```
### 2.5 Traitement d'images (remplace `Pillow`)
| Crate | Version | Notes |
|---|---|---|
| **`image`** | 0.25.x | Standard Rust. Encodage/décodage PNG, JPEG. Resize, crop. Pure Rust. |
| `fast_image_resize` | - | Resize SIMD optimisé. |
**Recommandation** : **`image`** (crate standard). Couvre tous nos besoins : lecture PNG des captures, conversion en JPEG pour le streaming, resize pour le hash perceptuel, crop pour la capture duale.
### 2.6 Notifications toast (remplace `plyer`)
| Crate | Version | Support Windows | Notes |
|---|---|---|---|
| **`winrt-notification`** | 0.5.x | Windows 8.1/10/11 | Wrapper WinRT toast API. Léger, stable. |
| `notify-rust` | 4.x | Cross-platform | Linux/macOS via D-Bus/NSNotification. Windows via winrt-notification. |
| `win-toast-notify` | 0.1.x | Windows 10/11 | Récent, testé Windows 11. |
**Recommandation** : **`winrt-notification`** directement (Windows only pour l'agent). Si cross-platform souhaité plus tard, `notify-rust` qui utilise winrt-notification en backend.
### 2.7 Fenêtre de chat (remplace `tkinter`)
C'est la fonctionnalité la plus complexe à porter. L'agent Python utilise tkinter (stdlib) pour une fenêtre de chat complète avec :
- Messages (bulles Léa/utilisateur), scrollbar, historique
- Barre d'actions rapides (Apprenez-moi, Lancer, Données, Arrêter, Aide)
- Champ de saisie avec placeholder, upload de fichiers
- Indicateur de connexion serveur, drag & resize
- DPI awareness, ancrage bas-droite
| Crate | Version | Approche | Notes |
|---|---|---|---|
| **`wry`** | 0.54.x | WebView2 (Edge) | Par Tauri. Charge du HTML/CSS/JS dans une fenêtre native. WebView2 est préinstallé sur Windows 10+. |
| `webview2` | - | WebView2 (direct) | Bindings Rust directs pour WebView2. Plus bas niveau que wry. |
| `iced` | 0.13.x | GUI native Rust | Elm-like, cross-platform. Pas de HTML/CSS. Courbe d'apprentissage. |
| `egui` | 0.30.x | GUI immediate mode | Très léger, rendu custom. Pas adapté pour un chat riche. |
**Recommandation** : **`wry`** (WebView2). C'est l'approche optimale pour notre cas :
1. **Le chat est essentiellement une interface web** — HTML/CSS est le meilleur outil pour rendre des bulles de chat, du scroll, des boutons d'actions rapides
2. **WebView2 est préinstallé** sur Windows 10 21H2+ et Windows 11 — pas de dépendance supplémentaire
3. **L'interface chat du VWB existe déjà** côté serveur (port 5004) — on peut charger l'URL directement dans le WebView
4. **Pas besoin de réécrire le frontend** — la fenêtre wry charge simplement `http://{server}:5004/chat` avec injection de l'identité machine
**Taille supplémentaire** : ~0 octets (WebView2 est fourni par Windows, wry ne fait que l'instancier)
**Fallback** : Si WebView2 n'est pas disponible (Windows 7/8), l'agent fonctionne sans la fenêtre de chat (systray + notifications seulement).
### 2.8 Floutage données sensibles (remplace `OpenCV`)
| Crate | Version | Notes |
|---|---|---|
| **`libblur`** | 0.19.x | Pure Rust, SIMD (SSE/AVX/NEON). Gaussian blur 100 FPS sur 4K. Pas de dépendance OpenCV. |
| `imageproc` | 0.25.x | Traitement d'images basé sur le crate `image`. Contours, seuillage, morphologie. |
| `opencv` (Rust) | 0.93.x | Bindings OpenCV. Lourds (~200 Mo de DLL), difficile à cross-compiler. |
**Recommandation** : **`libblur`** pour le flou gaussien + **`imageproc`** pour la détection de contours et le seuillage. Cela remplace OpenCV sans aucune dépendance native.
**Détail de l'implémentation** : Le module `blur_sensitive.py` actuel utilise OpenCV pour :
1. Conversion en niveaux de gris → `image` crate
2. Seuillage binaire → `imageproc` (threshold)
3. Morphologie (close) → `imageproc` (dilate/erode)
4. Détection de contours → `imageproc` (find_contours)
5. Gaussian blur sur les zones détectées → `libblur`
Toutes ces opérations ont des équivalents purs Rust. Pas besoin d'OpenCV.
### 2.9 Informations fenêtre active (remplace `pywin32`)
| Crate | Version | Notes |
|---|---|---|
| **`windows`** | 0.61.x | Crate officiel Microsoft. Accès complet à Win32 API. |
**Recommandation** : **`windows`** (crate officiel Microsoft). On utilisera :
- `GetForegroundWindow()` pour obtenir le handle de la fenêtre active
- `GetWindowTextW()` pour le titre
- `GetWindowThreadProcessId()` pour le PID
- `OpenProcess()` + `QueryFullProcessImageNameW()` pour le nom du processus
Cela remplace pywin32 + psutil en une seule dépendance, sans DLL externe.
### 2.10 Mini-serveur HTTP local (remplace `http.server`)
| Crate | Version | Notes |
|---|---|---|
| **`axum`** | 0.8.x | Framework web async (tokio). Léger, performant. |
| `warp` | 0.3.x | Alternative plus légère. |
| `tiny_http` | 0.12.x | Synchrone, minimaliste. Parfait pour un mini-serveur. |
**Recommandation** : **`tiny_http`** pour le serveur de capture (port 5006). C'est un serveur HTTP synchrone minimaliste qui suffit pour 2 endpoints (GET /capture, POST /file-action). Pas besoin d'un framework async pour ça.
### 2.11 Runtime asynchrone
| Crate | Version | Notes |
|---|---|---|
| **`tokio`** | 1.x | Runtime async standard. Nécessaire pour reqwest async. |
**Note** : On peut éviter tokio en utilisant `reqwest` en mode `blocking` et des threads standards. Cela simplifie l'architecture et réduit la taille du binaire. L'agent actuel en Python utilise des threads daemon, pas de l'async — le portage direct en threads Rust est plus fidèle.
**Recommandation** : **Pas de tokio**. Utiliser des threads standards (`std::thread`) + `reqwest` blocking. L'agent n'a pas besoin de milliers de connexions concurrentes — il a 4-5 boucles parallèles avec des `sleep` de 1-5 secondes.
---
## 3. Architecture proposée
```
rpa_agent_rust/
├── Cargo.toml — Dépendances et configuration build
├── build.rs — Embedding icône + manifeste Windows
├── assets/
│ ├── lea-icon.ico — Icône de l'application
│ ├── manifest.xml — Manifeste Windows (DPI awareness, UAC)
│ └── chat/
│ └── index.html — Fallback HTML si serveur chat indisponible
├── src/
│ ├── main.rs — Point d'entrée, orchestrateur (équivalent main.py)
│ ├── config.rs — Configuration (SERVER_URL, MACHINE_ID, ports, chemins)
│ ├── state.rs — État partagé thread-safe (équivalent shared_state.py)
│ │
│ ├── capture/
│ │ ├── mod.rs
│ │ ├── screen.rs — Capture d'écran (xcap) — équivalent capturer.py
│ │ ├── events.rs — Capture souris/clavier (rdev) — équivalent captor.py
│ │ └── window_info.rs — Info fenêtre active (windows crate) — équivalent window_info.py
│ │
│ ├── replay/
│ │ ├── mod.rs
│ │ ├── executor.rs — Exécution d'actions (enigo) — équivalent executor.py
│ │ └── poller.rs — Polling serveur replay — partie de executor.py
│ │
│ ├── network/
│ │ ├── mod.rs
│ │ ├── streamer.rs — Streaming événements/images (reqwest) — équivalent streamer.py
│ │ └── http_server.rs — Mini-serveur capture (tiny_http) — équivalent capture_server.py
│ │
│ ├── vision/
│ │ ├── mod.rs
│ │ └── blur.rs — Floutage zones sensibles (libblur + imageproc) — équivalent blur_sensitive.py
│ │
│ ├── ui/
│ │ ├── mod.rs
│ │ ├── tray.rs — Icône systray (tray-icon) — équivalent smart_tray.py
│ │ ├── chat.rs — Fenêtre chat (wry/WebView2) — équivalent chat_window.py
│ │ └── notifications.rs — Toasts Windows (winrt-notification) — équivalent notifications.py
│ │
│ ├── storage/
│ │ ├── mod.rs
│ │ └── session.rs — Stockage local + nettoyage — équivalent storage.py
│ │
│ └── file_actions/
│ ├── mod.rs
│ └── handler.rs — Opérations fichiers (list, move, sort) — partie de capture_server.py
└── tests/
├── integration/
│ ├── test_streamer.rs
│ └── test_replay.rs
└── unit/
├── test_blur.rs
├── test_events.rs
└── test_state.rs
```
### 3.1 Flux de données principal
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ EventCaptor │────→│ Orchestrator │────→│ Streamer │────→ Serveur :5005
│ (rdev) │ │ (main.rs) │ │ (reqwest) │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────┴──────┐
│ScreenCapture│
│ (xcap) │
└─────────────┘
┌──────────────┐ ┌──────────────┐
│ ReplayPoller │────→│ Executor │────→ Souris/Clavier (enigo)
│ (reqwest) │ │ (enigo) │
└──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ TrayIcon │←───→│ State │←───→│ ChatWindow │
│ (tray-icon) │ │ (Arc<Mutex>)│ │ (wry) │
└──────────────┘ └──────────────┘ └──────────────┘
```
### 3.2 Gestion des threads
L'agent actuel utilise 8-10 threads daemon. Le portage Rust conserve cette architecture :
| Thread | Rôle | Crate principal |
|---|---|---|
| **Main** | Boucle événements systray (tray-icon) | `tray-icon` |
| **Event Listener** | Capture souris/clavier | `rdev` |
| **Window Focus** | Surveillance focus fenêtre (500ms) | `windows` |
| **Heartbeat** | Screenshot + envoi toutes les 5s | `xcap` + `reqwest` |
| **Background HB** | Heartbeat sans session active | `xcap` + `reqwest` |
| **Replay Poller** | GET /replay/next (1-30s backoff) | `reqwest` |
| **Stream Worker** | Queue d'envoi événements/images | `reqwest` |
| **Health Check** | Vérification serveur (30s) | `reqwest` |
| **HTTP Server** | Serveur capture port 5006 | `tiny_http` |
| **Chat Window** | Fenêtre WebView2 (si visible) | `wry` |
### 3.3 Communication inter-threads
```rust
// État partagé — équivalent de shared_state.py
pub struct AgentState {
inner: Arc<Mutex<AgentStateInner>>,
// Channel pour notifier les listeners
notify: broadcast::Sender<StateChange>,
}
struct AgentStateInner {
recording: bool,
recording_name: String,
actions_count: u32,
replay_active: bool,
server_connected: bool,
}
// Queue de streaming — équivalent de la queue bornée Python
// crossbeam-channel est plus performant que std::sync::mpsc
type StreamQueue = crossbeam_channel::bounded<StreamItem>(100);
```
---
## 4. Évaluation de faisabilité par fonctionnalité
| Fonctionnalité | Difficulté | Crates | Commentaire |
|---|---|---|---|
| Capture d'écran (full + crop) | **Facile** | xcap, image | API directe, bien documentée |
| Encodage JPEG + resize | **Facile** | image | Standard Rust |
| Hash perceptuel (16x16 → MD5) | **Facile** | image, md5 | Trivial |
| Capture événements souris | **Facile** | rdev | Global listener, events bien typés |
| Capture événements clavier | **Facile** | rdev | Idem |
| Buffer de saisie texte (flush 500ms) | **Moyen** | rdev + timers | Logique métier spécifique, timers thread-safe |
| Détection double-clic | **Facile** | rdev | Logique pure, pas de dépendance |
| Simulation clic souris | **Facile** | enigo | API directe |
| Simulation frappe clavier | **Facile** | enigo | keyboard.type() + key combos |
| Simulation combo clavier | **Facile** | enigo | press/release des modifiers |
| Info fenêtre active (Win32) | **Facile** | windows | GetForegroundWindow, GetWindowTextW |
| Streaming HTTP événements | **Facile** | reqwest (blocking) | POST JSON |
| Upload image multipart | **Facile** | reqwest (multipart) | POST multipart/form-data |
| Retry + backoff exponentiel | **Facile** | Code pur | Boucle simple |
| Compression JPEG avant envoi | **Facile** | image | encode_to_jpeg |
| Queue bornée + backpressure | **Facile** | crossbeam-channel | bounded(100) + try_send |
| Health-check serveur | **Facile** | reqwest (blocking) | GET /stats |
| Polling replay | **Facile** | reqwest (blocking) | GET + POST JSON |
| Résolution visuelle (POST screenshot) | **Facile** | reqwest + base64 | POST JSON avec image b64 |
| Icône systray dynamique | **Facile** | tray-icon | Génération d'icônes cercle coloré |
| Menu systray dynamique | **Moyen** | tray-icon | Reconstruction du menu à chaque changement d'état |
| Sous-menu workflows | **Moyen** | tray-icon | Nécessite des sous-menus dynamiques (liste du serveur) |
| Notifications toast | **Facile** | winrt-notification | API simple, rate limiting en code pur |
| Hotkey global Ctrl+Shift+L | **Moyen** | rdev (grab) ou windows | Nécessite un hook clavier global |
| Fenêtre de chat (WebView2) | **Moyen** | wry | Création fenêtre + chargement URL serveur |
| Chat fallback (HTML local) | **Moyen** | wry | Embedding HTML/CSS/JS dans le binaire |
| Floutage champs de saisie | **Moyen** | imageproc + libblur | Réécriture de la détection de contours + seuillage |
| Dialogues tkinter (consentement, nom) | **Moyen** | windows (MessageBoxW) | Dialogues Win32 natifs au lieu de tkinter |
| Mini-serveur HTTP (port 5006) | **Facile** | tiny_http | 2-3 endpoints, synchrone |
| Actions fichiers (list, sort, move) | **Facile** | std::fs | API standard Rust |
| Stockage sessions + nettoyage | **Facile** | std::fs | Nettoyage par âge et taille, trivial |
| État partagé thread-safe | **Facile** | Arc<Mutex<T>> | Pattern standard Rust |
### Résumé de faisabilité
- **Facile** : 24 fonctionnalités (75%)
- **Moyen** : 8 fonctionnalités (25%)
- **Difficile** : 0
- **Bloquant** : 0
**Verdict : 100% faisable. Aucun bloqueur identifié.**
---
## 5. Stratégie de migration par phases
### Phase 1 — Agent minimal fonctionnel (2-3 semaines)
**Objectif** : Un .exe qui capture et envoie au serveur, et exécute les replays.
**Fonctionnalités** :
- [x] Configuration (SERVER_URL, MACHINE_ID, ports)
- [x] Capture d'écran (xcap) — full + crop
- [x] Capture événements souris/clavier (rdev) — clics, combos, texte
- [x] Streaming réseau (reqwest) — événements + images + register/finalize
- [x] Polling replay (reqwest) — GET /replay/next + POST /result
- [x] Exécution d'actions (enigo) — clic, type, key_combo, scroll, wait
- [x] Heartbeat toutes les 5s avec dédup
- [x] État partagé (Arc<Mutex>)
- [x] Stockage local sessions
**Pas encore** : UI, notifications, floutage, serveur capture, chat
**Livrable** : `lea-agent.exe` (~3 Mo) qui s'exécute en mode headless (pas d'icône, logs en console). Test d'intégration avec le serveur streaming existant.
**Critère de validation** : Enregistrer une session depuis l'agent Rust, vérifier qu'elle apparaît dans le VWB, lancer un replay.
### Phase 2 — Systray + notifications (1-2 semaines)
**Objectif** : L'agent a une présence visuelle dans la barre des tâches.
**Fonctionnalités** :
- [x] Icône systray avec états (idle/recording/connected/replay)
- [x] Menu contextuel (Apprenez-moi, C'est terminé, Mes tâches, Quitter)
- [x] Sous-menu dynamique des workflows (fetch depuis serveur)
- [x] Notifications toast (winrt-notification)
- [x] Dialogue de consentement (MessageBoxW natif)
- [x] Dialogue de saisie du nom de tâche (InputBox Win32)
- [x] Arrêt d'urgence (Article 14, Règlement IA)
- [x] Hotkey global Ctrl+Shift+L
**Livrable** : `lea-agent.exe` (~4 Mo) avec icône systray fonctionnelle. Même expérience utilisateur que l'agent Python (sans le chat).
### Phase 3 — Fenêtre de chat (1-2 semaines)
**Objectif** : Le chat Léa intégré dans l'agent.
**Fonctionnalités** :
- [x] Fenêtre WebView2 (wry) chargeant `http://{server}:5004/chat`
- [x] Toggle via Ctrl+Shift+L ou menu systray
- [x] Ancrage bas-droite de l'écran
- [x] Fallback HTML local si serveur indisponible
- [x] Communication bidirectionnelle via JavaScript bridge (pour l'état agent)
**Remarque** : Le chat actuel (tkinter) est une réécriture complète du frontend. Avec wry, on peut simplement charger l'interface chat existante du serveur, ce qui est **plus simple** et **plus maintenable** que l'approche Python.
**Livrable** : `lea-agent.exe` (~5 Mo) avec chat intégré.
### Phase 4 — Parité complète (1-2 semaines)
**Objectif** : 100% des fonctionnalités de l'agent Python.
**Fonctionnalités** :
- [x] Floutage des données sensibles (libblur + imageproc)
- [x] Mini-serveur HTTP port 5006 (capture à la demande, CORS)
- [x] Actions fichiers (list_dir, create_dir, move, copy, sort_by_ext)
- [x] Sécurité chemins (zones autorisées C:\Users, D:\, E:\)
- [x] Nettoyage automatique des sessions (par âge et taille)
- [x] Info fenêtre active (surveillance focus, Win32 API)
- [x] Divulgation IA (Article 50, Règlement IA)
- [x] Log rotation et rétention 180 jours (Article 12, Règlement IA)
**Livrable** : `lea-agent.exe` (~6-8 Mo), parité fonctionnelle complète. Prêt pour déploiement en production.
### Phase 5 — Optimisations et polish (optionnel)
- Auto-update (téléchargement de la nouvelle version depuis le serveur)
- Installeur MSI/NSIS optionnel (pour l'inscription au démarrage Windows)
- Compression UPX du binaire (~3-4 Mo)
- Signature du code (certificat Authenticode)
- Télémétrie d'erreurs (panic handler → envoi au serveur)
---
## 6. Build et distribution
### 6.1 Cross-compilation Linux → Windows
Deux approches possibles :
**Option A : `cargo-xwin` (recommandée)**
```bash
# Installation une fois
cargo install cargo-xwin
rustup target add x86_64-pc-windows-msvc
# Build
cargo xwin build --target x86_64-pc-windows-msvc --release
```
Avantage : Produit un .exe compatible MSVC (le standard Windows).
Inconvénient : Télécharge le SDK Windows (~1 Go la première fois).
**Option B : `cross` (plus simple, GNU)**
```bash
cargo install cross
cross build --target x86_64-pc-windows-gnu --release
```
Avantage : Utilise un container Docker, rien à installer.
Inconvénient : Produit un .exe GNU (peut nécessiter mingw runtime).
**Recommandation** : `cargo-xwin` pour la production (MSVC target), `cross` pour les tests rapides.
### 6.2 Icône dans le .exe
```rust
// build.rs
fn main() {
if std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
let mut res = tauri_winres::WindowsResource::new();
res.set_icon("assets/lea-icon.ico");
res.set("ProductName", "Léa Agent");
res.set("FileDescription", "Agent RPA Vision - Léa");
res.set("LegalCopyright", "Copyright 2026");
res.compile().unwrap();
}
}
```
Dépendance build : `tauri-winres` (fork maintenu de `winres`).
### 6.3 Manifeste Windows (DPI + UAC)
Le manifeste `assets/manifest.xml` déclare :
- DPI awareness (per-monitor V2) — nécessaire pour les captures d'écran correctes
- requestedExecutionLevel = asInvoker (pas besoin d'admin)
- Windows 10+ compatibility
### 6.4 Signature du code
Pour éviter les avertissements SmartScreen à l'exécution :
```bash
# Avec signtool (Windows SDK)
signtool sign /f certificate.pfx /p password /tr http://timestamp.digicert.com /td sha256 /fd sha256 lea-agent.exe
# Ou avec le crate rust-codesign depuis Linux
cargo install codesign
codesign --cert certificate.pfx --password password lea-agent.exe
```
**Coût** : Un certificat EV Code Signing coûte ~300-500 EUR/an. Un certificat OV standard ~100-200 EUR/an. Pour un usage interne (déploiement sur des machines contrôlées), la signature n'est pas obligatoire — on peut ajouter une exception Windows Defender.
### 6.5 Estimation de taille du .exe
| Configuration | Taille estimée |
|---|---|
| Debug build | ~30-50 Mo |
| Release build (défaut) | ~15-20 Mo |
| Release + LTO + strip | ~6-10 Mo |
| Release + LTO + strip + opt-level z | ~5-8 Mo |
| Release + LTO + strip + UPX | ~3-5 Mo |
**Profil release optimisé** (Cargo.toml) :
```toml
[profile.release]
opt-level = "s" # Optimisation taille (z = encore plus petit mais plus lent)
lto = true # Link-Time Optimization
codegen-units = 1 # Optimisation max
panic = "abort" # Pas de stack unwinding
strip = true # Suppression des symboles
```
---
## 7. Comparaison de performances estimée
| Métrique | Python (actuel) | Rust (estimé) | Amélioration |
|---|---|---|---|
| **Taille installation** | ~200 Mo (Python + venv) | ~6-10 Mo (.exe seul) | **20-30x plus petit** |
| **Temps de démarrage** | 3-5 secondes | 100-300 ms | **10-15x plus rapide** |
| **Mémoire au repos** | 80-120 Mo | 15-30 Mo | **4-5x moins** |
| **Mémoire en session** | 120-180 Mo | 25-50 Mo | **3-4x moins** |
| **Capture screenshot** | 30-80 ms (mss) | 5-15 ms (xcap/DXGI) | **3-5x plus rapide** |
| **Compression JPEG** | 15-30 ms (Pillow) | 5-10 ms (image crate) | **2-3x plus rapide** |
| **Envoi HTTP** | ~identique (TCP) | ~identique (TCP) | Négligeable |
| **Floutage** | 50-150 ms (OpenCV) | 10-30 ms (libblur SIMD) | **3-5x plus rapide** |
| **Utilisation CPU idle** | 1-3% | <0.5% | **3-5x moins** |
| **Complexité installation** | Script .bat + Python + pip | Copier 1 fichier .exe | **Incomparable** |
**Notes** :
- L'envoi HTTP est dominé par la latence réseau, pas par le langage
- Les captures screenshot sont déjà rapides en Python (mss utilise DXGI) — le gain Rust est surtout sur le traitement post-capture
- Le gain le plus significatif est sur la **distribution** (1 fichier vs installation complexe)
---
## 8. Risques et défis
### 8.1 Risques techniques
| Risque | Impact | Probabilité | Mitigation |
|---|---|---|---|
| `rdev` buggy sur certaines versions de Windows | Élevé | Faible | Tests sur Windows 10 LTSC, 11 23H2. Fallback vers `willhook` + `windows` crate direct. |
| WebView2 absent (Windows 7/8) | Faible | Très faible | PC cible = Windows 10+. Le chat est optionnel, le systray + notifications suffisent. |
| `tray-icon` ne supporte pas les sous-menus dynamiques | Moyen | Faible | Tester en amont. Fallback : menu plat avec numérotation. |
| DPI scaling incorrect pour les captures | Moyen | Moyen | Manifeste DPI-aware + tests multi-écran. Le problème existe déjà en Python. |
| Cross-compilation échoue avec certains crates (liens natifs) | Moyen | Moyen | Build natif sur une VM Windows si nécessaire. CI GitHub Actions avec Windows runner. |
| Bugs de threading (deadlocks, races) | Élevé | Faible | Le borrow checker Rust élimine les data races. Les deadlocks restent possibles — revue attentive de l'ordre de verrouillage. |
### 8.2 Ce qui reste côté serveur
Les fonctionnalités suivantes ne sont PAS portées en Rust car elles restent côté serveur (Python) :
- **Analyse visuelle (CLIP, FAISS, ScreenAnalyzer)** — GPU server
- **VLM (Ollama, template matching)** — GPU server
- **Construction du workflow (GraphBuilder)** — serveur streaming
- **Chat LLM (Copilot, GestureCatalog)** — serveur chat port 5004
- **Dashboard, VWB** — serveur web
L'agent Rust est **strictement un client** : il capture, envoie, et exécute. Toute l'intelligence reste sur le serveur.
### 8.3 Chat : stratégie WebView vs natif
Le chat tkinter actuel (chat_window.py, 1150 LOC) est le module le plus gros de l'agent. Il implémente un frontend complet en tkinter : bulles de messages, scrollbar, boutons d'actions, upload fichier, indicateur de connexion.
**L'approche WebView (wry) est supérieure** pour plusieurs raisons :
1. **Pas de duplication** : L'interface chat existe déjà sur le serveur (port 5004). En la chargeant dans un WebView, on bénéficie de toutes les améliorations côté serveur sans rebuilder l'agent.
2. **Rendu supérieur** : HTML/CSS produit une interface beaucoup plus riche que tkinter (animations, emojis, markdown, images inline).
3. **Maintenance réduite** : Une seule codebase pour le chat (côté serveur), pas de duplication Python/Rust.
4. **Taille binaire** : wry ajoute ~200 Ko au binaire (le rendu est fait par WebView2 déjà installé sur Windows).
**Inconvénient** : L'agent nécessite une connexion au serveur pour afficher le chat. En mode offline, il faudrait un HTML de fallback embarqué dans le binaire (message « Connexion au serveur requise »).
### 8.4 AZERTY et internationalisation
Le clavier du PC cible est AZERTY. Points d'attention :
- **rdev** renvoie les scancodes bruts — la conversion en caractères dépend du layout clavier Windows. Vérifier que rdev gère correctement AZERTY (les accents, les chiffres en Shift, etc.).
- **enigo** pour la simulation : `keyboard.type()` utilise SendInput avec les caractères Unicode, pas les scancodes — devrait fonctionner correctement en AZERTY.
- À tester en priorité lors de la Phase 1.
---
## 9. Dépendances Cargo.toml prévisionnelles
```toml
[package]
name = "lea-agent"
version = "1.0.0"
edition = "2021"
description = "Agent RPA Vision - Léa"
build = "build.rs"
[dependencies]
# Capture d'écran
xcap = "0.8"
# Capture événements souris/clavier
rdev = "0.5"
# Simulation souris/clavier (replay)
enigo = { version = "0.6", features = ["serde"] }
# Systray
tray-icon = "0.21"
# HTTP client
reqwest = { version = "0.12", default-features = false, features = [
"json", "multipart", "blocking", "rustls-tls"
] }
# Image processing
image = "0.25"
# Floutage
libblur = "0.19"
imageproc = "0.25"
# Notifications Windows
winrt-notification = "0.5"
# Chat window (WebView2)
wry = "0.54"
# Mini-serveur HTTP
tiny_http = "0.12"
# Sérialisation
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Logging
log = "0.4"
env_logger = "0.11"
# Hash (perceptuel screenshot)
md5 = "0.7"
# Base64 (screenshots post-action)
base64 = "0.22"
# UUID (session IDs)
uuid = { version = "1", features = ["v4"] }
# Date/heure
chrono = "0.4"
# Canaux inter-threads performants
crossbeam-channel = "0.5"
# Win32 API (info fenêtre, dialogues)
[target.'cfg(windows)'.dependencies]
windows = { version = "0.61", features = [
"Win32_UI_WindowsAndMessaging",
"Win32_System_Threading",
"Win32_Foundation",
"Win32_Security",
] }
[build-dependencies]
tauri-winres = "0.2"
[profile.release]
opt-level = "s"
lto = true
codegen-units = 1
panic = "abort"
strip = true
```
**Nombre de dépendances directes** : 18 (vs ~10 en Python, mais toutes compilées statiquement dans le binaire final — pas de DLL, pas de pip, pas de virtualenv).
---
## 10. Plan de tests
### 10.1 Tests unitaires (Phase 1)
| Module | Tests |
|---|---|
| `config.rs` | Lecture variables d'environnement, valeurs par défaut |
| `state.rs` | Transitions d'état, notifications listeners, thread-safety |
| `vision/blur.rs` | Détection champs de saisie, application du flou, image inchangée si pas de champ |
| `capture/events.rs` | Buffer texte : accumulation, flush après timeout, flush sur clic |
| `capture/events.rs` | Double-clic : détection, tolérance position, timeout |
| `replay/executor.rs` | Conversion coordonnées normalisées → pixels |
| `storage/session.rs` | Création dossier, nettoyage par âge, nettoyage par taille |
| `file_actions/handler.rs` | Validation chemins (sécurité anti-traversal) |
### 10.2 Tests d'intégration (Phase 2-3)
| Scénario | Description |
|---|---|
| Streaming | Envoyer événement + image au serveur :5005, vérifier réception |
| Replay | Simuler une action replay depuis le serveur, vérifier exécution |
| Heartbeat | Vérifier envoi périodique + dédup (même écran = pas d'envoi) |
| Tray menu | Vérifier affichage menu, changement d'icône sur enregistrement |
| Chat WebView | Vérifier ouverture fenêtre, chargement URL serveur |
### 10.3 Tests manuels sur PC Windows cible
| Test | Vérification |
|---|---|
| AZERTY | Taper « été » → le texte capturé est bien « été » |
| Multi-écran | Capture correcte sur l'écran principal |
| DPI 125%/150% | Coordonnées de clic correctes après scaling |
| Sleep/Reprise | L'agent se reconnecte après mise en veille |
| Replay complet | Workflow calculatrice : 3+5=8 |
| Arrêt d'urgence | Le systray stoppe immédiatement toute activité |
---
## 11. Calendrier prévisionnel
| Phase | Durée | Livrable |
|---|---|---|
| Phase 1 — Agent minimal | 2-3 semaines | lea-agent.exe headless, capture + replay |
| Phase 2 — Systray + notifications | 1-2 semaines | Icône, menu, toasts |
| Phase 3 — Chat WebView | 1-2 semaines | Fenêtre de chat intégrée |
| Phase 4 — Parité complète | 1-2 semaines | Floutage, serveur capture, actions fichiers |
| Phase 5 — Polish (optionnel) | 1 semaine | Auto-update, signature, installeur |
| **Total** | **6-10 semaines** | **Parité complète** |
### Prérequis avant de commencer
1. **Installer la toolchain Rust** sur la machine de dev :
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add x86_64-pc-windows-msvc
cargo install cargo-xwin
```
2. **Préparer l'environnement de test** :
- VM Windows 10/11 ou accès au PC cible (192.168.1.11)
- Serveur streaming (:5005) accessible depuis le PC cible
3. **Créer le repo** :
```bash
cd ~/ai/rpa_vision_v3
mkdir -p rpa_agent_rust/src rpa_agent_rust/assets
cd rpa_agent_rust
cargo init
```
---
## 12. Conclusion
La réécriture de l'agent Windows en Rust est **techniquement faisable à 100%** et apporterait des bénéfices majeurs :
**Gains quantifiables** :
- Taille : 200 Mo → 6-10 Mo (**20x plus petit**)
- Démarrage : 5s → 300ms (**15x plus rapide**)
- Mémoire : 120 Mo → 30 Mo (**4x moins**)
- Installation : Script .bat + Python → Copier 1 fichier (**incomparable**)
**Gains qualitatifs** :
- Plus de problèmes de version Python sur les PC cibles
- Plus de virtualenv cassé, plus de `pip install` qui échoue
- Distribution par email, clé USB, ou téléchargement direct
- Fiabilité accrue (borrow checker, pas de GIL, pas de GC)
**Risques principaux** :
- Courbe d'apprentissage Rust (si pas de développeur Rust dans l'équipe)
- Temps de développement initial plus long qu'en Python (~6-10 semaines vs ~4 semaines)
- Maintenance de deux codebases pendant la transition
**Recommandation** : Procéder par phases. La Phase 1 (agent minimal) permet de valider la faisabilité en conditions réelles avec un investissement limité. Si elle réussit, les phases suivantes sont incrémentales et relativement simples.
---
*Document généré le 18 mars 2026.*