Keep/_bmad-output/planning-artifacts/epic-search-improvement.md
sepehr 640fcb26f7 fix: improve note interactions and markdown LaTeX support
## Bug Fixes

### Note Card Actions
- Fix broken size change functionality (missing state declaration)
- Implement React 19 useOptimistic for instant UI feedback
- Add startTransition for non-blocking updates
- Ensure smooth animations without page refresh
- All note actions now work: pin, archive, color, size, checklist

### Markdown LaTeX Rendering
- Add remark-math and rehype-katex plugins
- Support inline equations with dollar sign syntax
- Support block equations with double dollar sign syntax
- Import KaTeX CSS for proper styling
- Equations now render correctly instead of showing raw LaTeX

## Technical Details

- Replace undefined currentNote references with optimistic state
- Add optimistic updates before server actions for instant feedback
- Use router.refresh() in transitions for smart cache invalidation
- Install remark-math, rehype-katex, and katex packages

## Testing

- Build passes successfully with no TypeScript errors
- Dev server hot-reloads changes correctly
2026-01-09 22:13:49 +01:00

19 KiB

Epic: Amélioration de la Recherche Sémantique - Version 2.0

Epic ID: EPIC-SEARCH-2.0 Status: Draft Priority: High Created: 2026-01-09 Owner: Development Team


Description du Problème

L'actuel système de recherche hybride (mots-clés + sémantique) produit des résultats non pertinents et imprévisibles. Les utilisateurs se plaignent que la recherche "fait n'importe quoi" - les résultats ne correspondent pas à leurs attentes, même quand les notes contiennent les termes recherchés.

Analyse des Causes Racines

Après analyse du code dans keep-notes/app/actions/notes.ts (lignes 108-212), les problèmes identifiés sont:

  1. Seuil de similarité cosine trop bas (0.40): Permet des correspondances sémantiques de très faible qualité, créant du bruit dans les résultats
  2. RRF (Reciprocal Rank Fusion) mal configuré: Le constant k=60 n'est pas optimal pour le petit nombre de notes typique dans Keep
  3. Pondération fixe recherche mot-clé/sémantique: Les poids sont hardcodés (3 pour titre, 1 pour contenu, 2 pour labels) sans adaptation au type de requête
  4. Absence de validation des embeddings: Pas de vérification que les embeddings sont correctement générés ou de dimensionnalité cohérente
  5. Manque de prétraitement des requêtes: Pas de stemming, lemmatization, ou expansion de requête
  6. Aucune métrique de qualité: Impossible de mesurer l'amélioration ou la dégradation des performances

Impact Utilisateur

  • Frustration: Perte de temps à filtrer manuellement les résultats non pertinents
  • Perte de confiance: Les utilisateurs désactivent ou évitent la recherche intelligente
  • Expérience dégradée: Contredit la promesse d'une recherche "intuitive" du PRD

Objectifs Mesurables

  1. Améliorer la précision de recherche de 40% (mesurée par tests automatisés)
  2. Réduire les faux positifs sémantiques de 60% (seuil plus strict)
  3. Temps de réponse < 300ms pour 1000 notes (objectif PRD existant)
  4. Satisfaction utilisateur > 4/5 (feedback post-déploiement)
  5. Taux de "serendipity" (résultats sémantiques sans mots-clés) entre 20-40% (objectif PRD)

User Stories

Story 1: Validation et Qualité des Embeddings

ID: SEARCH-2.0-1 Title: Valider la qualité des embeddings générés Priority: Must Have Estimation: 3h

En tant que: développeur Je veux: valider que les embeddings sont correctement générés et stockés Afin que: la recherche sémantique fonctionne sur des données de qualité

Critères d'Acceptation:

  1. Given une note avec du contenu texte
  2. When l'embedding est généré via provider.getEmbeddings()
  3. Then le vecteur doit:
    • Avoir une dimensionnalité > 0
    • Contenir des nombres valides (pas de NaN, Infinity)
    • Avoir une norme L2 normale (entre 0.7 et 1.2)
  4. Given des embeddings existants en base de données
  5. When ils sont chargés via parseNote()
  6. Then ils doivent être correctement désérialisés et validés
  7. And une action admin /api/debug/embeddings/validate doit lister les notes problématiques

Fichiers à Modifier:

  • keep-notes/app/actions/notes.ts - Ajouter validation dans parseNote()
  • keep-notes/lib/utils.ts - Ajouter validateEmbedding() et normalizeEmbedding()
  • keep-notes/app/api/admin/embeddings/validate/route.ts - Nouveau endpoint debug

Tests Nécessaires:

  • Test unitaire validateEmbedding() avec vecteurs valides/invalides
  • Test d'intégration création note → validation embedding
  • Test endpoint API debug

Risques:

  • Certains embeddings existants invalides nécessiteront un re-indexing
  • Performance impact de la validation à chaque chargement

Story 2: Optimisation du Seuil de Similarité Sémantique

ID: SEARCH-2.0-2 Title: Ajuster le seuil de similarité cosine pour éliminer le bruit Priority: Must Have Estimation: 4h

En tant que: utilisateur Je veux: ne voir que des résultats sémantiquement pertinents Afin que: la recherche me fasse confiance et me fasse gagner du temps

Critères d'Acceptation:

  1. Given une requête de recherche sémantique
  2. When les notes sont classées par similarité cosine
  3. Then seules les notes avec similarité >= 0.65 sont considérées (au lieu de 0.40)
  4. And le seuil doit être configurable dans SystemConfig (SEARCH_SEMANTIC_THRESHOLD)
  5. Given une recherche avec résultats sémantiques faibles
  6. When le seuil est appliqué
  7. Then les faux positifs sont réduits d'au moins 50%
  8. And un test automatisé mesure la réduction des faux positifs

Fichiers à Modifier:

  • keep-notes/app/actions/notes.ts - Ligne 190, remplacer 0.40 par config.SEARCH_SEMANTIC_THRESHOLD || 0.65
  • keep-notes/lib/config.ts - Lire la config depuis DB
  • keep-notes/tests/search-quality.spec.ts - Ajouter tests de seuil

Tests Nécessaires:

  • Test Playwright: recherche "coding" ne retourne PAS des notes sur "cuisine" (faux positifs)
  • Test unitaire: vérifier que les scores < seuil sont filtrés
  • Test de non-régression: s'assurer que les vrais positifs ne sont pas perdus

Risques:

  • Seuil trop élevé peut éliminer des résultats pertinents (faux négatifs)
  • Nécessite A/B testing pour trouver le seuil optimal
  • Différents modèles d'embedding peuvent nécessiter des seuils différents

Story 3: Reconfiguration de l'Algorithme RRF

ID: SEARCH-2.0-3 Title: Optimiser le paramètre k du Reciprocal Rank Fusion Priority: Should Have Estimation: 3h

En tant que: système Je veux: un RRF avec un paramètre k adapté au nombre de notes typique Afin que: le ranking hybride reflète mieux la pertinence réelle

Critères d'Acceptation:

  1. Given un RRF avec constant k
  2. When le nombre moyen de notes par utilisateur est < 500
  3. Then k doit être 20 (au lieu de 60) pour mieux pénaliser les bas rangs
  4. And k doit être configurable: k = max(20, nombre_notes / 10)
  5. Given deux listes de ranking (mot-clé + sémantique)
  6. When RRF est appliqué
  7. Then les résultats bien classés dans les deux listes sont fortement favorisés
  8. And la formule RRF est documentée dans le code

Fichiers à Modifier:

  • keep-notes/app/actions/notes.ts - Lignes 182-198, ajuster k et ajouter logique adaptive
  • keep-notes/lib/utils.ts - Ajouter calculateRRFK(totalNotes: number): number

Tests Nécessaires:

  • Test unitaire: vérifier la formule RRF avec différents k
  • Test d'intégration: comparer rankings avec k=20 vs k=60
  • Test avec dataset de benchmark (notes + requêtes + résultats attendus)

Risques:

  • Changer k peut impacter significativement l'ordre des résultats
  • Nécessite validation utilisateur sur de vraies données
  • Peut nécessiter des ajustements itératifs

Story 4: Pondération Adaptative des Scores de Recherche

ID: SEARCH-2.0-4 Title: Adapter les poids mot-clé/sémantique selon le type de requête Priority: Should Have Estimation: 6h

En tant que: utilisateur Je veux: que la recherche privilégie les mots-clés pour les termes exacts **Et qu'elle privilégie le sémantique pour les concepts abstraits Afin que: les résultats soient toujours pertinents

Critères d'Acceptation:

  1. Given une requête avec des guillemets (ex: "Error 404")
  2. When la recherche est exécutée
  3. Then le poids mot-clé est multiplié par 2 (recherche exacte prioritaire)
  4. Given une requête conceptuelle (ex: "comment améliorer...")
  5. When la recherche est exécutée
  6. Then le poids sémantique est multiplié par 1.5 (concept prioritaire)
  7. Given une requête mixte
  8. When aucun pattern n'est détecté
  9. Then les poids par défaut sont utilisés
  10. And la logique de détection est documentée

Fichiers à Modifier:

  • keep-notes/app/actions/notes.ts - Ajouter detectQueryType() et ajuster les poids
  • keep-notes/lib/types.ts - Ajouter QueryType: 'exact' | 'conceptual' | 'mixed'
  • keep-notes/lib/utils.ts - Ajouter detectQueryType(query: string): QueryType

Tests Nécessaires:

  • Test unitaire detectQueryType() avec différents patterns
  • Test d'intégration: vérifier que "Error 404" privilégie les mots-clés
  • Test d'intégration: vérifier que "comment cuisiner" privilégie le sémantique
  • Test Playwright: scénarios de recherche avec guillemets

Risques:

  • La détection automatique du type de requête peut être imprécise
  • Nécessite des règles bien pensées pour éviter les effets de bord
  • Peut nécessiter du machine learning pour être vraiment efficace

Story 5: Expansion et Normalisation des Requêtes

ID: SEARCH-2.0-5 Title: Améliorer les requêtes par expansion et normalisation Priority: Could Have Estimation: 5h

En tant que: utilisateur francophone/anglophone Je veux: que ma recherche trouve les résultats même avec des variations de mots Afin que: je n'aie pas à deviner les termes exacts utilisés

Critères d'Acceptation:

  1. Given une requête avec des mots au pluriel (ex: "recettes pizzas")
  2. When la recherche est exécutée
  3. Then les singuliers sont aussi recherchés ("recette pizza")
  4. Given une requête avec des accents (ex: "éléphant")
  5. When la recherche est exécutée
  6. Then les variantes sans accents sont aussi recherchées ("elephant")
  7. Given une requête courte (< 3 mots)
  8. When l'expansion est activée
  9. Then des synonymes courants sont ajoutés (ex: "bug" → "erreur", "problème")
  10. And l'expansion est limitée à 3 termes par mot original

Fichiers à Modifier:

  • keep-notes/app/actions/notes.ts - Ajouter expandQuery() avant le calcul des scores
  • keep-notes/lib/utils.ts - Implémenter expandQuery() et normalizeText()
  • keep-notes/lib/data/synonyms.json - Créer une liste de synonymes (FR/EN)

Tests Nécessaires:

  • Test unitaire expandQuery() avec différents cas
  • Test d'intégration: recherche "pizzas" trouve notes avec "pizza"
  • Test d'intégration: recherche "bug" trouve notes avec "erreur"
  • Test performance: vérifier que l'expansion ne dégrade pas les performances

Risques:

  • L'expansion de requête peut augmenter significativement les faux positifs
  • La gestion des synonymes est complexe et contextuelle
  • Nécessite une base de synonymes bien maintenue
  • Peut ne pas être pertinent pour toutes les langues

Story 6: Interface de Debug et Monitoring de Recherche

ID: SEARCH-2.0-6 Title: Créer une interface de debug pour analyser la qualité de recherche Priority: Should Have Estimation: 8h

En tant que: développeur/testeur Je veux: visualiser les détails du calcul de score pour chaque résultat Afin que: je puisse comprendre et optimiser la recherche

Critères d'Acceptation:

  1. Given une recherche exécutée
  2. When le mode debug est activé (?debug=true)
  3. Then chaque résultat affiche:
    • Score mot-clé brut
    • Score sémantique brut (similarité cosine)
    • Score RRF final
    • Rang dans chaque liste (mot-clé / sémantique)
  4. Given la page /debug-search
  5. When j'accède à la page
  6. Then je vois une interface pour:
    • Tester des requêtes avec tous les paramètres
    • Comparer différents seuils de similarité
    • Voir les embeddings des notes
  7. And les métriques sont exportables en JSON

Fichiers à Modifier:

  • keep-notes/app/actions/notes.ts - Retourner les scores debug si demandé
  • keep-notes/app/debug-search/page.tsx - Créer la page (existante dans git status)
  • keep-notes/components/search-debug-results.tsx - Nouveau composant
  • keep-notes/app/api/debug/search/route.ts - Nouveau endpoint API

Tests Nécessaires:

  • Test E2E: accès à la page debug-search
  • Test API: endpoint retourne bien les scores détaillés
  • Test visuel: vérifier l'affichage des scores dans l'UI
  • Test de performance: vérifier que le mode debug n'impacte pas la recherche normale

Risques:

  • Complexité supplémentaire dans la UI
  • Nécessite de bien sécuriser l'accès (admin uniquement)
  • Informations sensibles (embeddings) visibles

Story 7: Re-génération et Validation des Embeddings Existants

ID: SEARCH-2.0-7 Title: Script de re-indexation des embeddings invalides ou manquants Priority: Must Have Estimation: 4h

En tant que: administrateur système Je veux: un script pour régénérer les embeddings des notes existantes Afin que: toutes les notes bénéficient de la recherche sémantique

Critères d'Acceptation:

  1. Given des notes avec embeddings manquants ou invalides
  2. When je lance le script npm run reindex-embeddings
  3. Then les embeddings sont régénérés pour toutes les notes
  4. And la progression est affichée (X/Y notes traitées)
  5. Given des notes avec des embeddings valides
  6. When le script est lancé avec --force
  7. Then tous les embeddings sont régénérés (même les valides)
  8. And le script peut être relancé sans erreurs (idempotent)
  9. And un rapport final résume les succès/erreurs

Fichiers à Modifier:

  • keep-notes/scripts/reindex-embeddings.ts - Créer le script
  • keep-notes/app/actions/admin.ts - Ajouter reindexAllEmbeddings()
  • keep-notes/package.json - Ajouter le script npm

Tests Nécessaires:

  • Test du script sur base de données vide
  • Test du script avec des notes sans embeddings
  • Test du script avec des embeddings invalides (NaN, dimension 0)
  • Test du mode --force
  • Test d'idempotence (relancer le script deux fois)

Risques:

  • Temps d'exécution long si beaucoup de notes (plusieurs minutes/heures)
  • Peut saturer le provider IA (Ollama/OpenAI) avec trop de requêtes
  • Nécessite un mécanisme de rate limiting
  • Peut impacter les performances de l'application si lancé pendant l'utilisation

Story 8: Suite de Tests Automatisés de Qualité de Recherche

ID: SEARCH-2.0-8 Title: Créer des tests automatisés pour mesurer la qualité de recherche Priority: Should Have Estimation: 6h

En tant que: développeur Je veux: une suite de tests automatisés pour valider la qualité de recherche Afin que: les améliorations soient mesurables et les régressions détectées

Critères d'Acceptation:

  1. Given un dataset de test (notes + requêtes + résultats attendus)
  2. When les tests sont exécutés
  3. Then les métriques suivantes sont calculées:
    • Precision: % de résultats pertinents dans le top 10
    • Recall: % de résultats pertinents trouvés
    • MRR (Mean Reciprocal Rank): rang moyen du premier résultat pertinent
    • Faux positifs sémantiques: résultats sans pertinence
  4. Given une modification du code de recherche
  5. When les tests sont relancés
  6. Then une régression de plus de 5% fait échouer les tests
  7. And les tests prennent < 2 minutes à s'exécuter
  8. And un rapport HTML est généré pour analyse

Fichiers à Modifier:

  • keep-notes/tests/search-benchmark.spec.ts - Créer le benchmark
  • keep-notes/tests/fixtures/search-dataset.json - Dataset de test
  • keep-notes/tests/utils/search-metrics.ts - Utilitaires de calcul de métriques
  • keep-notes/playwright.config.ts - Configuration pour générer le rapport HTML

Tests Nécessaires:

  • Test du test (métatest) avec un dataset trivial
  • Test avec dataset réel (notes sur tech, cuisine, voyage, etc.)
  • Test de régression: introduire un bug et vérifier que les tests le détectent
  • Test de performance: temps d'exécution des tests

Risques:

  • Création du dataset manuelle et longue
  • Subjectivité de la "pertinence" (qui décide quoi est pertinent?)
  • Maintenance du dataset à chaque nouvelle feature
  • Tests peuvent être "flaky" si les embeddings changent

Dépendances Entre Stories

SEARCH-2.0-1 (Validation Embeddings)
    ↓
SEARCH-2.0-7 (Re-génération) ← dépend de 1 pour détecter les invalides
    ↓
SEARCH-2.0-2 (Seuil Similarité) ← dépend de 1 pour des embeddings valides
    ↓
SEARCH-2.0-3 (RRF Config)
    ↓
SEARCH-2.0-4 (Pondération Adaptative)
    ↓
SEARCH-2.0-8 (Tests Automatisés) ← dépend de 2,3,4 pour mesurer les améliorations
    ↓
SEARCH-2.0-6 (Debug Interface) ← utile pendant le développement de toutes les autres
    ↓
SEARCH-2.0-5 (Expansion Requêtes) ← amélioration optionnelle à la fin

Ordre recommandé:

  1. Sprint 1: SEARCH-2.0-1, SEARCH-2.0-7 (Base solide avec embeddings valides)
  2. Sprint 2: SEARCH-2.0-2, SEARCH-2.0-3 (Corrections critiques du ranking)
  3. Sprint 3: SEARCH-2.0-4, SEARCH-2.0-6 (Améliorations + tooling)
  4. Sprint 4: SEARCH-2.0-8, SEARCH-2.0-5 (Tests + optimisations optionnelles)

Métriques de Succès

Avant/Après (Objectifs)

Métrique Avant Après (Objectif) Comment Mesurer
Précision Top-10 ~50% (estimé) 70%+ Tests automatisés Story 8
Faux positifs sémantiques ~30% <10% Tests Playwright
Temps de réponse (1000 notes) 200ms <300ms Tests performance
Taux de serendipité N/A 20-40% Tests dataset
Satisfaction utilisateur 2/5 (subjectif) 4/5+ Sondage post-déploiement

Configuration Système Proposée

Nouveaux paramètres dans SystemConfig:

interface SearchConfig {
  // Seuil de similarité cosine minimum (0-1)
  SEARCH_SEMANTIC_THRESHOLD: number; // défaut: 0.65

  // Constante k pour RRF (adaptive)
  SEARCH_RRF_K_BASE: number; // défaut: 20
  SEARCH_RRF_K_ADAPTIVE: boolean; // défaut: true

  // Pondération mot-clé vs sémantique
  SEARCH_KEYWORD_BOOST_EXACT: number; // défaut: 2.0 (guillemets)
  SEARCH_KEYWORD_BOOST_CONCEPTUAL: number; // défaut: 0.7
  SEARCH_SEMANTIC_BOOST_EXACT: number; // défaut: 0.7
  SEARCH_SEMANTIC_BOOST_CONCEPTUAL: number; // défaut: 1.5

  // Expansion de requête
  SEARCH_QUERY_EXPANSION_ENABLED: boolean; // défaut: true
  SEARCH_QUERY_EXPANSION_MAX_SYNONYMS: number; // défaut: 3

  // Debug
  SEARCH_DEBUG_MODE: boolean; // défaut: false
}

Fichers Critiques pour l'Implémentation

Les 5 fichiers les plus importants à modifier:

  1. keep-notes/app/actions/notes.ts - Logique de recherche principale (RRF, seuils, ranking)
  2. keep-notes/lib/utils.ts - Fonctions de similarité cosine et nouvelles utilités
  3. keep-notes/lib/ai/providers/ollama.ts - Génération des embeddings avec validation
  4. keep-notes/tests/search-quality.spec.ts - Tests de qualité de recherche
  5. keep-notes/lib/config.ts - Configuration des nouveaux paramètres

Document Version: 1.0 Last Updated: 2026-01-09 Agent: Plan (a551c9b)