# Rapport Kazeia — État, architecture, performances **Date :** 2026-05-04 **Contexte :** Kazeia est une application Android de soutien thérapeutique conversationnel (psychologue virtuel) tournant **100 % on-device** sur OnePlus Pad 3, sans connectivité réseau requise et sans root utilisateur. --- ## 1. Synthèse exécutive ### Ce qui fonctionne en production - Pipeline complet **STT → LLM → TTS** opérationnel, multi-tour, avec cascade Thinker+Speaker. - Latence end-to-end utilisable en conversation : **3-5 s** entre la fin du PTT et le premier audio de réponse. - Stable mémoire après débloat ColorOS + désinstallation du watchdog OEM Athena. - Application 100 % autonome sur la tablette : aucun service réseau requis, modèles et runtime tous embarqués. - Stack figée et documentée (`/opt/Kazeia/FROZEN.md`). - Backup tablette complet et automatisable dans `/opt/Kazeia/kazeia-tablet-backup/`. ### Limites actuelles connues - Le **Thinker Guard-0.6B** produit des bullets de qualité analytique limitée (modèle conçu pour classification de safety, détourné vers analyse clinique). Le Speaker compense en pratique mais l'apport qualitatif de la cascade est moins net qu'espéré. - Le rendu UI de l'orbe (cycle couleurs pendant Thinking) demande encore validation visuelle utilisateur. - Conversation history non persisté entre sessions (à dessein actuellement). - Pas de fine-tuning thérapeutique français spécifique sur les modèles — on utilise Qwen3-4B générique pré-entraîné. --- ## 2. Plateforme matérielle | Composant | Spec | |---|---| | Tablette | OnePlus Pad 3 (OPD2415EEA) | | RAM | 16 GB LPDDR5X | | Stockage | 256 GB UFS | | SoC | Snapdragon 8 Elite | | CPU | Oryon (8 cœurs ARMv9) | | GPU | Adreno 830 | | **NPU/DSP** | **Hexagon V79 (HMX/HVX)** | | OS | Android 16, ColorOS post-2026-03-18 OTA | | Root | Aucun (user mode standard) | --- ## 3. Stack logicielle ``` ┌────────────────────────────────────────────────────────────────┐ │ Kazeia Android App (Kotlin) │ │ /data/app/.../com.kazeia │ └───────────────────┬─────────────────────────┬───────────────────┘ │ │ ┌───────────▼──────────┐ ┌───────────▼──────────┐ │ KazeiaService │ │ ChatActivity (UI) │ │ (state machine) │ │ AudioVisualizerView│ │ • PTT │ │ (orb HSV cycle) │ │ • Cascade │ └──────────────────────┘ │ • Pipeline state │ └─┬────────┬────────┬──┘ │ │ │ ┌──────▼─┐ ┌───▼────┐ ┌─▼──────┐ │ STT │ │ LLM │ │ TTS │ │ Engine │ │ Engine │ │ Engine │ └────────┘ └────────┘ └────────┘ ``` ### 3.1 STT — Whisper-Small NPU (QAIRT) - **Modèle** : `HfWhisper-Small` (12 layers, 12 heads) - **Format** : QAIRT context binary (Qualcomm SDK natif) - **Fichiers** : - `HfWhisperEncoder_qairt_context.bin` (210 MB) - `HfWhisperDecoder_qairt_context.bin` (361 MB) - **Backend** : Hexagon V79 via QAIRT runtime (`libQnnHtpV79Skel.so`) - **VAD** : RMS énergie inline (seuil 80, frames 100 ms) - **Mode** : continuous listening avec streaming Whisper pendant PTT - **Performance mesurée** : RTF 0.4 (transcription "Bonjour" ~640 ms total : mel 260 ms + encoder 115 ms + decoder 170 ms) ### 3.2 LLM — Cascade Option E v2 (in-process) **Architecture cascade** : 2 `LlmModule` ExecuTorch dans le **même process app**, partageant le `QnnBackendBundle` via le singleton `QnnBackendUnifiedRegistry` (donc 1 seul handle fastrpc DSP, pas de collision). ``` Process com.kazeia ├── Speaker Qwen3-4B (.pte 3.3 GB, ~4.4 GB ION) │ └── system prompt: SYS_KAZEIA + "Indices internes (NE PAS RÉPÉTER) : ..." └── Thinker Qwen3Guard-Gen-0.6B (.pte 700 MB, lazy first PTT) └── system prompt: SYS_THINKER (4 bullets émotion/besoin/approche/axe) ``` | Composant | Modèle | Format | Taille | Quantization | |---|---|---|---|---| | Speaker | Qwen/Qwen3-4B | ExecuTorch `.pte` QNN HTP V79 | 3.3 GB | Q4 W4A16 | | Thinker | Qwen/Qwen3Guard-Gen-0.6B | ExecuTorch `.pte` QNN HTP V79 | 700 MB | Q4 W4A16 | **Cascade sequence par tour utilisateur :** 1. STT → texte patient (transcription Whisper) 2. **Thinker pass** : `Guard-0.6B(SYS_THINKER + msg)` → 4 bullets cliniques 3. Bullets tronqués à 400 chars (sécurité prefill budget) 4. **Speaker pass** : `Qwen3-4B(SYS_KAZEIA + bullets en system, msg en user)` → réponse 1-2 phrases 5. Streaming TTS phrase par phrase au fur et à mesure du décodage ### 3.3 TTS — Qwen3-TTS ggml-cpu - **Modèle source** : Qwen3-TTS-0.6B-Base (clonage vocal) - **Pipeline 3 étapes**, toutes en CPU pur (NEON ARMv9, pas de DSP) : | Étape | Modèle | Fichier | Taille | Backend | |---|---|---|---|---| | Talker | transformer audio → tokens semantic | `talker_f16.gguf` | 851 MB | ggml-cpu | | Code Predictor (CP) | sem → 16 codebooks RVQ | `cp_q8_0.gguf` | 84 MB + 240 MB embeds | ggml-cpu | | Decoder | RVQ → mel → audio 24 kHz | `qwen3tts_decoder.gguf` | 325 MB | ggml-cpu (libtts_decoder_ggml) | - **Voice cloning** : prefix/suffix embeddings pré-extraits (10 voix : damien, elodie, jerome, richard, sid, zelda, didier, amir...) - **Streaming sentence-level** : audio joue dès la 1ère phrase pendant que LLM décode encore les suivantes (Opt TTS-B) - **RTF mesuré** : 0.96 sur tablette (chraac-llama fork) ### 3.4 Glue applicative | Composant | Rôle | |---|---| | `KazeiaService` (Kotlin) | State machine PTT + cascade + pipeline | | `ExecuTorchLlmEngine` | Wrapper Java→JNI sur `LlmModule` ExecuTorch | | `Qwen3TtsEngine` | Orchestration JNI Talker+CP+Decoder + voice clone | | `WhisperHybridEngine` | JNI QAIRT + mel extractor NEON | | `AudioVisualizerView` | Orbe Canvas, HSV cycle pendant Thinking | | `SentenceStreamer` | Découpage phrases pour streaming TTS | | `PipelineMetrics` | Instrumentation timings end-to-end | --- ## 4. Performances mesurées ### 4.1 Latences par étape (mesures réelles dans logcat) | Étape | Temps typique | Note | |---|---|---| | **STT Whisper-Small** | 460-680 ms | RTF 0.4 sur audio 1.7s | | ├ Mel extraction | 190-270 ms | NEON sur CPU | | ├ Encoder NPU | 115 ms | QAIRT V79 | | └ Decoder NPU | 75-300 ms | dépend du nb tokens | | **Thinker (Guard-0.6B)** | 446 ms | 22 tokens à 49 tok/s, TTFT 130 ms | | ├ Lazy load (1ère fois) | 1 088 ms | -844 MB MemAvailable | | └ Inference | 446 ms | 4 bullets, prompt ~180 tok | | **Speaker (Qwen3-4B)** | 1 000-2 200 ms | 14-46 tokens à 14-21 tok/s, TTFT 190 ms | | ├ resetContext | <1 ms | clear KV cache | | ├ Prefill (~150 tok) | 200 ms | hybrid mode AR=128 | | └ Decode | 800-2000 ms | 13.9-21 tok/s selon longueur | | **TTS Talker step** | ~25 ms | ggml-cpu NEON | | **TTS CP step** | ~43 ms | Q8_0, optimisé | | **TTS Decoder full** | RTF 0.96 | ggml-cpu, premier audio <1 s | ### 4.2 Latence end-to-end PTT → 1ᵉʳ audio ``` PTT release ─┬───── 100 ms finalize segment │ ╔═══ 600 ms ═══════ STT Whisper ─┐ ║ │ ╠═══ 450 ms ═══════ Thinker Guard-0.6B │ utilisateur ║ │ entend silence ╠═══ 500 ms ═══════ Speaker prefill + 1ère phrase │ ║ │ ╠═══ 800 ms ═══════ TTS 1ère phrase synthèse ─┘ ║ ▼ 1ᵉʳ audio joué (~ 2.5 s après PTT release) ``` **Total PTT release → 1ᵉʳ audio : ~ 2.5 s** (pour message court). **Total PTT release → réponse complète audio : ~ 4-6 s**. ### 4.3 Throughput LLM mesuré | Modèle | Hardware | Decode tok/s | TTFT | |---|---|---|---| | Qwen3-4B Speaker | Hexagon V79 | 14-21 | 190 ms | | Guard-0.6B Thinker | Hexagon V79 | **49** | 130 ms | | Whisper-Small | Hexagon V79 | n/a (encoder/decoder fusion) | 115+75 ms | --- ## 5. Profil mémoire ### 5.1 Tablette en idle (Speaker chargé, Thinker pas encore) | Métrique | Valeur | |---|---| | MemAvailable | 5.10 GB | | Cached (file cache) | 4.51 GB | | Process Kazeia RSS | 2.06 GB | | Process Kazeia PSS total | 2.14 GB | ### 5.2 Décomposition PSS Kazeia (idle) | Section | PSS | % | |---|---|---| | Native Heap (modèles, ION, native libs) | 1.65 GB | 77 % | | Java/Dalvik Heap | 362 MB | 17 % | | Graphics (EGL/GL) | 64 MB | 3 % | | Code (.so + .apk + .dex mmap) | 42 MB | 2 % | | Stack/divers | 26 MB | 1 % | | **TOTAL PSS** | **2.14 GB** | | ### 5.3 Pendant cascade et TTS | | Idle | Cascade Thinker | TTS pic | |---|---|---|---| | RSS Kazeia | 2.06 GB | ~2.9 GB | ~3.5 GB | | ION DSP cumulé | ~3.3 GB | ~5.0 GB | ~5.5 GB | | MemAvailable | 5.10 GB | 4.0 GB | 3.0 GB | Le pic TTS reste loin du seuil critique (~1 GB MemAvailable où Linux LMK commencerait à killer). **Stable**. ### 5.4 Tablet baseline (sans Kazeia) - **Stock fraîche** : MemAvailable ~3.5 GB (900+ processus OEM ColorOS) - **Après débloat + reboot** : MemAvailable ~11.1 GB → **+7.7 GB de marge** --- ## 6. Trajectoire — Ce qui a été fait ### Phase A : Pipeline initial (avril 2026) - Migration v1 du PoC Python/Flask vers app Android Kotlin native - Speaker via ExecuTorch QNN delegate (Qwen3-4B `.pte` hybrid mode) - STT Whisper-Small NPU via QAIRT - TTS Qwen3-TTS originalement en ExecuTorch `.pte` ### Phase B : Migration TTS vers ggml-cpu (avril-mai 2026) - Talker exporté en GGUF f16 (vs `.pte` ExecuTorch) - CP exporté en GGUF Q8_0 (84 MB vs 316 MB f32 vs 158 MB f16) - Decoder porté en C++ via libtts_decoder_ggml.so - Validation RTF < 1 sur tablette (chraac-llama fork) ### Phase C : Tablette stabilisation (mai 2026) - Mesure baseline ColorOS : ~3.5 GB MemAvailable seulement - **Débloat** sans root : 84 packages désinstallés via `pm uninstall --user 0` - **Athena watchdog** : désinstallation du package (kill ION quota 2 GB sur app) - Suppression de symlinks ⚠ SELinux ColorOS sans Magisk les bloque - Backup complet 25 GB dans `/opt/Kazeia/kazeia-tablet-backup/` - Factory reset + restore réussi ### Phase D : Cascade Thinker+Speaker (mai 2026) Tentatives successives : | Tentative | Approche | Résultat | |---|---|---| | 1 | Subprocess `qnn_llama_runner` daemon | ✗ Collision DSP fastrpc (2 sessions QNN HTP) | | 2 | Option E v1 : 2 LlmModule (Speaker 4B + Guard 4B) | ✗ OOM Linux LMK (8.8 GB ION cumulé) | | 3 | Option B : 2-pass sur même Speaker | ✓ Stable, qualité Thinker faible (Qwen3-4B générique) | | 4 | **Option E v2** : Speaker 4B + Guard-0.6B | **✓ Production actuelle** | Pour Option E v2 : export Qwen3Guard-Gen-0.6B HF → ExecuTorch `.pte` QNN HTP V79 en local via `executorch.examples.qualcomm.oss_scripts.llama` (registration ajoutée dans `__init__.py` + `decoder_constants.py`). ### Phase E : Robustesse runtime (mai 2026) - Fix VAD seuil RMS (150 → 80) après calibration mic post-reset - Fix PTT release : finalisation forcée du segment (sans ça, le LLM n'était jamais déclenché si bruit ambiant > seuil) - Fix `seq_len` ExecuTorch : passer 512 (max compilé .pte) au lieu de `maxNewTokens` (assertion `cur_pos+prompt < seq_len`) - Fix `cur_pos_` accumulation : appel `LlmModule.resetContext()` à chaque generate (sinon crash après quelques tours) - Fix `prefill()` overflow : truncation bullets à 400 chars (Guard-0.6B peut rambler et faire dépasser le budget prefill du Speaker) - Fix Speaker régurgitant les bullets : injection bullets dans **system prompt** (avec instruction « NE PAS RÉPÉTER ») au lieu du user prompt - Fix orbe visuelle : `Paint.shader = null` avant pose couleur dans `drawThinking` (sinon le shader posé par `drawIdle` masquait la couleur) ### Phase F : Backup APK + scripts (en continu) - `/opt/Kazeia/kazeia-tablet-backup/` (25 GB total) - APK actuelle (110 MB, libs natives custom bundlées) - Modèles TTS+STT (12 GB) - LLM `.pte` (12 GB inc. Guard-0.6B nouveau) - Voix de référence (130 MB) - `restore.sh` automatisé - `/opt/Kazeia/kazeia-debloat/` : `safelist.txt` + `debloat.sh` + `rebloat.sh` - `/opt/Kazeia/export_guard_06b.sh` : pipeline d'export Guard-0.6B reproducible --- ## 7. Architecture cascade détaillée (Option E v2) ``` ┌──────────────────────────────────────────────────────────────────┐ │ KazeiaService │ │ │ │ PTT release ───► startContinuousListening() drains buffer │ │ ► processSpeechInput / streaming Whisper │ │ ► text = "Comment ça va ?" │ │ ► processLlmResponse(text) │ │ │ │ ┌─ ensureThinkerEngine() ──┐ (lazy 1ère fois ~1.1 s) │ │ │ ▼ │ │ │ ┌─────────────────────┐ │ │ │ │ thinkerEngine │ │ │ │ │ ExecuTorchLlmEngine │ │ │ │ │ Guard-0.6B │ │ │ │ │ SYS_THINKER │ │ │ │ │ T=0.0, max 128 tok │ │ │ │ └──────────┬──────────┘ │ │ │ ▼ │ │ │ bullets (400 chars max) │ │ │ │ │ │ │ ┌─────────────────────────┴──────┐ │ │ │ │ speakerSystemPrompt = │ │ │ │ │ SYS_KAZEIA + │ │ │ │ │ "\n\nIndices d'analyse... │ │ │ │ │ (NE PAS CITER) :\n" + │ │ │ │ │ bullets │ │ │ │ └────────────┬────────────────────┘ │ │ │ ▼ │ │ │ ┌──────────────────────────┐ │ │ │ │ llm = ExecuTorchLlmEngine │ │ │ │ │ Speaker Qwen3-4B │ │ │ │ │ generateWithSystem() │ │ │ │ │ system=above, user=msg │ │ │ │ │ T=0.7, max 450 tok │ │ │ │ └──────────┬───────────────┘ │ │ │ ▼ token-by-token streaming │ │ │ ┌──────────────────────────┐ │ │ │ │ SentenceStreamer │ │ │ │ │ split sur . ! ? │ │ │ │ └──────────┬───────────────┘ │ │ │ ▼ phrase à phrase │ │ │ ┌──────────────────────────┐ │ │ │ │ Qwen3TtsEngine │ │ │ │ │ enqueueSentence() │ │ │ │ └──────────┬───────────────┘ │ │ │ ▼ │ │ └─────► AudioPlayback (24 kHz mono, AudioTrack) │ │ │ └──────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────┐ │ DSP Hexagon V79 │ │ • QAIRT (Whisper) │ │ • ExecuTorch QNN HTP │ │ (Speaker + Thinker │ │ partagent bundle) │ └─────────────────────────┘ │ ▼ ┌─────────────────────────┐ │ CPU NEON ARMv9 │ │ • TTS Talker (ggml) │ │ • TTS CP (ggml) │ │ • TTS Decoder (ggml) │ └─────────────────────────┘ ``` --- ## 8. Prompts système (production) ### Speaker ``` Tu es Kazeia, psychologue bienveillant. Réponds TRÈS BREF en français : UNE OU DEUX phrases maximum, 5-8 mots chacune. Validation émotionnelle simple. Pas de remplissage, pas d'explication, pas de conseil non sollicité. /no_think [ + injection en cascade : ] Indices d'analyse interne (NE PAS RÉPÉTER, NE PAS CITER, sert uniquement à orienter le ton de ta réponse) : - emotion : ... - besoin : ... - approche : ... - axe : ... ``` ### Thinker ``` Tu es un psychologue clinicien experimente qui ecoute attentivement ton patient. Avant de repondre, tu prepares une analyse silencieuse pour orienter ta reponse. Produis UNIQUEMENT le bloc clinique au format strict suivant : - emotion : - besoin : - approche : - axe : Pas d'introduction, pas d'explication. Juste les 4 lignes en francais. /no_think ``` --- ## 9. Risques connus & mitigations | Risque | Impact | Mitigation actuelle | Action future | |---|---|---|---| | Athena framework (system_server) tue Kazeia au-dessus de seuil RAM | Crash app | Débloat OEM réduit pression mémoire | Investiguer si killable plus profond | | LMK Linux kill si MemAvailable < 1 GB | Crash app | Marge 3 GB en pic actuel | Lazy-load STT (-600 MB) | | Speaker régurgite bullets cascade | Phrases bizarres lues par TTS | Bullets en system prompt + interdiction | Fine-tune/instruction tuning | | Thinker Guard-0.6B mal calibré pour analyse FR | Bullets imprécis | Speaker compense par sa robustesse | Évaluer Qwen3-1.7B en remplacement | | Whisper hallucinations sur silence | "que tu entends ce que je dis ici." apparaît au repos | Ignorer streaming results vides | Filtre RMS/longueur audio min | | Orbe ne change pas (avant fix) | UX dégradée | `Paint.shader = null` dans drawThinking | OK | | Symlinks `.pte` invisibles app | Mode perroquet | Fichiers réguliers (cp pas ln) | OK, mémoire feedback enregistrée | | OOM 2× ExecuTorch HTP 4B | Crash | Thinker = Guard-0.6B (5.1 GB ION total OK) | Plus petit Thinker possible | --- ## 10. Optimisations identifiées (memory profiling 2026-05-04) | Optim | Effort | Gain idle | Gain pic | Risque | Recommandation | |---|---|---|---|---|---| | Strip jniLibs Hexagon non-V79 | 30 min | 50 MB code | 50 MB | Bas | **Tier 1** | | Lazy-load Whisper STT | 1 h | 600 MB | 0 | Moyen (+0.5s 1ᵉʳ PTT) | **Tier 1** | | Trim Java heap (logs StateFlow) | 1 h | 100-200 MB | 100 MB | Bas | **Tier 1** | | Release Thinker entre tours | 2 h | 0 | -700 MB | Moyen (+1.5s 2ᵉ PTT) | Tier 2 | | Talker GGUF Q8_0 (vs f16) | 1 j | 400 MB | 400 MB | Haut (qualité audio) | Tier 2 | | Decoder Q4 sélectif | 2-3 j | 200 MB | 200 MB | Haut | Tier 3 | | Re-export Speaker plus petit | 5-10 j | 500-1 000 MB | similar | Très haut | Tier 3 | --- ## 11. Roadmap ### Court terme (1-2 semaines) - [ ] Appliquer Tier 1 mémoire (strip libs + lazy STT + trim heap) - [ ] Évaluation qualitative cascade FR sur corpus thérapeutique 20+ prompts de référence - [ ] Comparer Guard-0.6B vs Qwen3-1.7B comme Thinker (export et bench) - [ ] Documenter procédure réinstallation tablette neuve (incl. débloat) ### Moyen terme (1-2 mois) - [ ] Rewrite Kazeia Phase 1-5 (architecture propre Kotlin/JNI) - [ ] Voice clone .bin generator on-device (POC) - [ ] Persistance conversation history (Room database) - [ ] UI polish (mode conversation libre vs PTT) - [ ] Voice commands étendus ### Long terme (3+ mois) - [ ] Fine-tuning thérapeutique français spécifique (LoRA) - [ ] Migration runtime briques (POC dans `/opt/htp-runtime/`) — alternative à ExecuTorch/QNN sur le long terme pour autonomie - [ ] Évaluation clinique avec professionnels santé mentale --- ## 12. Références — Fichiers clés ### Code source app - `/opt/Kazeia/kazeia-android/` — projet Gradle 8.12 / NDK r27d / API 36 - `app/src/main/java/com/kazeia/service/KazeiaService.kt` — orchestrateur - `app/src/main/java/com/kazeia/llm/ExecuTorchLlmEngine.kt` — wrapper LLM - `app/src/main/java/com/kazeia/tts/Qwen3TtsEngine.kt` — TTS - `app/src/main/java/com/kazeia/stt/WhisperHybridEngine.kt` — STT - `app/src/main/java/com/kazeia/ui/AudioVisualizerView.kt` — orbe - `app/src/main/jni/` — bridges JNI custom (libtts_*, libmel_extractor) ### Scripts et outils - `/opt/Kazeia/export_guard_06b.sh` — export Qwen3Guard-Gen-0.6B → `.pte` - `/opt/Kazeia/kazeia-debloat/` — débloat tablette - `/opt/Kazeia/kazeia-tablet-backup/` — backup + restore - `/opt/Kazeia/executorch/examples/qualcomm/oss_scripts/llama/` — pipeline export QNN HTP ### Modèles tablette - `/data/local/tmp/kazeia-et/hybrid_llama_qnn_4b.pte` — Speaker - `/data/local/tmp/kazeia-et/hybrid_llama_qnn_guard06b.pte` — Thinker (nouveau) - `/data/local/tmp/kazeia-et/tokenizer.json` / `tokenizer_guard06b.json` - `/data/local/tmp/kazeia/models/` — Talker, CP, Decoder GGUF, Whisper QAIRT - `/data/local/tmp/kazeia/voix/` — voix de référence ### Documents de référence - `/opt/Kazeia/FROZEN.md` — stack figée - `/opt/Kazeia/kazeia-tts-perf-validated/` — TTS RTF<1 validé - `/opt/Kazeia/kazeia-stt-perf-validated/` — STT RTF 0.4 validé - `/opt/Kazeia/kazeia-debloat/README.md` — procédure tablette ### Mémoire persistante (sessions Claude Code) - `/home/alf/.claude/projects/-opt-Kazeia/memory/MEMORY.md` — index - 50+ entrées documentant chaque décision technique majeure --- ## 13. Conclusion Kazeia tourne **end-to-end stable** sur OnePlus Pad 3 sans connexion internet, sans root utilisateur, avec des latences acceptables pour une conversation thérapeutique (3-5 s entre tours). L'architecture cascade Speaker+Thinker in-process via ExecuTorch QNN HTP est validée et reproductible. Les principaux **blocages historiques** ont été résolus : DSP contention, OOM, SELinux symlinks, watchdog OEM, accumulation KV cache. La stack est documentée et figée, avec backup + procédure de restore complets. Les **axes d'amélioration restants** sont incrémentaux (mémoire, qualité Thinker, UX) plutôt que structurels. Le pipeline est prêt pour **évaluation clinique** sur corpus de référence en français. --- *Rapport généré 2026-05-04. Auteur : Richard Loyer + assistance IA.*