# Kazeia-Engine — handoff intégration (MAJ 26/05/2026) Point d'entrée unique. Remplace le LLM ExecuTorch/Genie par llama.cpp (fork ql) + Hexagon. GGUF, pas de `.pte`. STT reste ORT-QAIRT (inchangé). ## Décisions figées cette session - **Modèle Speaker = Qwen3.5-4B en variante `q35-lmq4.gguf`** (embeds en Q4 au lieu du Q6_K par défaut → 2.38 GB, decode +5%, qualité ≈ Q4_0 mesurée). Choisi sur l'éval qualité (`../eval/VERDICT.md`) : gagne le SAFETY (cite 3114/15) et la majorité du SUPPORT vs le dense. - **9B écarté** (decode ~4-5 t/s réel, trop lent ; pas de gain qualité justifiant). Cascade éventuelle = Guard-4B (thinker) + 4B (speaker), pas 9B. - **Kernel GDN = chantier clos** : son calcul n'est PAS le goulot (cf RAPPORT_RD §7). Ne pas y retoucher pour la perf. Decode au plafond BW CPU (~25 GB/s). ## État du bridge JNI (`jni/kazeia_engine_jni.cpp`) — OPTION C câblée (27/05) Le JNI implémente **C** (prefill HTP / decode CPU), validé multi-tour (`jni/dual_ctx_mt.cpp` : 3 tours cohérents + mémoire conversationnelle OK, prefill HTP 103-144 t/s, decode CPU ~7-9). - `load()` : pose `GGML_HEXAGON_GDN_PREFILL=1` + `GGML_HEXAGON_OPFILTER=SSM_CONV` (avant init backend), charge le modèle **2×** : instance HTP (ngl99, device HTP0, t8) + instance CPU (ngl0, t4). ~4.7 GB. - `generate()` : prefill du prompt sur HTP → `llama_state_seq_get/set_data` transfère le KV → decode sur CPU. Sans état entre appels (l'app passe l'historique complet dans le prompt → re-prefill HTP, rapide). - KV **f16**, flash_attn ON, thinking OFF (`` vide), greedy. API inchangée : `load/generate/reset/free`. - ⚠ **Rebuild `libkazeia_engine.so` requis** (le .so livré est l'ancienne version CPU-only/q8_0). `CMakeLists.txt` inchangé (linke llama+ggml+ggml-base+log) ; compile+linke vérifié (4 symboles JNI OK). ## Décisions (vérifiées 27/05, batterie 90%, device froid) 1. **KV = f16 (RÉSOLU, corrigé dans le JNI).** Mesuré : decode q35-lmq4 f16=**10.9** vs q8_0=**6.5** (−40%, idem à d=512). Le déquant KV en flash-attn coûte plus que le BW épargné. `type_k/v` repassé en `GGML_TYPE_F16`. → **rebuild `libkazeia_engine.so` avant de shipper** (le .so livré contient encore l'ancien q8_0). q8_0 seulement si OOM KV en très long contexte. 2. **Prefill HTP : RÉCUPÉRABLE via `GGML_HEXAGON_OPFILTER=SSM_CONV`.** Cause du charabia localisée : **une seule op, SSM_CONV (conv1d), est cassée sur HTP en multi-token (prefill) pour d_inner=1024/2048** (oracle FAIL ERR~1.3 ; decode n=1 OK ; bug côté backend hexagon, pas le calcul — les 2 chemins HTP échouent, ggml-cpu est correct). En forçant cette op sur CPU (`OPFILTER=SSM_CONV`), le reste du prefill tourne sur HTP et la **sortie redevient cohérente** : « Je comprends que cette rumination nocturne… ». Mesuré : **prefill HTP 181 t/s** (×13 vs CPU 14) avec sortie correcte. Trois options d'intégration : - **A (livrée) CPU-only** : prefill 14, decode 10.9, 2.4 GB. Simple. - **B mono-contexte HTP + OPFILTER** : prefill **181**, decode HTP **6.4**, 2.4 GB (+ION). Gros gain prefill, decode + lent, aucune RAM en plus, peu de code (ngl99 + env). **Recommandé** pour prompts longs/multi-tour. - **C dual-contexte (prefill HTP+OPFILTER → transfert KV → decode CPU)** : prefill 181, decode **10.9**, **+2.3 GB** RAM (2e instance). Optimal mais + complexe. Harness prouvé : `jni/dual_ctx.cpp`. TODO propre : corriger SSM_CONV HTP (gate `test-backend-ops -o SSM_CONV` = 45/45) → enlèverait l'OPFILTER. ## Perf (sains, 27/05, device froid) | | prefill | decode | RAM | |---|--:|--:|--:| | q35-lmq4 — **C: HTP-prefill / CPU-decode (CÂBLÉ JNI)** | **103-180** (HTP, monte avec ctx) | **~7-9** (CPU, profondeur) | 4.7 GB | | q35-lmq4 — A: CPU-only | 14 | 10.9 (ctx court) | 2.4 GB | | q35-lmq4 — B: HTP+OPFILTER mono-ctx | 181 | 6.4 (HTP) | 2.4 GB +ION | | q35-lmq4 — HTP sans OPFILTER | 189 *(sortie CASSÉE: SSM_CONV)* | — | — | Multi-tour mesuré (C) : tour1 63tok→prefill 103/decode 9 ; tour3 268tok→prefill 144/decode 6.7 ; mémoire OK. Decode CPU baisse avec la profondeur de contexte (normal). Un tour ~80 tok ≈ prefill 1-3 s + decode 9-12 s. Decode = ce que l'utilisateur ressent (10.9, OK). Prefill CPU 14 t/s → prompt 200 tok ≈ 14 s ; garder le system prompt + l'historique courts. Le prefill HTP rapide existe mais sort du charabia (cf décision 2). ## Système (prompt) — `system_fr.txt` fourni Inclut les garde-fous (3114, pas de prescription) + 2 correctifs issus de l'éval : tutoiement constant, et « ne présume pas du pire, fais préciser » (le modèle lisait « le départ de ma fille » comme un décès). À passer tel quel en `sys` de `generate()`. ## Paquet à intégrer - `lib/*.so` → `app/src/main/jniLibs/arm64-v8a/` (libkazeia_engine.so **statique** + ggml/htp ; htp-v79 = celui de cette session, fp16 derrière env `KZ_F16` OFF par défaut = comportement inchangé). - `jni/kazeia_engine_jni.cpp` + `jni/EngineLlmEngine.kt` + `include/` → projet app. - `q35-lmq4.gguf` → external storage (2.38 GB, hors git ; le pousser ou le reproduire, cf MODELS.md). - `./package.sh` réassemble `lib/` depuis le build `ql/b` + recopie le prompt. ## Détails INTEGRATION.md (build/CMake), MODELS.md (modèle + recette), PERF.md (mesures+leviers morts), PITFALLS.md (pièges vécus), STATUS.md (limites). Verdict qualité : `../eval/VERDICT.md`.