-
Notifications
You must be signed in to change notification settings - Fork 0
FEATURE_SIMILARITY_FINDER
Version: 1.0.0-rc.3 (re-verified) Status: Implémenté Dernière mise à jour: 2026-07-20
La fonctionnalité Similarity Finder permet à l'historien·ne de comparer son texte en cours de rédaction avec les PDFs indexés dans sa base vectorielle. Le système analyse chaque segment du texte (section, paragraphe ou phrase selon le choix) et recommande des sources pertinentes pour approfondir chaque partie.
Philosophie : Analyse asynchrone à la demande, adaptée aux contraintes d'un environnement local avec Ollama.
- L'utilisateur·rice rédige son article dans ClioDeck (
document.md) - À un moment opportun (pause, fin de session), il/elle clique sur "Trouver des similarités" dans la barre d'outils de l'éditeur
- Une barre de progression indique l'avancement de l'analyse
- Une fois terminé, l'utilisateur·rice peut cliquer sur n'importe quel segment de son texte
- Un panneau latéral affiche les PDFs recommandés pour ce segment
- L'utilisateur·rice peut ouvrir le PDF ou insérer une citation
L'utilisateur·rice choisit la granularité d'analyse :
| Granularité | Découpage | Cas d'usage |
|---|---|---|
| Section | Titres Markdown (#, ##, ###) |
Vue d'ensemble, textes longs |
| Paragraphe | Blocs séparés par lignes vides | Équilibre précision/performance |
| Phrase | Segmentation par ponctuation | Analyse fine, textes courts |
Défaut recommandé : Paragraphe
| Option | Type | Valeur par défaut | Description |
|---|---|---|---|
granularity |
enum | paragraph |
Niveau de découpage (section, paragraph, sentence) |
maxResults |
number | 5 |
Nombre max de PDFs par segment (1-20) |
similarityThreshold |
number | 0 |
Seuil minimum de similarité (0-1) — aucun filtrage par défaut |
collectionFilter |
string[] | null |
Restriction à certaines collections Zotero |
sourceType |
enum | secondary |
Type de sources à rechercher (secondary, primary, both) — PDFs uniquement par défaut |
useReranking |
boolean | true |
Reranking par LLM activé par défaut pour une meilleure pertinence |
useContextualEmbedding |
boolean | true |
Embeddings contextuels activés par défaut (contexte du document ajouté) |
Interaction : Au clic sur un segment analysé, un panneau latéral s'affiche avec :
- Titre du segment (premiers mots ou numéro)
- Score de similarité global
- Liste des PDFs recommandés :
- Titre de la source
- Auteur(s)
- Score de similarité (%)
- Bouton "Ouvrir le PDF"
- Bouton "Insérer citation"
Stratégie de cache intelligent — le verrou se pose au niveau du document entier, pas segment par segment :
Hash du document = hash(contenu_texte_entier)
Hash de la BDD = hash(dernière_modification_vectorstore)
Si document_hash + bdd_hash + options identiques existent en cache :
→ Lire les résultats par segment depuis cache.segments
Sinon :
→ Recalculer les embeddings
→ Sauvegarder en cache
Le cache expire aussi après 24 heures, et se réinvalide si
granularity, maxResults ou similarityThreshold changent — pas
seulement si le document ou l'index vectoriel changent.
Stockage : Fichier JSON dans le dossier projet (.cliodeck/similarity_cache.json)
| Action | Comportement |
|---|---|
| Ouvrir le PDF | Ouvre le PDF dans le lecteur ClioDeck |
| Insérer citation | Insère une référence Markdown au curseur (format configurable) |
┌─────────────────────────────────────────────────────────────────┐
│ RENDERER │
├─────────────────────────────────────────────────────────────────┤
│ EditorPanel (barre d'outils inline, pas un composant dédié) │
│ └── [Bouton "Trouver des similarités"] │
│ │
│ SimilarityPanel │
│ ├── SimilarityProgress (barre de progression) │
│ ├── SimilarityResults (panneau latéral) │
│ │ └── SimilarityCard (carte par PDF recommandé) │
│ └── SimilarityOptions (modal de configuration) │
│ │
│ useSimilarityStore (Zustand) │
│ ├── results: Map<string, SimilarityResult> (un par segment) │
│ ├── isAnalyzing: boolean │
│ ├── progress: { current: number, total: number } │
│ └── options: SimilarityOptions │
└─────────────────────────────────────────────────────────────────┘
│
│ IPC
▼
┌─────────────────────────────────────────────────────────────────┐
│ MAIN │
├─────────────────────────────────────────────────────────────────┤
│ similarity-handlers.ts │
│ ├── similarity:analyze │
│ ├── similarity:get-results │
│ └── similarity:clear-cache │
│ │
│ SimilarityService │
│ ├── analyzeDocument(text, options) │
│ ├── segmentText(text, granularity) │
│ ├── findSimilarPDFs(segment, options) │
│ ├── loadCache() / saveCache() │
│ └── computeHashes() │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ BACKEND │
├─────────────────────────────────────────────────────────────────┤
│ VectorStoreManager (existant) │
│ └── searchSimilar(embedding, options) │
│ │
│ LLMProvider (typed ProviderRegistry — Ollama, embedded, │
│ Anthropic, OpenAI-compatible, Mistral, Gemini) │
│ └── generateEmbedding(text) / rerank via generate() │
└─────────────────────────────────────────────────────────────────┘
SimilarityService no longer falls back to a legacy OllamaClient — callers (the IPC handler, tests) wire in an LLMProvider via setLLMProvider() before analysis runs. If reranking is requested but no provider is wired (or the LLM call fails for any reason), the error is caught and logged — not surfaced to the user — and the original, non-reranked order is used silently instead. Only the first 10 candidates are ever sent to the LLM for reranking (maxCandidates = Math.min(candidates.length, 10)), regardless of maxResults.
// Types principaux
interface SimilarityOptions {
granularity: 'section' | 'paragraph' | 'sentence';
maxResults: number;
similarityThreshold: number;
collectionFilter: string[] | null;
sourceType: 'secondary' | 'primary' | 'both';
useReranking: boolean;
useContextualEmbedding: boolean;
}
interface TextSegment {
id: string; // Hash unique
content: string; // Contenu textuel
startLine: number; // Position dans le document
endLine: number;
type: 'section' | 'paragraph' | 'sentence';
title?: string; // Pour les sections : le texte du titre
}
interface SimilarityResult {
segmentId: string;
segment: TextSegment; // Le segment complet, pas seulement son id
recommendations: PDFRecommendation[];
analyzedAt: number; // Timestamp
}
interface PDFRecommendation {
pdfId: string;
title: string;
authors: string[];
similarity: number; // 0-1
chunkPreview: string; // Extrait pertinent du PDF
zoteroKey?: string; // Pour insertion citation
pageNumber?: number;
sourceType?: 'secondary' | 'primary';
// Champs spécifiques aux sources primaires (Tropy) :
sourceId?: string;
archive?: string;
collection?: string;
date?: string;
tags?: string[];
}Côté renderer, le store Zustand (similarityStore.ts:71) garde results: Map<string, SimilarityResult> — un seul résultat par segment, pas un
tableau (SimilarityResult[]) comme un schéma antérieur de cette page le
suggérait.
| Composant existant | Utilisation |
|---|---|
VectorStoreManager |
Recherche de similarité |
LLMProvider (via ProviderRegistry) |
Génération d'embeddings et reranking — n'importe quel provider configuré, pas seulement Ollama |
PDFService |
Métadonnées des PDFs |
ZoteroService |
Collections et citations |
editorStore |
Gestion du document |
- Dans l'éditeur, cliquez sur "Trouver des similarités" dans la barre d'outils
Avant l'analyse, vous pouvez configurer :
- Granularité : Section, Paragraphe, ou Phrase
- Nombre de résultats : Maximum de sources par segment
- Seuil de similarité : Filtrer les résultats peu pertinents
- Collection : Limiter à une collection Zotero
Attention : useReranking est activé par défaut, et quand il réussit,
le reranking remplace le score de similarité sémantique par un score de
rang synthétique ((nombre_candidats - position) / nombre_candidats — pour
5 candidats : 1.0, 0.8, 0.6, 0.4, 0.2). Dans la configuration par défaut, le
nombre affiché reflète donc l'ordre de pertinence jugé par le LLM, pas une
mesure de similarité sémantique brute. Si le reranking échoue silencieusement
(provider non configuré, erreur d'appel), l'ordre et les scores d'origine
sont conservés — les seuils ci-dessous ne s'appliquent que dans ce cas, ou
avec useReranking: false :
- Score élevé (> 0.8) : Source très pertinente pour ce segment
- Score moyen (0.5-0.8) : Source potentiellement intéressante
- Score faible (< 0.5) : Source marginalement liée
- Indexez vos PDFs avant d'utiliser le Similarity Finder
- Choisissez la bonne granularité selon la longueur de votre texte
- Utilisez le filtrage par collection pour des résultats plus ciblés
- Relancez l'analyse après avoir modifié significativement votre texte
- Nécessite un provider d'embeddings configuré (Ollama, embarqué, ou un provider cloud) — plus une dépendance à Ollama spécifiquement
- Performance dépend de la taille du corpus et du texte analysé
- Cache invalidé si l'index vectoriel change