diff --git a/docs/rag_corpus_seed/README.md b/docs/rag_corpus_seed/README.md new file mode 100644 index 0000000..6ebc35b --- /dev/null +++ b/docs/rag_corpus_seed/README.md @@ -0,0 +1,9 @@ +# Corpus seed RAG (psychoéducation FR) + +Corpus de DÉPART pour le RAG Kazeia — contenu standard et sûr (hygiène du sommeil, +respiration, ancrage 5-4-3-2-1, activation comportementale, rumination, solitude). +**À curer/étendre par le clinicien.** En prod il est distribué via le catalogue +Nextcloud et déposé sous `.../kazeia/rag_corpus/` ; l'app l'ingère au 1er démarrage +si la base RAG est vide (ou via l'intent `rag_ingest`). + +Modèle d'embedding : `multilingual-e5-small` (384-dim, préfixes query:/passage:). diff --git a/docs/rag_corpus_seed/ancrage.txt b/docs/rag_corpus_seed/ancrage.txt new file mode 100644 index 0000000..c84ab9a --- /dev/null +++ b/docs/rag_corpus_seed/ancrage.txt @@ -0,0 +1 @@ +Technique d'ancrage 5-4-3-2-1. Lorsqu'une émotion devient envahissante ou en cas d'attaque de panique, ramener son attention au présent aide à reprendre pied. Nommez cinq choses que vous voyez autour de vous, quatre choses que vous pouvez toucher, trois sons que vous entendez, deux odeurs que vous percevez, et une chose que vous pouvez goûter. Cet exercice d'ancrage sensoriel interrompt le cycle des pensées anxieuses en reconnectant le corps à l'environnement immédiat. diff --git a/docs/rag_corpus_seed/humeur.txt b/docs/rag_corpus_seed/humeur.txt new file mode 100644 index 0000000..9401731 --- /dev/null +++ b/docs/rag_corpus_seed/humeur.txt @@ -0,0 +1 @@ +Activation comportementale et humeur basse. Quand le moral est bas, l'envie de faire des choses diminue, ce qui réduit encore les occasions de ressentir du plaisir : un cercle vicieux s'installe. L'activation comportementale consiste à programmer de petites activités agréables ou valorisantes, même sans motivation au départ. Commencez petit : une courte marche, un appel à un proche, une tâche simple menée à son terme. L'action précède souvent la motivation, et non l'inverse. Noter ce qui a procuré un peu de satisfaction aide à repérer ce qui fait du bien. diff --git a/docs/rag_corpus_seed/respiration.txt b/docs/rag_corpus_seed/respiration.txt new file mode 100644 index 0000000..e405424 --- /dev/null +++ b/docs/rag_corpus_seed/respiration.txt @@ -0,0 +1 @@ +Respiration et apaisement de l'anxiété. La respiration lente et profonde active le système nerveux parasympathique et réduit la tension physique. Un exercice simple : inspirez doucement par le nez pendant quatre secondes, puis expirez lentement par la bouche pendant six secondes. L'expiration plus longue que l'inspiration est ce qui calme le rythme cardiaque. Répétez ce cycle pendant quelques minutes, en posant une main sur le ventre pour sentir le souffle. Cet exercice peut se pratiquer discrètement, à tout moment d'une montée d'angoisse. diff --git a/docs/rag_corpus_seed/rumination.txt b/docs/rag_corpus_seed/rumination.txt new file mode 100644 index 0000000..52a6657 --- /dev/null +++ b/docs/rag_corpus_seed/rumination.txt @@ -0,0 +1 @@ +Rumination mentale. Ruminer, c'est tourner en boucle sur des pensées négatives sans parvenir à une solution. Cela amplifie la détresse sans rien résoudre. Pour s'en dégager, il peut aider de fixer un court moment dédié aux soucis dans la journée, puis de reporter les pensées qui surgissent en dehors de ce créneau. Distinguer ce qui est sous votre contrôle de ce qui ne l'est pas oriente l'énergie vers l'action possible. Prendre du recul en observant la pensée comme un événement mental passager, plutôt que comme une vérité, réduit son emprise. diff --git a/docs/rag_corpus_seed/solitude.txt b/docs/rag_corpus_seed/solitude.txt new file mode 100644 index 0000000..517406d --- /dev/null +++ b/docs/rag_corpus_seed/solitude.txt @@ -0,0 +1 @@ +Sentiment de solitude. Se sentir seul est une expérience douloureuse et fréquente, qui ne reflète pas une valeur personnelle. Le lien social, même bref et modeste, nourrit le bien-être : un message, un sourire échangé, une courte conversation comptent. Reprendre contact progressivement, à son rythme, vaut mieux que d'attendre de se sentir prêt. Les activités partagées autour d'un intérêt commun créent du lien sans la pression de la conversation directe. Tenir un journal de trois moments positifs par jour aide à rééquilibrer l'attention souvent captée par le manque. diff --git a/docs/rag_corpus_seed/sommeil.txt b/docs/rag_corpus_seed/sommeil.txt new file mode 100644 index 0000000..72ec5e8 --- /dev/null +++ b/docs/rag_corpus_seed/sommeil.txt @@ -0,0 +1 @@ +Hygiène du sommeil. Pour favoriser l'endormissement, il est utile de se coucher et se lever à des horaires réguliers, même le week-end. Évitez les écrans lumineux dans l'heure qui précède le coucher, car la lumière bleue retarde la sécrétion de mélatonine. La chambre doit rester fraîche, sombre et silencieuse. La caféine et l'alcool perturbent le sommeil profond : limitez-les en fin de journée. Si le sommeil ne vient pas après vingt minutes, mieux vaut se lever et faire une activité calme plutôt que de rester au lit à ruminer. 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 70ffe6a..e6be62e 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 @@ -59,6 +59,22 @@ class Rag( return n } + /** Ingestion de tous les .txt/.md d'un dossier (source = nom de fichier). */ + @Synchronized + fun ingestDir(dir: String): Int { + val d = java.io.File(dir) + if (!d.isDirectory) { log("ingestDir: '$dir' absent"); return 0 } + val files = d.listFiles { f -> f.isFile && (f.name.endsWith(".txt") || f.name.endsWith(".md")) } + ?.sortedBy { it.name } ?: emptyList() + var total = 0 + for (f in files) { + val txt = try { f.readText() } catch (e: Exception) { log("ingestDir: lecture ${f.name} échec"); continue } + total += ingest(f.nameWithoutExtension, txt) + } + log("ingestDir '$dir': ${files.size} fichiers, $total chunks") + return total + } + /** * Récupère le contexte pertinent pour [query] et le formate en bloc prêt à * injecter, ou null si rien au-dessus du seuil. Le seuil évite d'injecter du 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 c3e0bd2..67f948a 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 @@ -660,6 +660,19 @@ Pas d'introduction, pas d'explication. Juste les 4 lignes en francais. /no_think } catch (e: Exception) { log("RAG real test error: ${e.message}"); e.printStackTrace() } } } + intent?.getStringExtra("rag_ingest")?.let { dirArg -> + // (Ré)ingestion du corpus dans l'instance RAG live : clear + ingestDir. + // dirArg vide -> dossier corpus par défaut. Brique de l'app admin future. + val dir = dirArg.ifBlank { "${KazeiaApplication.MODELS_DIR}/../rag_corpus" } + log("RAG ingest: $dir") + serviceScope.launch { + val r = rag + if (r == null || !r.isReady) { log("RAG ingest: RAG non prêt (ragEnabled off ?)"); return@launch } + r.clear() + val n = r.ingestDir(dir) + log("RAG ingest terminé: $n chunks (corpus=${r.count()})") + } + } 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 @@ -834,6 +847,9 @@ Pas d'introduction, pas d'explication. Juste les 4 lignes en francais. /no_think val emb = com.kazeia.rag.EngineEmbedder(embModel, nThreads = 4, pooling = -1, queryPrefix = "query: ", passagePrefix = "passage: ", log = { m -> log(m) }) com.kazeia.rag.Rag(this@KazeiaService, emb, "e5-small") { m -> log(m) }.also { + // Auto-ingestion du corpus au 1er démarrage (base vide). Le corpus + // (catalogue Nextcloud en prod) est déposé sous .../kazeia/rag_corpus/. + if (it.isReady && it.count() == 0) it.ingestDir("${KazeiaApplication.MODELS_DIR}/../rag_corpus") it.loadIndex(); log("[RAG] activé (ready=${it.isReady}, chunks=${it.count()})") } } catch (e: Throwable) { log("[RAG] init échec: ${e.message}"); null } @@ -1747,7 +1763,9 @@ Pas d'introduction, pas d'explication. Juste les 4 lignes en francais. /no_think // JAMAIS la gestion de crise. rag?.takeIf { it.isReady }?.let { r -> try { - r.retrieve(patientMessage)?.let { ctx -> + // Seuil ~0.82 calé sur e5 (cosinus de base ~0.80 ; pertinent ~0.88) pour + // ne PAS injecter de hors-sujet. k=3. À tuner sur corpus réel (logs "top="). + r.retrieve(patientMessage, k = 3, threshold = 0.82f)?.let { ctx -> prompt = "$ctx\n\n$prompt" log("[RAG] contexte injecté (${ctx.length} chars)") }