## 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
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:
- 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
- RRF (Reciprocal Rank Fusion) mal configuré: Le constant k=60 n'est pas optimal pour le petit nombre de notes typique dans Keep
- 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
- Absence de validation des embeddings: Pas de vérification que les embeddings sont correctement générés ou de dimensionnalité cohérente
- Manque de prétraitement des requêtes: Pas de stemming, lemmatization, ou expansion de requête
- 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
- Améliorer la précision de recherche de 40% (mesurée par tests automatisés)
- Réduire les faux positifs sémantiques de 60% (seuil plus strict)
- Temps de réponse < 300ms pour 1000 notes (objectif PRD existant)
- Satisfaction utilisateur > 4/5 (feedback post-déploiement)
- 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:
- Given une note avec du contenu texte
- When l'embedding est généré via
provider.getEmbeddings() - 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)
- Given des embeddings existants en base de données
- When ils sont chargés via
parseNote() - Then ils doivent être correctement désérialisés et validés
- And une action admin
/api/debug/embeddings/validatedoit lister les notes problématiques
Fichiers à Modifier:
keep-notes/app/actions/notes.ts- Ajouter validation dansparseNote()keep-notes/lib/utils.ts- AjoutervalidateEmbedding()etnormalizeEmbedding()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:
- Given une requête de recherche sémantique
- When les notes sont classées par similarité cosine
- Then seules les notes avec similarité >= 0.65 sont considérées (au lieu de 0.40)
- And le seuil doit être configurable dans
SystemConfig(SEARCH_SEMANTIC_THRESHOLD) - Given une recherche avec résultats sémantiques faibles
- When le seuil est appliqué
- Then les faux positifs sont réduits d'au moins 50%
- And un test automatisé mesure la réduction des faux positifs
Fichiers à Modifier:
keep-notes/app/actions/notes.ts- Ligne 190, remplacer 0.40 parconfig.SEARCH_SEMANTIC_THRESHOLD || 0.65keep-notes/lib/config.ts- Lire la config depuis DBkeep-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:
- Given un RRF avec constant k
- When le nombre moyen de notes par utilisateur est < 500
- Then k doit être 20 (au lieu de 60) pour mieux pénaliser les bas rangs
- And k doit être configurable:
k = max(20, nombre_notes / 10) - Given deux listes de ranking (mot-clé + sémantique)
- When RRF est appliqué
- Then les résultats bien classés dans les deux listes sont fortement favorisés
- 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 adaptivekeep-notes/lib/utils.ts- AjoutercalculateRRFK(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:
- Given une requête avec des guillemets (ex:
"Error 404") - When la recherche est exécutée
- Then le poids mot-clé est multiplié par 2 (recherche exacte prioritaire)
- Given une requête conceptuelle (ex: "comment améliorer...")
- When la recherche est exécutée
- Then le poids sémantique est multiplié par 1.5 (concept prioritaire)
- Given une requête mixte
- When aucun pattern n'est détecté
- Then les poids par défaut sont utilisés
- And la logique de détection est documentée
Fichiers à Modifier:
keep-notes/app/actions/notes.ts- AjouterdetectQueryType()et ajuster les poidskeep-notes/lib/types.ts- AjouterQueryType: 'exact' | 'conceptual' | 'mixed'keep-notes/lib/utils.ts- AjouterdetectQueryType(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:
- Given une requête avec des mots au pluriel (ex: "recettes pizzas")
- When la recherche est exécutée
- Then les singuliers sont aussi recherchés ("recette pizza")
- Given une requête avec des accents (ex: "éléphant")
- When la recherche est exécutée
- Then les variantes sans accents sont aussi recherchées ("elephant")
- Given une requête courte (< 3 mots)
- When l'expansion est activée
- Then des synonymes courants sont ajoutés (ex: "bug" → "erreur", "problème")
- And l'expansion est limitée à 3 termes par mot original
Fichiers à Modifier:
keep-notes/app/actions/notes.ts- AjouterexpandQuery()avant le calcul des scoreskeep-notes/lib/utils.ts- ImplémenterexpandQuery()etnormalizeText()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:
- Given une recherche exécutée
- When le mode debug est activé (
?debug=true) - 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)
- Given la page
/debug-search - When j'accède à la page
- 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
- 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 composantkeep-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:
- Given des notes avec embeddings manquants ou invalides
- When je lance le script
npm run reindex-embeddings - Then les embeddings sont régénérés pour toutes les notes
- And la progression est affichée (X/Y notes traitées)
- Given des notes avec des embeddings valides
- When le script est lancé avec
--force - Then tous les embeddings sont régénérés (même les valides)
- And le script peut être relancé sans erreurs (idempotent)
- And un rapport final résume les succès/erreurs
Fichiers à Modifier:
keep-notes/scripts/reindex-embeddings.ts- Créer le scriptkeep-notes/app/actions/admin.ts- AjouterreindexAllEmbeddings()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:
- Given un dataset de test (notes + requêtes + résultats attendus)
- When les tests sont exécutés
- 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
- Given une modification du code de recherche
- When les tests sont relancés
- Then une régression de plus de 5% fait échouer les tests
- And les tests prennent < 2 minutes à s'exécuter
- And un rapport HTML est généré pour analyse
Fichiers à Modifier:
keep-notes/tests/search-benchmark.spec.ts- Créer le benchmarkkeep-notes/tests/fixtures/search-dataset.json- Dataset de testkeep-notes/tests/utils/search-metrics.ts- Utilitaires de calcul de métriqueskeep-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é:
- Sprint 1: SEARCH-2.0-1, SEARCH-2.0-7 (Base solide avec embeddings valides)
- Sprint 2: SEARCH-2.0-2, SEARCH-2.0-3 (Corrections critiques du ranking)
- Sprint 3: SEARCH-2.0-4, SEARCH-2.0-6 (Améliorations + tooling)
- 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:
- keep-notes/app/actions/notes.ts - Logique de recherche principale (RRF, seuils, ranking)
- keep-notes/lib/utils.ts - Fonctions de similarité cosine et nouvelles utilités
- keep-notes/lib/ai/providers/ollama.ts - Génération des embeddings avec validation
- keep-notes/tests/search-quality.spec.ts - Tests de qualité de recherche
- keep-notes/lib/config.ts - Configuration des nouveaux paramètres
Document Version: 1.0 Last Updated: 2026-01-09 Agent: Plan (a551c9b)