Kazeia-engine/dist/HANDOFF.md

6.8 KiB
Raw Blame History

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é).

Générique : fait tourner N'IMPORTE QUEL LLM (auto-détection d'archi au load)

Le JNI lit general.architecture et choisit le chemin :

  • qwen35 / qwen3next (hybride GDN) → option C : prefill HTP → transfert KV → decode CPU (le decode GDN sur HTP est lent, donc on le garde sur CPU). Validé (3.5).
  • tout le reste (qwen3 dense, etc.) → CPU pur : chemin universel, marche pour tout modèle llama.cpp, jamais de crash. (Le dense tournait déjà sur HTP en contexte unique 98/11.4 — voir note dense ci-dessous — mais le decode dense HTP ≈ CPU, donc CPU suffit et reste sûr pour les archis non validées.) Les deux modèles cibles (dense + 3.5) tournent. Validé via jni/test_jni_native.cpp sur les deux.

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, validé end-to-end (27/05)

Le JNI implémente C (prefill HTP / decode CPU). Validé bout-en-bout sur device via jni/test_jni_native.cpp (dlopen + JNIEnv mock → load/generate/generateRaw/free : sortie FR cohérente, mémoire conversationnelle OK) et jni/dual_ctx_mt.cpp (3 tours, prefill HTP 103-144 t/s, decode CPU ~7-9).

  • load() : pose GGML_HEXAGON_GDN_PREFILL=1 (le fallback CPU du conv1d multi-token est dans le backend, pas d'OPFILTER), charge le modèle 2× : instance HTP (ngl99, device HTP0, t8) + instance CPU (ngl0, t4). ~4.7 GB.
  • API : generate(sys,usr,max) (mono-tour) OU generateRaw(prompt,max) (multi-tour, prompt pré-formaté). EngineLlmEngine.kt fournit ChatSession (accumule l'historique + construit le ChatML validé → l'app n'a pas à connaître le template). Cœur : prefill HTP → llama_state_seq_get/set_data (transfert KV) → decode CPU. Sans état entre appels (re-prefill de l'historique complet sur HTP, rapide).
  • KV f16, flash_attn ON, thinking OFF (<think></think> vide), greedy. API : load/generate/generateRaw/reset/free.
  • lib/libkazeia_engine.so à jour (option C, 5 symboles JNI, SHARED) + libggml-hexagon.so (fix SSM_CONV) + libggml-htp-v79.so à jour. Prebuilts utilisables tels quels (NDK r27d) ; rebuild depuis jni/ dans ton build app recommandé pour garantir l'ABI/STL (CMakeLists.txt fourni, linke llama+ggml+ggml-base+log).

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 : CORRIGÉ (fix intégré au backend). Cause du charabia localisée à une seule op, SSM_CONV (conv1d), cassée sur HTP en multi-token (prefill) pour certains d_inner (1024/2048 ; 1536 OK) — bug données/marshalling côté DSP (HVX ET scalaire HTP faux, ggml-cpu correct), non localisé à la ligne. Fix livré dans libggml-hexagon.so (ggml_hexagon_supported_ssm_conv : n_t>1 → CPU) : le conv1d prefill tourne sur ggml-cpu (correct, bon marché), le reste du prefill sur HTP, decode conv sur HTP (n_t=1). Plus besoin de OPFILTER. Validé : oracle SSM_CONV 18/18, cli -dev HTP0 cohérent, multi-tour cohérent+mémoire. Mesuré : prefill HTP 110-180 t/s (×8-13 vs CPU 14), sortie correcte. Libs prebuilts à jour dans lib/. Trois options (le JNI implémente C) :
    • A 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/*.soapp/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.