# 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`:

```typescript
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)
