fix(safety): rappel « mieux mort » + regex normalize compilées une fois

Code-review (2e passe) :
- Faux négatif CrisisGuard : « je serais/suis mieux mort », « mieux mort que
  vivant » ne déclenchaient aucun motif. Ajout du motif « mieux mort » + 3 cas
  de test. (Sécurité vitale — sensibilité d'abord.)
- normalize() recompilait 3 Regex à chaque appel (1×/message patient) :
  hoistées en constantes (MARKS/APOS/SPACES).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kazeia Team 2026-06-11 16:22:27 +02:00
parent 7669a79f4f
commit d7e90c16c4
2 changed files with 13 additions and 3 deletions

View File

@ -40,19 +40,26 @@ object CrisisGuard {
"(vaudrait|vaut) mieux (en finir|tout arreter|que je (parte|disparaisse|meure)|d ?arreter|arreter)", "(vaudrait|vaut) mieux (en finir|tout arreter|que je (parte|disparaisse|meure)|d ?arreter|arreter)",
"me faire du mal", "m ?automutiler", "me mutiler", "me scarifier", "me faire du mal", "m ?automutiler", "me mutiler", "me scarifier",
"plus la force de vivre", "mieux sans moi", "tous mieux sans moi", "plus la force de vivre", "mieux sans moi", "tous mieux sans moi",
"mieux mort", // « je serais/suis mieux mort », « mieux mort que vivant »
"me pendre", // aucun usage bénin → autonome "me pendre", // aucun usage bénin → autonome
"(veux|voudrais|envie de) (disparaitre|en finir|me noyer)", "(veux|voudrais|envie de) (disparaitre|en finir|me noyer)",
"me jeter (par|sous|dans|du)", "passer a l acte" "me jeter (par|sous|dans|du)", "passer a l acte"
).map { Regex(it) } ).map { Regex(it) }
// Regex de normalisation compilées une fois (detect() est appelé à chaque
// message patient — inutile de les recompiler à chaque appel).
private val MARKS = Regex("\\p{Mn}+")
private val APOS = Regex("[']")
private val SPACES = Regex("\\s+")
/** minuscules + suppression des accents (NFD retrait des diacritiques) + /** minuscules + suppression des accents (NFD retrait des diacritiques) +
* apostrophes (droites/typographiques) espace + espaces compactés. Permet * apostrophes (droites/typographiques) espace + espaces compactés. Permet
* d'écrire les motifs sans accents ni apostrophes (robuste au STT et à la frappe). */ * d'écrire les motifs sans accents ni apostrophes (robuste au STT et à la frappe). */
private fun normalize(s: String): String = private fun normalize(s: String): String =
java.text.Normalizer.normalize(s.lowercase(), java.text.Normalizer.Form.NFD) java.text.Normalizer.normalize(s.lowercase(), java.text.Normalizer.Form.NFD)
.replace(Regex("\\p{Mn}+"), "") .replace(MARKS, "")
.replace(Regex("[']"), " ") .replace(APOS, " ")
.replace(Regex("\\s+"), " ") .replace(SPACES, " ")
/** true si le message évoque une idéation suicidaire / auto-agression. */ /** true si le message évoque une idéation suicidaire / auto-agression. */
fun detect(message: String): Boolean { fun detect(message: String): Boolean {

View File

@ -60,6 +60,9 @@ class CrisisGuardTest {
"je veux disparaître", "je veux disparaître",
"je pense à me pendre", "je pense à me pendre",
"je vais passer à l'acte", "je vais passer à l'acte",
"je serais mieux mort", // forme conditionnelle « mieux mort »
"tout le monde serait mieux sans moi",
"je suis mieux mort que vivant",
// accents supprimés (sortie STT fréquente) — DOIVENT matcher quand même // accents supprimés (sortie STT fréquente) — DOIVENT matcher quand même
"je veux mettre fin a mes jours", "je veux mettre fin a mes jours",
"ca sert a rien de vivre", "ca sert a rien de vivre",