From 95a7a253350d6056e76cce925f73eaf435a2bea7 Mon Sep 17 00:00:00 2001 From: Kazeia Team Date: Wed, 10 Jun 2026 12:02:44 +0200 Subject: [PATCH] =?UTF-8?q?feat(rag):=20valider=20EngineEmbedder=20e5=20in?= =?UTF-8?q?-app=20(E2E=20r=C3=A9el)=20+=20pr=C3=A9fixe=20passage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le dev kazeia-engine a livré le bridge CPU-only avec embedText (commit engine 655d65a) + un GGUF multilingual-e5-small fonctionnel (converter patché pour le tokenizer XLM-R/unigram). Bridge swappé dans jniLibs (ABI cohérente : libllama/ libggml déjà = build CPU-only du 2 juin). - Rag.ingest : les passages portent désormais le préfixe "passage: " (e5), en symétrie du "query: " de retrieve (EngineEmbedder.embedPassage). - Intent `rag_real_test` : valide le VRAI EngineEmbedder (e5) in-app. Validé device (CPU-only, untrusted_app) : embedder ready=true dim=384, AUCUN crash SELinux/fastrpc (la .so ne tire plus hexagon), retrieval FR correct — requête sommeil → doc insomnie en tête (top=0.88). embedText fonctionne in-app end-to-end. Le .so (gitignored) est un artefact build, hors git. Co-Authored-By: Claude Opus 4.8 --- .../app/src/main/java/com/kazeia/rag/Rag.kt | 5 +++- .../java/com/kazeia/service/KazeiaService.kt | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/kazeia-android/app/src/main/java/com/kazeia/rag/Rag.kt b/kazeia-android/app/src/main/java/com/kazeia/rag/Rag.kt index 23bcf81..70ffe6a 100644 --- a/kazeia-android/app/src/main/java/com/kazeia/rag/Rag.kt +++ b/kazeia-android/app/src/main/java/com/kazeia/rag/Rag.kt @@ -48,7 +48,10 @@ class Rag( val chunks = Chunker.chunk(rawText, source) var n = 0 for ((i, ch) in chunks.withIndex()) { - val v = embedder.embed(ch) ?: continue + // e5 : les passages doivent porter le préfixe "passage: " (symétrique au + // "query: " de retrieve). EngineEmbedder.embedPassage le gère ; le fallback + // FakeEmbedder embed() sans préfixe (symétrique aussi). + val v = (embedder as? EngineEmbedder)?.embedPassage(ch) ?: embedder.embed(ch) ?: continue db.insert(source, i, ch, modelTag, v); n++ } log("ingest '$source': $n chunks") diff --git a/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt b/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt index 8b16022..c3e0bd2 100644 --- a/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt +++ b/kazeia-android/app/src/main/java/com/kazeia/service/KazeiaService.kt @@ -637,6 +637,29 @@ Pas d'introduction, pas d'explication. Juste les 4 lignes en francais. /no_think } catch (e: Exception) { log("RAG test error: ${e.message}"); e.printStackTrace() } } } + intent?.getStringExtra("rag_real_test")?.let { query -> + // Valide le VRAI EngineEmbedder in-app (e5-small via kazeia-engine CPU-only) : + // prouve loadEmbedder/embedText sans crash SELinux + retrieval FR réel. + log("RAG real test: '$query'") + serviceScope.launch { + try { + val embModel = "${KazeiaApplication.MODELS_DIR}/embed-e5-small.gguf" + val emb = com.kazeia.rag.EngineEmbedder(embModel, nThreads = 4, pooling = -1, + queryPrefix = "query: ", passagePrefix = "passage: ", log = { m -> log(m) }) + log("RAG real: embedder ready=${emb.isReady} dim=${emb.dim}") + if (!emb.isReady) { log("RAG real: embedder NON prêt — abort"); return@launch } + val rag = com.kazeia.rag.Rag(this@KazeiaService, emb, "e5-small") { m -> log(m) } + rag.clear() + rag.ingest("insomnie", "L'insomnie est un trouble du sommeil fréquent. Pour mieux dormir, évitez les écrans le soir et gardez une heure de coucher régulière.") + rag.ingest("solitude", "Le sentiment de solitude peut être douloureux. Renouer un petit lien social, même bref, aide beaucoup.") + rag.ingest("respiration", "La respiration lente et profonde calme l'anxiété : inspirez quatre secondes, expirez six secondes, répétez.") + rag.loadIndex() + val ctx = rag.retrieve(query) + log("RAG real RESULT (corpus=${rag.count()}):\n${ctx ?: "(rien au-dessus du seuil)"}") + emb.close() + } catch (e: Exception) { log("RAG real test error: ${e.message}"); e.printStackTrace() } + } + } if (intent?.hasExtra("cfg_set") == true) { // Maintenance/bench : persiste engine+model+prompt sans passer par le // ContentProvider CLI (qui casse sur les valeurs contenant ':'). Les