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 :
- STT → texte patient (transcription Whisper)
- Thinker pass :
Guard-0.6B(SYS_THINKER + msg) → 4 bullets cliniques
- Bullets tronqués à 400 chars (sécurité prefill budget)
- Speaker pass :
Qwen3-4B(SYS_KAZEIA + bullets en system, msg en user) → réponse 1-2 phrases
- 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 : <emotion principale ressentie, 5 mots max>
- besoin : <besoin sous-jacent du patient, 5 mots max>
- approche : <posture therapeutique adaptee, 5 mots max>
- axe : <point cle a valider ou explorer, 5 mots max>
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)
Moyen terme (1-2 mois)
Long terme (3+ mois)
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.