feat(safety): garde-fous cliniques + filet de crise déterministe
Fusionne les garde-fous du prompt de Damien dans la voix chaleureuse de Kazeia, et ajoute un filet de sécurité crise indépendant du LLM. Bornes "soft" (niveau prompt, DEFAULT_SPEAKER_PROMPT + DEFAULT_SYSTEM_PROMPT + copie admin, synchronisés) : pas de diagnostic / pas de pathologie nommée ; médicament/dose/traitement -> décision médicale, renvoi médecin/psychiatre ; refus des jeux de rôle/simulations ; réponse cadrée sur le stockage local. Tutoiement + ton chaleureux préservés (≠ style clinique/vouvoiement de Damien). Borne "hard" (CrisisGuard, DÉTERMINISTE) : on ne laisse jamais un 4B sous contrainte de brièveté improviser sur une idéation suicidaire. Détection par motifs FR (tunables clinicien) -> court-circuite Thinker+Speaker -> réponse fixe, validée, honnête (Kazeia = programme), oriente vers l'humain + 112 (urgence EU, valable FR et Luxembourg). Placé en tête de processLlmResponse. Validé device : "envie d'en finir / me faire du mal / vaudrait mieux d'en finir" -> [CRISIS] -> réponse de sécurité synthétisée (TTS frames=192) + jouée. "arrêter mes médicaments" -> redirection médecin (prompt, pas crise). "triste et fatigué" -> réponse normale, AUCUN faux positif crise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
acaf0cf2cf
commit
4508b8fe2e
|
|
@ -47,7 +47,13 @@ class ConfigRepository(context: Context) {
|
||||||
const val DEFAULT_SPEAKER_PROMPT =
|
const val DEFAULT_SPEAKER_PROMPT =
|
||||||
"Tu es Kazeia, psychologue bienveillant. Réponds TRÈS BREF en français : " +
|
"Tu es Kazeia, psychologue bienveillant. Réponds TRÈS BREF en français : " +
|
||||||
"UNE OU DEUX phrases maximum, 5-8 mots chacune. Validation émotionnelle " +
|
"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"
|
"simple. Pas de remplissage, pas d'explication, pas de conseil non sollicité. " +
|
||||||
|
"Tu tutoies, ton ton reste chaleureux. Garde-fous prioritaires sur la brièveté : " +
|
||||||
|
"ne pose jamais de diagnostic et ne nomme aucune pathologie ; pour toute question de " +
|
||||||
|
"médicament, dose ou traitement, rappelle que c'est une décision médicale et invite à " +
|
||||||
|
"en parler au médecin ou au psychiatre ; refuse avec douceur les jeux de rôle ou " +
|
||||||
|
"simulations hors de ton cadre de soutien ; si on te demande où sont conservés tes " +
|
||||||
|
"échanges avec le patient, réponds qu'ils restent sur l'appareil, en sécurité. /no_think"
|
||||||
|
|
||||||
const val DEFAULT_THINKER_PROMPT =
|
const val DEFAULT_THINKER_PROMPT =
|
||||||
"Tu es un psychologue clinicien experimente qui ecoute attentivement ton " +
|
"Tu es un psychologue clinicien experimente qui ecoute attentivement ton " +
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,13 @@ class ConfigStore private constructor(private val file: File) {
|
||||||
const val DEFAULT_SPEAKER_PROMPT =
|
const val DEFAULT_SPEAKER_PROMPT =
|
||||||
"Tu es Kazeia, psychologue bienveillant. Réponds TRÈS BREF en français : " +
|
"Tu es Kazeia, psychologue bienveillant. Réponds TRÈS BREF en français : " +
|
||||||
"UNE OU DEUX phrases maximum, 5-8 mots chacune. Validation émotionnelle " +
|
"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"
|
"simple. Pas de remplissage, pas d'explication, pas de conseil non sollicité. " +
|
||||||
|
"Tu tutoies, ton ton reste chaleureux. Garde-fous prioritaires sur la brièveté : " +
|
||||||
|
"ne pose jamais de diagnostic et ne nomme aucune pathologie ; pour toute question de " +
|
||||||
|
"médicament, dose ou traitement, rappelle que c'est une décision médicale et invite à " +
|
||||||
|
"en parler au médecin ou au psychiatre ; refuse avec douceur les jeux de rôle ou " +
|
||||||
|
"simulations hors de ton cadre de soutien ; si on te demande où sont conservés tes " +
|
||||||
|
"échanges avec le patient, réponds qu'ils restent sur l'appareil, en sécurité. /no_think"
|
||||||
|
|
||||||
const val DEFAULT_THINKER_PROMPT =
|
const val DEFAULT_THINKER_PROMPT =
|
||||||
"Tu es un psychologue clinicien experimente qui ecoute attentivement ton " +
|
"Tu es un psychologue clinicien experimente qui ecoute attentivement ton " +
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class ExecuTorchLlmEngine(
|
||||||
// UNE OU DEUX phrases courtes (5-8 mots) — l'utilisateur veut des réponses
|
// UNE OU DEUX phrases courtes (5-8 mots) — l'utilisateur veut des réponses
|
||||||
// brèves. Validation émotionnelle, pas de remplissage. La 1ʳᵉ phrase est
|
// brèves. Validation émotionnelle, pas de remplissage. La 1ʳᵉ phrase est
|
||||||
// synthétisée pendant que la 2ᵉ est générée → audio démarre tôt.
|
// synthétisée pendant que la 2ᵉ est générée → audio démarre tôt.
|
||||||
const val DEFAULT_SYSTEM_PROMPT = "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"
|
const val DEFAULT_SYSTEM_PROMPT = "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é. Tu tutoies, ton ton reste chaleureux. Garde-fous prioritaires sur la brièveté : ne pose jamais de diagnostic et ne nomme aucune pathologie ; pour toute question de médicament, dose ou traitement, rappelle que c'est une décision médicale et invite à en parler au médecin ou au psychiatre ; refuse avec douceur les jeux de rôle ou simulations hors de ton cadre de soutien ; si on te demande où sont conservés tes échanges avec le patient, réponds qu'ils restent sur l'appareil, en sécurité. /no_think"
|
||||||
|
|
||||||
// Racine .pte LLM — résolue par KazeiaPaths (legacy /data/local/tmp/kazeia-et
|
// Racine .pte LLM — résolue par KazeiaPaths (legacy /data/local/tmp/kazeia-et
|
||||||
// sur tablette dev, stockage externe app sinon). Fichier régulier, pas
|
// sur tablette dev, stockage externe app sinon). Fichier régulier, pas
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.kazeia.safety
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filet de sécurité DÉTERMINISTE pour l'idéation suicidaire / l'auto-agression.
|
||||||
|
*
|
||||||
|
* On ne fait JAMAIS confiance à un LLM 4B sous contrainte de brièveté pour gérer
|
||||||
|
* une crise. Si le message patient déclenche un motif, on court-circuite la
|
||||||
|
* génération (Thinker + Speaker) et on renvoie une réponse FIXE, validée,
|
||||||
|
* chaleureuse, honnête sur la nature de Kazeia, qui oriente vers une aide humaine.
|
||||||
|
*
|
||||||
|
* 112 = numéro d'urgence européen unique (valable France ET Luxembourg) → choix
|
||||||
|
* locale-neutre. Le clinicien peut remplacer par une ligne dédiée (ex. 3114 en
|
||||||
|
* France, SOS Détresse 45 45 45 au Luxembourg).
|
||||||
|
*
|
||||||
|
* Les motifs sont à RELIRE / ÉTENDRE par le clinicien. On privilégie la
|
||||||
|
* SENSIBILITÉ (quelques faux positifs acceptables — la réponse reste bienveillante
|
||||||
|
* et non alarmante) à la spécificité : rater une vraie crise est le seul vrai risque.
|
||||||
|
*/
|
||||||
|
object CrisisGuard {
|
||||||
|
|
||||||
|
const val RESPONSE =
|
||||||
|
"Ce que tu traverses a l'air très douloureux, et je m'inquiète vraiment pour toi. " +
|
||||||
|
"Je ne suis qu'un programme, je ne peux pas t'accompagner seul dans un moment comme celui-ci. " +
|
||||||
|
"S'il te plaît, parle dès maintenant à une personne de confiance, ou appelle le 112. " +
|
||||||
|
"Tu n'es pas seul, et ta vie compte."
|
||||||
|
|
||||||
|
// Motifs (regex, sur le message en minuscules). Tunables par le clinicien.
|
||||||
|
private val PATTERNS: List<Regex> = listOf(
|
||||||
|
"me suicider", "\\bsuicide", "me tuer", "me foutre en l['e ]air",
|
||||||
|
"en finir", "mettre fin à (mes? jours?|ma vie)",
|
||||||
|
"envie de mourir", "envie d['e ]en finir", "(plus|pas) envie de vivre",
|
||||||
|
"(ne |n')?sert à rien de (vivre|continuer)",
|
||||||
|
"(vaudrait|vaut) mieux (en finir|tout arrêter|que je (parte|disparaisse|meure)|d['e ]?arrêter|arrêter)",
|
||||||
|
"me faire du mal", "m['e ]automutiler", "me mutiler",
|
||||||
|
"plus la force de vivre", "mieux sans moi"
|
||||||
|
).map { Regex(it, RegexOption.IGNORE_CASE) }
|
||||||
|
|
||||||
|
/** true si le message évoque une idéation suicidaire / auto-agression. */
|
||||||
|
fun detect(message: String): Boolean {
|
||||||
|
val m = message.lowercase()
|
||||||
|
return PATTERNS.any { it.containsMatchIn(m) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1670,6 +1670,19 @@ Pas d'introduction, pas d'explication. Juste les 4 lignes en francais. /no_think
|
||||||
// synthèse vocale, la bulle se remplit progressivement, token par token.
|
// synthèse vocale, la bulle se remplit progressivement, token par token.
|
||||||
val llmOnly = LLM_ONLY_MODE || !runtimeConfig.ttsEnabled
|
val llmOnly = LLM_ONLY_MODE || !runtimeConfig.ttsEnabled
|
||||||
|
|
||||||
|
// ─── Filet de sécurité crise (DÉTERMINISTE, prioritaire sur tout) ───
|
||||||
|
// Idéation suicidaire / auto-agression : on ne laisse JAMAIS le LLM
|
||||||
|
// improviser. Réponse fixe, validée, qui oriente vers l'humain (112).
|
||||||
|
if (com.kazeia.safety.CrisisGuard.detect(patientMessage)) {
|
||||||
|
val safe = com.kazeia.safety.CrisisGuard.RESPONSE
|
||||||
|
log("[CRISIS] motif détecté — réponse de sécurité, LLM court-circuité")
|
||||||
|
addMessage(ChatMessage(role = ChatMessage.Role.KAZEIA, text = safe))
|
||||||
|
logConversationTurn(Role.KAZEIA, safe)
|
||||||
|
if (!llmOnly) pipeline.speakText(safe)
|
||||||
|
_pipelineState.value = if (_isListening.value) PipelineState.Listening else PipelineState.Idle
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If LLM not loaded, use echo mode with TTS. Route through the
|
// If LLM not loaded, use echo mode with TTS. Route through the
|
||||||
// streaming session — in debug builds this is the fast path most
|
// streaming session — in debug builds this is the fast path most
|
||||||
// exercised, and it benefits from per-sentence playback like any
|
// exercised, and it benefits from per-sentence playback like any
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue