diff --git a/generations/library_rag/ANALYSE_ARCHITECTURE_WEAVIATE.md b/generations/library_rag/ANALYSE_ARCHITECTURE_WEAVIATE.md deleted file mode 100644 index 2aa2ac8..0000000 --- a/generations/library_rag/ANALYSE_ARCHITECTURE_WEAVIATE.md +++ /dev/null @@ -1,613 +0,0 @@ -# Analyse Architecture Weaviate - Library RAG - -**Date**: 2026-01-03 -**Dernier commit**: `b76e56e` - refactor: Suppression tous fonds beiges header section -**Status**: Production (13,829 vecteurs indexés) - ---- - -## 📋 Table des Matières - -1. [Vue d'Ensemble de la Base Weaviate](#1-vue-densemble-de-la-base-weaviate) -2. [Collections et leurs Relations](#2-collections-et-leurs-relations) -3. [Focus: Œuvre, Document, Chunk - La Hiérarchie Centrale](#3-focus-œuvre-document-chunk---la-hiérarchie-centrale) -4. [Stratégie de Recherche: Résumés → Chunks](#4-stratégie-de-recherche-résumés--chunks) -5. [Outils Weaviate: Utilisés vs Non-Utilisés](#5-outils-weaviate-utilisés-vs-non-utilisés) -6. [Recommandations pour Exploiter Weaviate à 100%](#6-recommandations-pour-exploiter-weaviate-à-100) -7. [Annexes Techniques](#7-annexes-techniques) - ---- - -## 1. Vue d'Ensemble de la Base Weaviate - -### 1.1 Architecture Générale - -Library RAG utilise **Weaviate 1.34.4** comme base vectorielle pour indexer et rechercher des textes philosophiques. L'architecture suit un modèle **normalisé avec dénormalisation stratégique** via nested objects. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ WEAVIATE DATABASE │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Work (0 objets) Document (16 objets) │ -│ └─ Métadonnées œuvre └─ Métadonnées édition │ -│ (vectorisé) (non vectorisé) │ -│ │ -│ Chunk (5,404 objets) ⭐ Summary (8,425 objets) │ -│ └─ Fragments vectorisés └─ Résumés vectorisés │ -│ COLLECTION PRINCIPALE (recherche hiérarchique) │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - -### 1.2 Statistiques Clés - -| Collection | Objets | Vectorisé | Taille estimée | Utilisation | -|------------|--------|-----------|----------------|-------------| -| **Chunk** | **5,404** | ✅ Oui (text + keywords) | ~3 MB | **Recherche sémantique principale** | -| **Summary** | **8,425** | ✅ Oui (text + concepts) | ~5 MB | Recherche hiérarchique par chapitres | -| **Document** | **16** | ❌ Non | ~10 KB | Métadonnées éditions | -| **Work** | **0** | ✅ Oui (title + author)* | 0 B | Prêt pour migration | - -**Total vecteurs**: 13,829 (5,404 chunks + 8,425 summaries) -**Ratio Summary/Chunk**: 1.56 (1.6 résumés par chunk, excellent pour recherche hiérarchique) - -\* *Work est configuré avec vectorisation depuis migration 2026-01 mais actuellement vide* - -### 1.3 Modèle de Vectorisation - -**Modèle**: BAAI/bge-m3 -**Dimensions**: 1024 -**Contexte**: 8192 tokens -**Langues supportées**: Grec ancien, Latin, Français, Anglais - -**Migration Dec 2024**: MiniLM-L6 (384-dim) → BGE-M3 (1024-dim) -- **Gain**: 2.7x plus riche en représentation sémantique -- **Performance**: Meilleure sur textes philosophiques/académiques -- **Multilingue**: Support natif grec/latin - ---- - -## 2. Collections et leurs Relations - -### 2.1 Architecture des Collections - -``` -Work (Œuvre philosophique) - │ - │ Nested in Document.work: {title, author} - │ Nested in Chunk.work: {title, author} - ▼ -Document (Édition/traduction spécifique) - │ - │ Nested in Chunk.document: {sourceId, edition} - │ Nested in Summary.document: {sourceId} - ▼ - ├─► Chunk (Fragments de texte, 200-800 chars) - │ └─ Vectorisé: text, keywords - │ └─ Filtres: sectionPath, unitType, orderIndex - │ - └─► Summary (Résumés de chapitres/sections) - └─ Vectorisé: text, concepts - └─ Hiérarchie: level (1=chapitre, 2=section, 3=subsection) -``` - -### 2.2 Collection Work (Œuvre) - -**Rôle**: Représente une œuvre philosophique canonique (ex: Ménon de Platon) - -**Propriétés**: -```python -title: TEXT (VECTORISÉ) # "Ménon", "République" -author: TEXT (VECTORISÉ) # "Platon", "Peirce" -originalTitle: TEXT [skip_vec] # "Μένων" (grec) -year: INT # -380 (avant J.-C.) -language: TEXT [skip_vec] # "gr", "la", "fr" -genre: TEXT [skip_vec] # "dialogue", "traité" -``` - -**Vectorisation**: Activée depuis 2026-01 -- ✅ `title` vectorisé → recherche "dialogues socratiques" trouve Ménon -- ✅ `author` vectorisé → recherche "philosophie analytique" trouve Haugeland - -**Status actuel**: Vide (0 objets), prêt pour migration - -### 2.3 Collection Document (Édition) - -**Rôle**: Représente une édition ou traduction spécifique d'une œuvre - -**Propriétés**: -```python -sourceId: TEXT # "platon_menon_cousin" -edition: TEXT # "trad. Cousin, 1844" -language: TEXT # "fr" (langue de cette édition) -pages: INT # 120 -chunksCount: INT # 338 (nombre de chunks extraits) -toc: TEXT (JSON) # Table des matières structurée -hierarchy: TEXT (JSON) # Hiérarchie complète des sections -createdAt: DATE # 2025-12-09T09:20:30 - -# Nested object -work: { - title: TEXT # "Ménon" - author: TEXT # "Platon" -} -``` - -**Vectorisation**: ❌ Non (métadonnées uniquement) - -### 2.4 Collection Chunk ⭐ (PRINCIPALE) - -**Rôle**: Fragments de texte optimisés pour recherche sémantique (200-800 caractères) - -**Propriétés vectorisées**: -```python -text: TEXT (VECTORISÉ) # Contenu du fragment -keywords: TEXT_ARRAY (VECTORISÉ) # ["justice", "vertu", "connaissance"] -``` - -**Propriétés de filtrage** (non vectorisées): -```python -sectionPath: TEXT [skip_vec] # "Présentation > Qu'est-ce que la vertu?" -sectionLevel: INT # 2 (profondeur hiérarchique) -chapterTitle: TEXT [skip_vec] # "Présentation" -canonicalReference: TEXT [skip_vec] # "Ménon 80a" ou "CP 5.628" -unitType: TEXT [skip_vec] # "argument", "définition", "exposition" -orderIndex: INT # 42 (position séquentielle 0-based) -language: TEXT [skip_vec] # "fr", "en", "gr" -``` - -**Nested objects** (dénormalisation): -```python -work: { - title: TEXT # "Ménon" - author: TEXT # "Platon" -} -document: { - sourceId: TEXT # "platon_menon_cousin" - edition: TEXT # "trad. Cousin" -} -``` - -**Exemple d'objet**: -```json -{ - "text": "SOCRATE. - Peux-tu me dire, Ménon, si la vertu peut s'enseigner?", - "keywords": ["vertu", "enseignement", "question socratique"], - "sectionPath": "Présentation > Qu'est-ce que la vertu?", - "sectionLevel": 2, - "chapterTitle": "Présentation", - "canonicalReference": "Ménon 70a", - "unitType": "argument", - "orderIndex": 0, - "language": "fr", - "work": { - "title": "Ménon ou de la vertu", - "author": "Platon" - }, - "document": { - "sourceId": "platon_menon_cousin", - "edition": "trad. Cousin" - } -} -``` - -### 2.5 Collection Summary (Résumés) - -**Rôle**: Résumés LLM de chapitres/sections pour recherche hiérarchique - -**Propriétés vectorisées**: -```python -text: TEXT (VECTORISÉ) # Résumé généré par LLM -concepts: TEXT_ARRAY (VECTORISÉ) # ["réminiscence", "anamnèse", "connaissance innée"] -``` - -**Propriétés de filtrage**: -```python -sectionPath: TEXT [skip_vec] # "Livre I > Chapitre 2" -title: TEXT [skip_vec] # "La réminiscence et la connaissance" -level: INT # 1=chapitre, 2=section, 3=subsection -chunksCount: INT # 15 (nombre de chunks dans cette section) -``` - ---- - -## 3. Focus: Œuvre, Document, Chunk - La Hiérarchie Centrale - -### 3.1 Modèle de Données - -L'architecture suit un modèle **normalisé avec dénormalisation stratégique** : - -``` -┌──────────────────────────────────────────────────────────────┐ -│ MODÈLE NORMALISÉ │ -├──────────────────────────────────────────────────────────────┤ -│ │ -│ Work (Source de vérité unique) │ -│ title: "Ménon ou de la vertu" │ -│ author: "Platon" │ -│ year: -380 │ -│ language: "gr" │ -│ genre: "dialogue" │ -│ │ -│ ├─► Document 1 (trad. Cousin) │ -│ │ sourceId: "platon_menon_cousin" │ -│ │ edition: "trad. Cousin, 1844" │ -│ │ language: "fr" │ -│ │ pages: 120 │ -│ │ chunksCount: 338 │ -│ │ work: {title, author} ← DÉNORMALISÉ │ -│ │ │ -│ │ ├─► Chunk 1 │ -│ │ │ text: "Peux-tu me dire, Ménon..." │ -│ │ │ work: {title, author} ← DÉNORMALISÉ │ -│ │ │ document: {sourceId, edition} ← DÉNORMALISÉ │ -│ │ │ │ -│ │ ├─► Chunk 2... │ -│ │ └─► Chunk 338 │ -│ │ │ -│ │ ├─► Summary 1 (chapitre 1) │ -│ │ │ text: "Cette section explore..." │ -│ │ │ level: 1 │ -│ │ │ document: {sourceId} ← DÉNORMALISÉ │ -│ │ │ │ -│ │ └─► Summary N... │ -│ │ │ -│ └─► Document 2 (Loeb Classical Library) │ -│ sourceId: "plato_meno_loeb" │ -│ edition: "Loeb Classical Library" │ -│ language: "en" │ -│ ... (même structure) │ -│ │ -└──────────────────────────────────────────────────────────────┘ -``` - -### 3.2 Pourquoi Nested Objects au lieu de Cross-References ? - -**Avantages**: -1. ✅ **Requête unique** - Récupération en une seule requête sans joins -2. ✅ **Performance** - Pas de jointures complexes côté application -3. ✅ **Simplicité** - Logique de requête plus simple -4. ✅ **Cache-friendly** - Toutes les métadonnées dans un seul objet - -**Trade-off**: -- Pour 5,404 chunks: ~200 KB de duplication -- Économie de temps: ~50-100ms par requête (évite 2 roundtrips Weaviate) - ---- - -## 4. Stratégie de Recherche: Résumés → Chunks - -### 4.1 Pourquoi Deux Collections Vectorisées ? - -**Problème**: Chercher directement dans 5,404 chunks peut manquer le contexte global - -**Solution**: Architecture à deux niveaux - -``` -┌─────────────────────────────────────────────────────────────┐ -│ RECHERCHE À DEUX NIVEAUX │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Niveau 1: MACRO (Summary - 8,425 objets) │ -│ "Quels chapitres parlent de la réminiscence?" │ -│ └─► Identifie: Chapitre 2, Section 3 │ -│ │ -│ Niveau 2: MICRO (Chunk - 5,404 objets) │ -│ "Quelle est la définition exacte de l'anamnèse?" │ -│ └─► Trouve: Chunk #42 dans la section identifiée │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - -**Avantages**: -1. ✅ Meilleure précision (contexte chapitres + détails chunks) -2. ✅ Performance optimale (filtrer chunks par section identifiée) -3. ✅ Hiérarchie exploitée (level 1=chapitre, 2=section, 3=subsection) - -### 4.2 Stratégies de Recherche Implémentables - -#### Stratégie 1: Sequential Search (Résumés puis Chunks) - -**Cas d'usage**: Recherche approfondie avec contexte - -```python -# 1. Chercher dans Summary (macro) -summaries = client.collections.get("Summary") -summary_results = summaries.query.near_text( - query="théorie de la réminiscence", - limit=5, - filters=Filter.by_property("level").equal(1) # Chapitres uniquement -) - -# 2. Extraire sections pertinentes -relevant_sections = [ - s.properties['sectionPath'] - for s in summary_results.objects -] - -# 3. Chercher chunks dans ces sections (micro) -chunks = client.collections.get("Chunk") -chunk_results = chunks.query.near_text( - query="qu'est-ce que l'anamnèse?", - limit=10, - filters=Filter.by_property("sectionPath").like(f"{relevant_sections[0]}*") -) -``` - -**Performance**: 2 requêtes (~50ms chacune) = 100ms total - -#### Stratégie 2: Hybrid Two-Stage avec Score Boosting - -**Algorithme recommandé pour production**: - -```python -def hybrid_search(query: str, limit: int = 10) -> List[ChunkResult]: - """Recherche hybride résumés → chunks avec boosting.""" - - # Stage 1: Summary search (macro) - summaries = client.collections.get("Summary") - summary_results = summaries.query.near_text( - query=query, - limit=3, # Top 3 chapitres - filters=Filter.by_property("level").less_or_equal(2), - return_metadata=wvq.MetadataQuery(distance=True) - ) - - # Stage 2: Chunk search avec boost par section - chunks = client.collections.get("Chunk") - all_chunks = [] - - for summary in summary_results.objects: - section_path = summary.properties['sectionPath'] - summary_score = 1 - summary.metadata.distance - - # Chercher chunks dans cette section - chunk_results = chunks.query.near_text( - query=query, - limit=5, - filters=Filter.by_property("sectionPath").like(f"{section_path}*"), - return_metadata=wvq.MetadataQuery(distance=True) - ) - - # Booster le score des chunks - for chunk in chunk_results.objects: - chunk_score = 1 - chunk.metadata.distance - boosted_score = (chunk_score * 0.7) + (summary_score * 0.3) - - all_chunks.append({ - 'chunk': chunk, - 'score': boosted_score, - 'context_chapter': section_path - }) - - # Trier par score boosted - all_chunks.sort(key=lambda x: x['score'], reverse=True) - return all_chunks[:limit] -``` - -**Impact**: +15-20% précision, ~120ms latence - ---- - -## 5. Outils Weaviate: Utilisés vs Non-Utilisés - -### 5.1 Outils Actuellement Utilisés ✅ - -1. **Semantic Search (near_text)** - Recherche sémantique principale -2. **Filters (Nested Objects)** - Filtrage par author, work, language -3. **Fetch Objects** - Récupération par ID -4. **Batch Insertion** - Insertion groupée adaptative (10-100 objets) -5. **Delete Many** - Suppression en masse - -### 5.2 Outils Weaviate NON Utilisés ❌ - -#### 1. Hybrid Search (Sémantique + BM25) ⚠️ **HAUTE PRIORITÉ** - -**Qu'est-ce que c'est?** -Combine recherche vectorielle (sémantique) + BM25 (mots-clés exacts) - -**Exemple d'implémentation**: -```python -result = chunks.query.hybrid( - query="qu'est-ce que la vertu?", - alpha=0.75, # 75% vectoriel, 25% BM25 - limit=10, - filters=filters, -) -``` - -**Impact attendu**: +10-15% précision sur requêtes factuelles - -#### 2. Generative Search (RAG natif) 🚨 **HAUTE PRIORITÉ** - -**Qu'est-ce que c'est?** -Weaviate génère directement une réponse synthétique à partir des chunks - -**Exemple**: -```python -result = chunks.generate.near_text( - query="qu'est-ce que la réminiscence chez Platon?", - limit=5, - grouped_task="Réponds à la question en utilisant ces 5 passages", -) - -# Résultat contient: -# - result.objects: chunks trouvés -# - result.generated: réponse LLM générée -``` - -**Impact**: Réduction 50% latence end-to-end (RAG complet en une requête) - -#### 3. Reranking (Cohere, Voyage AI) ⚠️ **MOYENNE PRIORITÉ** - -Re-score les résultats avec un modèle spécialisé - -**Impact**: +15-20% précision top-3, +50-100ms latence - -#### 4. RAG Fusion (Multi-Query Search) ⚠️ **MOYENNE PRIORITÉ** - -Génère N variantes de la requête et fusionne les résultats - -**Impact**: +20-25% recall - -### 5.3 Matrice Priorités - -| Outil | Priorité | Difficulté | Impact Précision | Impact Latence | Coût | -|-------|----------|------------|------------------|----------------|------| -| **Hybrid Search** | 🔴 Haute | Faible (1h) | +10-15% | +5ms | Gratuit | -| **Generative Search** | 🔴 Haute | Moyenne (3h) | +30% (RAG E2E) | -50% E2E | LLM API | -| **Reranking** | 🟡 Moyenne | Faible (2h) | +15-20% top-3 | +50-100ms | $0.001/req | -| **RAG Fusion** | 🟡 Moyenne | Moyenne (4h) | +20-25% recall | x3 requêtes | Gratuit | - ---- - -## 6. Recommandations pour Exploiter Weaviate à 100% - -### 6.1 Quick Wins (1-2 jours d'implémentation) - -#### Quick Win #1: Activer Hybrid Search - -**Fichier à modifier**: `schema.py` - -```python -# Ajouter index BM25 -wvc.Property( - name="text", - data_type=wvc.DataType.TEXT, - index_searchable=True, # ← Active BM25 -) -``` - -**Fichier à modifier**: `mcp_tools/retrieval_tools.py` - -```python -# Remplacer near_text par hybrid -result = chunks.query.hybrid( - query=input_data.query, - alpha=0.75, # 75% vectoriel, 25% BM25 - limit=input_data.limit, - filters=filters, -) -``` - -**Impact**: +10% précision, <5ms surcoût - -#### Quick Win #2: Implémenter Two-Stage Search - -Créer `utils/two_stage_search.py` avec l'algorithme hybrid boosting (voir section 4.2) - -**Impact**: +15-20% précision, ~120ms latence - -### 6.2 High-Impact Features (1 semaine d'implémentation) - -#### Feature #1: Generative Search (RAG Natif) - -**Étape 1**: Activer module dans docker-compose.yml - -```yaml -services: - weaviate: - environment: - GENERATIVE_ANTHROPIC_APIKEY: ${ANTHROPIC_API_KEY} -``` - -**Étape 2**: Endpoint Flask - -```python -@app.route("/search/generative", methods=["GET"]) -def search_generative(): - query = request.args.get("q", "") - - chunks = client.collections.get("Chunk") - result = chunks.generate.near_text( - query=query, - limit=5, - grouped_task=f"Réponds à: {query}. Utilise les passages fournis.", - ) - - return jsonify({ - "answer": result.generated, - "sources": [...] - }) -``` - -**Impact**: RAG complet en une requête, -50% latence E2E - ---- - -## 7. Annexes Techniques - -### 7.1 Exemple de Requête Complète - -```python -import weaviate -from weaviate.classes.query import Filter - -client = weaviate.connect_to_local() -chunks = client.collections.get("Chunk") - -# Recherche: "vertu" chez Platon en français -result = chunks.query.near_text( - query="qu'est-ce que la vertu?", - limit=10, - filters=( - Filter.by_property("work").by_property("author").equal("Platon") & - Filter.by_property("language").equal("fr") - ), - return_metadata=wvq.MetadataQuery(distance=True) -) - -for obj in result.objects: - props = obj.properties - similarity = 1 - obj.metadata.distance - - print(f"Similarité: {similarity:.3f}") - print(f"Texte: {props['text'][:100]}...") - print(f"Œuvre: {props['work']['title']}") - print(f"Référence: {props['canonicalReference']}") - print("---") - -client.close() -``` - -### 7.2 Glossaire Weaviate - -| Terme | Définition | -|-------|------------| -| **Collection** | Équivalent d'une "table" en SQL | -| **Object** | Une entrée dans une collection | -| **Vector** | Représentation numérique (1024-dim pour BGE-M3) | -| **near_text** | Recherche sémantique par similarité | -| **hybrid** | Recherche combinée (vectorielle + BM25) | -| **Nested Object** | Objet imbriqué (ex: `work: {title, author}`) | -| **HNSW** | Index vectoriel performant | -| **RQ** | Rotational Quantization (-75% RAM) | - ---- - -## Conclusion - -### Points Clés - -1. **Architecture solide** - 4 collections avec nested objects -2. **13,829 vecteurs** - Base de production opérationnelle -3. **Ratio 1.56 Summary/Chunk** - Excellent pour recherche hiérarchique -4. **Utilisation 30%** - Beaucoup de potentiel non exploité - -### Roadmap Recommandée - -**Q1 2026** (Quick Wins): -1. Hybrid Search (1 jour) -2. Two-Stage Search (3 jours) -3. Métriques/monitoring (2 jours) - -**Q2 2026** (High Impact): -1. Generative Search (1 semaine) -2. Reranking (3 jours) -3. Semantic caching (3 jours) - ---- - -**Dernière mise à jour**: 2026-01-03 -**Version**: 1.0 diff --git a/generations/library_rag/ANALYSE_RAG_FINAL.md b/generations/library_rag/ANALYSE_RAG_FINAL.md deleted file mode 100644 index b5ad77a..0000000 --- a/generations/library_rag/ANALYSE_RAG_FINAL.md +++ /dev/null @@ -1,206 +0,0 @@ -# Analyse Finale du Système RAG Library - État au 2026-01-03 - -## Résumé Exécutif - -Le système RAG a été considérablement amélioré grâce à la génération de résumés LLM de haute qualité. La recherche dans la collection **Summary** fonctionne excellemment (90% de visibilité des documents riches). Cependant, la recherche dans la collection **Chunk** souffre d'une domination écrasante des chunks Peirce (97% de la base), rendant les autres documents pratiquement introuvables. - -## État de la Base de Données - -### Collection Summary -- **Total**: 114 résumés -- **Riches** (>100 chars): 106 résumés -- **Vides** (titres): 8 résumés - -**Répartition par document:** -- Tiercelin: 51 résumés (43 riches) -- Haugeland: 50 résumés -- Platon: 12 résumés -- La logique de la science: 1 résumé - -**Performance de recherche**: 90% de visibilité (54/60 résultats sur 15 requêtes réelles) - -### Collection Chunk -- **Total**: 5,230 chunks -- **Peirce**: 5,068 chunks (97%) -- **Haugeland**: 50 chunks (1%) -- **Platon**: 50 chunks (1%) -- **Tiercelin**: 36 chunks (0.7%) -- **Autres**: 26 chunks (0.5%) - -**Ratio problématique**: 97:3 (Peirce:Autres) - -## Travaux Réalisés - -### Phase 1: Génération des Résumés -| Document | Résumés | Coût | Statut | -|----------|---------|------|--------| -| Tiercelin | 43 | $0.63 | ✅ Complet | -| Platon | 12 | $0.14 | ✅ Complet | -| La logique de la science | 1 | $0.02 | ✅ Complet | -| Haugeland | 50 | $0.44 | ✅ Complet | -| **TOTAL** | **106** | **$1.23** | **✅ Complet** | - -### Phase 2: Nettoyage de la Base -1. **Suppression de 7 doublons vides** (Tiercelin) -2. **Suppression de 8,313 résumés vides Peirce** - - Avant: 10% de visibilité - - Après: 63% → 90% (avec Haugeland) - -## Performance par Type de Recherche - -### ✅ Recherche dans Summary (EXCELLENT) -**15 requêtes réelles testées** couvrant 5 domaines: -- Pragmatisme/Peirce (3 requêtes) -- Platon/Vertu (3 requêtes) -- IA/Philosophie de l'esprit (3 requêtes) -- Sémiotique (3 requêtes) -- Épistémologie (3 requêtes) - -**Résultat**: 90% de visibilité des résumés riches (54/60 résultats) - -**Exemple de qualité**: -- Query: "Can virtue be taught according to Plato?" -- Top 3: Tous Platon, similarité 71-72% -- Résumés pertinents et informatifs - -### ❌ Recherche dans Chunks (PROBLÉMATIQUE) - -#### Test 1: Questions génériques sur l'IA (domaine de Haugeland) -**10 requêtes AI-spécifiques**: -- "What is the Turing test?" -- "Can machines think?" -- "What is a physical symbol system?" -- "How do connectionist networks work?" -- etc. - -**Résultats (50 total)**: -- 🔴 Peirce: 44/50 (88%) -- 🟣 Haugeland: 5/50 (10%) -- 🟢 Platon: 1/50 (2%) - -**Conclusion**: Même sur son domaine propre, Haugeland est écrasé. - -#### Test 2: Recherche hiérarchique (Summary → Chunks) -**Stratégie**: -1. Identifier documents pertinents via Summary (fonctionne bien) -2. Filtrer chunks de ces documents (échoue - Peirce domine toujours) - -**Exemple**: -- Query: "How do connectionist networks work?" -- Summary identifie correctement: Haugeland "Connectionist networks" -- Mais Chunk search retourne: 5/5 chunks Peirce (0/5 Haugeland) - -**Limitation technique**: Weaviate v4 ne permet pas de filtrer par nested objects dans les requêtes → filtrage en Python après récupération. - -## Problème Central - -### Domination des Chunks Peirce -**Cause**: 5,068 chunks Peirce sur 5,230 total (97%) - -**Impact**: -- Les chunks Peirce ont des similarités sémantiques élevées (73-77%) sur presque toutes les requêtes -- Ratio trop déséquilibré pour laisser apparaître d'autres documents -- Même la recherche hiérarchique ne résout pas le problème - -**Contrainte utilisateur**: -> "NE SUPPRIME PAS LES CHUNKLS D EPEIRCE BORDEL" - -Pas de suppression des chunks Peirce permise. - -## Solutions Proposées - -### Option A: Summary comme Interface Principale (RECOMMANDÉ) -**Statut**: Prouvé et fonctionnel (90% de visibilité) - -**Avantages**: -- ✅ Fonctionne immédiatement (déjà testé) -- ✅ Coût: $0 (déjà implémenté) -- ✅ Performance excellente démontrée -- ✅ Interface utilisateur claire - -**Mise en œuvre**: -```python -# Recherche primaire dans Summary -summary_results = summaries.query.near_text( - query=user_query, - limit=10, - return_metadata=wvq.MetadataQuery(distance=True) -) - -# Afficher résumés avec contexte -for result in summary_results: - print(f"Document: {result.properties['document']['sourceId']}") - print(f"Section: {result.properties['title']}") - print(f"Résumé: {result.properties['text']}") - print(f"Concepts: {', '.join(result.properties['concepts'])}") -``` - -**Flux utilisateur**: -1. User pose une question -2. Système retourne résumés pertinents (comme Google Scholar) -3. User peut cliquer pour voir les chunks détaillés d'une section - -### Option B: Système Hybride -**Statut**: Nécessite développement - -**Fonctionnalités**: -- Toggle "Recherche par résumés" / "Recherche détaillée" -- Mode résumés par défaut (pour découverte) -- Mode chunks pour requêtes très précises - -**Coût**: ~2-3 jours de développement UI - -### Option C: Régénération Résumés Peirce -**Statut**: Non implémenté - -**Estimation**: -- 5,068 chunks → ~500-600 sections -- Regroupement intelligent nécessaire -- Coût: $45-50 -- Temps: 15-20 heures (génération + ingestion) - -**Risque**: Peut ne pas résoudre le problème si les résumés Peirce restent sémantiquement proches de toutes les requêtes. - -## Tests Disponibles - -Tous les scripts de test sont dans `generations/library_rag/`: - -1. **test_summaries_validation.py** - Validation complète des résumés -2. **test_real_queries.py** - 15 requêtes réelles sur Summary -3. **test_hierarchical_search.py** - Test Summary → Chunks -4. **test_haugeland_ai.py** - Test spécifique IA (domaine Haugeland) - -## Recommandation Finale - -**Implémenter Option A immédiatement**: -1. Interface de recherche principale sur Summary -2. 90% de visibilité déjà prouvée -3. Coût $0, temps < 1 jour -4. Respecte la contrainte (pas de suppression chunks Peirce) - -**Future améliorations** (optionnel): -- Option B: Ajouter mode hybride si demandé -- Option C: Considérer seulement si vraiment nécessaire - -## Statistiques Finales - -### Coûts Totaux -- Génération résumés: $1.23 -- Suppression données vides: $0 -- **Total projet**: $1.23 - -### Résultats -- 106 résumés riches de haute qualité -- 90% de visibilité en recherche Summary -- Base de données propre et optimisée -- Interface de recherche fonctionnelle - -### Performance -- Summary search: 90% pertinence ✅ -- Chunk search: 10% pertinence ❌ (mais solution identifiée) - ---- - -**Date**: 2026-01-03 -**Système**: Weaviate 1.34.4 + BGE-M3 (1024-dim) -**LLM**: Claude Sonnet 4.5 (résumés) + text2vec-transformers (vectorisation) diff --git a/generations/library_rag/ANALYSE_RESULTATS_RESUME.md b/generations/library_rag/ANALYSE_RESULTATS_RESUME.md deleted file mode 100644 index f691669..0000000 --- a/generations/library_rag/ANALYSE_RESULTATS_RESUME.md +++ /dev/null @@ -1,436 +0,0 @@ -# Analyse des Résultats de Recherche - Collection Summary - -**Date**: 2026-01-03 -**Requête**: "Peirce et la sémiose" -**Collection**: Summary (8,425 objets) -**Résultats retournés**: 20 - ---- - -## 📊 Statistiques Globales - -| Métrique | Valeur | Évaluation | -|----------|--------|------------| -| **Total résultats** | 20 | ✅ Bon | -| **Similarité moyenne** | 0.716 | ⚠️ Moyenne (< 0.75) | -| **Meilleur score** | 0.723 | ⚠️ Faible pour top-1 | -| **Plus mauvais score** | 0.713 | ⚠️ Très faible | -| **Niveau hiérarchique** | 100% Level 1 | ❌ Pas de diversité | -| **Documents sources** | 1 seul | ❌ Pas de diversité | - ---- - -## 🚨 Problèmes Critiques Identifiés - -### 1. Résumés Vides (CRITIQUE) - -**Observation**: Tous les 20 résumés ont un champ `text` vide ou minimal. - -**Exemple**: -``` -Résumé: Peirce: CP 3.592 -``` - -**Attendu**: -``` -Résumé: Ce passage explore la théorie peircéenne de la sémiose comme processus -triadique impliquant le signe (representamen), l'objet et l'interprétant. -Peirce développe l'idée que la signification n'est jamais binaire mais -nécessite toujours cette relation ternaire irréductible... -``` - -**Impact**: -- ❌ La recherche ne peut pas matcher le contenu sémantique réel -- ❌ Les résumés ne servent à rien (pas de contexte) -- ❌ Impossible d'identifier les sections pertinentes - -**Cause probable**: -- Les Summary n'ont jamais été remplis avec de vrais résumés LLM -- Le pipeline d'ingestion a sauté l'étape de génération de résumés -- OU les résumés ont été générés mais pas insérés dans Weaviate - -### 2. Concepts Vides (CRITIQUE) - -**Observation**: Le champ `concepts` est vide pour tous les résumés. - -**Exemple**: -``` -Concepts: -``` - -**Attendu**: -``` -Concepts: sémiose, triade, signe, interprétant, représentamen, objet, signification -``` - -**Impact**: -- ❌ Impossible de filtrer par concepts philosophiques -- ❌ Perte d'une dimension sémantique clé -- ❌ Les résumés ne peuvent pas booster la recherche - -### 3. Pas de Chunks Associés (CRITIQUE) - -**Observation**: Tous les résumés ont `chunksCount: 0`. - -**Exemple**: -``` -Chunks dans cette section: 0 -``` - -**Attendu**: -``` -Chunks dans cette section: 15-50 -``` - -**Impact**: -- ❌ Les résumés ne sont pas liés aux chunks -- ❌ Impossible de faire une recherche hiérarchique (Summary → Chunk) -- ❌ La stratégie two-stage est cassée - -**Cause probable**: -- Les Summary ont été créés mais sans lien avec les Chunks -- Le champ `document.sourceId` dans Summary ne match pas avec `document.sourceId` dans Chunk -- OU les Summary ont été créés pour des sections qui n'ont pas de chunks - -### 4. Similarité Faible (ALERTE) - -**Observation**: Scores entre 0.713 et 0.723. - -**Analyse**: -| Score | Interprétation | -|-------|----------------| -| > 0.90 | Excellent match | -| 0.80-0.90 | Bon match | -| 0.70-0.80 | Match moyen | -| **0.71-0.72** | **Match faible** ⚠️ | -| < 0.70 | Pas pertinent | - -**Pourquoi c'est faible ?** -- Le modèle BGE-M3 match uniquement sur "Peirce: CP X.XXX" (titre) -- Pas de contenu sémantique à matcher -- La requête "Peirce et la sémiose" ne trouve que "Peirce" dans le titre - -**Comparaison attendue**: -- Avec vrais résumés: scores 0.85-0.95 -- Avec concepts remplis: boost de +0.05-0.10 - -### 5. Pas de Diversité Hiérarchique (ALERTE) - -**Observation**: 100% des résultats sont Level 1 (chapitres). - -**Distribution**: -``` -Chapitre (Level 1): 20 résultats (100%) -Section (Level 2): 0 résultats (0%) -Subsection (Level 3): 0 résultats (0%) -``` - -**Impact**: -- ❌ Pas de navigation hiérarchique -- ❌ Tous les résultats au même niveau de granularité -- ❌ Impossible de drill-down dans les sous-sections - -**Cause probable**: -- Les Summary ont été créés uniquement pour les Level 1 -- Le pipeline n'a pas généré de résumés pour Level 2/3 - -### 6. Un Seul Document Source (ALERTE) - -**Observation**: 100% des résultats viennent de `peirce_collected_papers_fixed`. - -**Impact**: -- ⚠️ Pas de diversité (autres auteurs sur la sémiose ignorés) -- ⚠️ Biais vers Peirce (normal pour la requête, mais limite les perspectives) - -**Note**: Ceci peut être acceptable car la requête contient "Peirce", mais d'autres documents comme "Tiercelin - La pensée-signe" devraient aussi matcher. - ---- - -## 🔍 Analyse Détaillée des Résultats - -### Top 5 Résultats - -#### [1] CP 3.592 - Similarité: 0.723 - -**Référence Peirce**: CP 3.592 (Collected Papers, Volume 3, §592) - -**Contenu actuel**: VIDE (juste "Peirce: CP 3.592") - -**Ce que CP 3.592 devrait contenir** (selon index Peirce): -- Volume 3 = Exact Logic -- Section probable: Théorie des signes ou logique des relations -- Contenu attendu: Discussion sur la triplicité du signe - -**Action requise**: Vérifier le JSON source `peirce_collected_papers_fixed_chunks.json` pour voir si le résumé existe. - -#### [2] CP 2.439 - Similarité: 0.719 - -**Référence**: CP 2.439 (Volume 2 = Elements of Logic) - -**Contenu attendu**: Probablement sur la classification des signes ou la sémiotique. - -#### [3] CP 2.657 - Similarité: 0.718 - -**Référence**: CP 2.657 (Volume 2) - -**Contenu attendu**: Classification des arguments ou inférence. - -#### [4] CP 5.594 - Similarité: 0.717 - -**Référence**: CP 5.594 (Volume 5 = Pragmatism and Pragmaticism) - -**Contenu attendu**: Relation entre pragmatisme et théorie des signes. - -#### [5] CP 4.656 - Similarité: 0.717 - -**Référence**: CP 4.656 (Volume 4 = The Simplest Mathematics) - -**Contenu attendu**: Logique mathématique ou théorie des relations. - -### Distribution par Volume Peirce - -| Volume | Résultats | Thématique principale | -|--------|-----------|----------------------| -| **CP 2** | 7 | Elements of Logic (forte pertinence) | -| **CP 3** | 3 | Exact Logic (pertinence moyenne) | -| **CP 4** | 2 | Mathematics (faible pertinence) | -| **CP 5** | 4 | Pragmatism (pertinence moyenne) | -| **CP 7** | 4 | Science and Philosophy (faible pertinence) | - -**Analyse**: Les résultats du Volume 2 (Elements of Logic) sont les plus pertinents pour "sémiose", ce qui est cohérent. - ---- - -## 🛠️ Diagnostic Technique - -### Vérification 1: Les Summary existent-ils dans Weaviate ? - -```python -import weaviate - -client = weaviate.connect_to_local() -summaries = client.collections.get("Summary") - -# Compter objets -count = summaries.aggregate.over_all(total_count=True) -print(f"Total Summary: {count.total_count}") # Attendu: 8,425 - -# Vérifier un objet au hasard -result = summaries.query.fetch_objects(limit=1) -obj = result.objects[0].properties -print(f"Exemple Summary:") -print(f" text: '{obj.get('text', 'VIDE')}'") -print(f" concepts: {obj.get('concepts', [])}") -print(f" chunksCount: {obj.get('chunksCount', 0)}") -``` - -**Résultat attendu**: 8,425 objets existent, mais avec champs vides. - -### Vérification 2: Comparer avec les Chunks - -```python -chunks = client.collections.get("Chunk") - -# Chercher chunks sur "sémiose" -result = chunks.query.near_text( - query="Peirce et la sémiose", - limit=10 -) - -for obj in result.objects: - props = obj.properties - similarity = 1 - obj.metadata.distance - print(f"Similarité: {similarity:.3f}") - print(f"Texte: {props['text'][:100]}...") - print(f"Section: {props['sectionPath']}") - print("---") -``` - -**Hypothèse**: Les Chunks devraient avoir de meilleurs scores (0.85-0.95) car ils contiennent le vrai contenu. - -### Vérification 3: Inspecter le JSON source - -```bash -# Vérifier si les résumés existent dans le JSON -jq '.summaries | length' output/peirce_collected_papers_fixed/peirce_collected_papers_fixed_chunks.json - -# Afficher un résumé -jq '.summaries[0]' output/peirce_collected_papers_fixed/peirce_collected_papers_fixed_chunks.json -``` - -**Hypothèses possibles**: -1. ✅ Les résumés existent dans le JSON mais n'ont pas été insérés dans Weaviate -2. ✅ Les résumés ont été insérés mais avec des champs vides -3. ❌ Les résumés n'ont jamais été générés (pipeline incomplet) - ---- - -## 📋 Plan d'Action Recommandé - -### Phase 1: Diagnostic Approfondi (30 min) - -1. **Vérifier le JSON source**: - ```bash - cd output/peirce_collected_papers_fixed - cat peirce_collected_papers_fixed_chunks.json | jq '.summaries[0:3]' - ``` - -2. **Vérifier un Summary dans Weaviate**: - ```python - # Dans test_resume.py, ajouter après la recherche: - print("\n=== INSPECTION DÉTAILLÉE ===") - summaries = client.collections.get("Summary") - result = summaries.query.fetch_objects( - filters=Filter.by_property("document").by_property("sourceId").equal("peirce_collected_papers_fixed"), - limit=5 - ) - for obj in result.objects: - print(f"UUID: {obj.uuid}") - print(f"Text length: {len(obj.properties.get('text', ''))}") - print(f"Concepts count: {len(obj.properties.get('concepts', []))}") - print(f"ChunksCount: {obj.properties.get('chunksCount', 0)}") - print("---") - ``` - -3. **Comparer avec Chunk**: - - Chercher "sémiose" dans Chunk - - Comparer les scores de similarité - -### Phase 2: Correction selon Diagnostic (1-4h) - -**Scénario A**: Les résumés existent dans le JSON mais pas dans Weaviate - -```bash -# Ré-injecter uniquement les Summary -python utils/weaviate_ingest.py --reingest-summaries --doc peirce_collected_papers_fixed -``` - -**Scénario B**: Les résumés dans Weaviate sont corrompus - -```python -# Supprimer et recréer les Summary pour ce document -from utils.weaviate_ingest import delete_summaries, ingest_summaries - -delete_summaries("peirce_collected_papers_fixed") -ingest_summaries("peirce_collected_papers_fixed") -``` - -**Scénario C**: Les résumés n'ont jamais été générés - -```bash -# Régénérer les résumés avec LLM -python utils/llm_summarizer.py --doc peirce_collected_papers_fixed --force -python utils/weaviate_ingest.py --doc peirce_collected_papers_fixed --summaries-only -``` - -### Phase 3: Validation (30 min) - -1. **Ré-exécuter test_resume.py**: - ```bash - python test_resume.py - ``` - -2. **Vérifier les améliorations**: - - Scores de similarité: 0.85-0.95 attendu - - Texte résumé: 100-500 caractères attendu - - Concepts: 5-15 mots-clés attendus - - ChunksCount: > 0 attendu - -3. **Tester la recherche two-stage**: - ```python - # Créer test_two_stage.py - from utils.two_stage_search import hybrid_search - - results = hybrid_search("Peirce et la sémiose", limit=10) - # Vérifier que ça fonctionne maintenant - ``` - ---- - -## 🎯 Résultats Attendus Après Correction - -### Exemple de Résultat Idéal - -``` -[1] Similarité: 0.942 | Level: 2 -Titre: La sémiose et les catégories phanéroscopiques -Section: Peirce: CP 5.314 > La sémiose et les catégories -Document: peirce_collected_papers_fixed -Concepts: sémiose, triade, signe, interprétant, représentamen, objet, priméité, secondéité, tiercéité - -Résumé: - Ce passage fondamental expose la théorie peircéenne de la sémiose comme - processus triadique irréductible. Peirce articule la relation entre signe - (representamen), objet et interprétant avec ses trois catégories universelles: - la Priméité (qualité pure), la Secondéité (réaction) et la Tiercéité (médiation). - La sémiose est définie comme un processus potentiellement infini où chaque - interprétant devient à son tour un nouveau signe, créant une chaîne sémiotique - sans fin. Cette conception s'oppose radicalement aux théories binaires du signe - (signifiant/signifié) et fonde l'épistémologie pragmatiste de Peirce. - - Chunks dans cette section: 23 -``` - -**Améliorations**: -- ✅ Similarité: 0.723 → 0.942 (+30%) -- ✅ Texte: 13 chars → 600 chars -- ✅ Concepts: 0 → 9 -- ✅ ChunksCount: 0 → 23 -- ✅ Niveau: Toujours 1 mais avec vrais sous-niveaux possibles - ---- - -## 📊 Comparaison Avant/Après (Projeté) - -| Métrique | Avant | Après | Gain | -|----------|-------|-------|------| -| **Similarité moyenne** | 0.716 | 0.88 | +23% | -| **Texte moyen** | 13 chars | 350 chars | +2600% | -| **Concepts moyens** | 0 | 7 | +∞ | -| **ChunksCount moyen** | 0 | 18 | +∞ | -| **Utilité recherche** | 10% | 95% | +850% | - ---- - -## 🔗 Documents Liés - -- `ANALYSE_ARCHITECTURE_WEAVIATE.md` - Architecture complète de la base -- `WEAVIATE_GUIDE_COMPLET.md` - Guide d'utilisation Weaviate -- `test_resume.py` - Script de test (ce fichier a généré l'analyse) -- `resultats_resume.txt` - Résultats bruts de la recherche - ---- - -## 🎓 Conclusion - -### État Actuel: ❌ COLLECTION SUMMARY NON FONCTIONNELLE - -La collection Summary existe (8,425 objets) mais est **inutilisable** pour la recherche car: -1. Les résumés sont vides (juste des titres) -2. Les concepts sont absents -3. Pas de lien avec les Chunks (chunksCount=0) -4. Scores de similarité très faibles (0.71-0.72) - -### Impact sur l'Architecture RAG - -**Stratégie Two-Stage cassée**: -- ❌ Impossible de faire Summary → Chunk -- ❌ Pas de recherche hiérarchique -- ✅ Chunk search seul fonctionne (mais perd le contexte) - -**Solution de contournement actuelle**: -- Utiliser uniquement la recherche directe dans Chunk -- Ignorer complètement Summary -- Perdre 8,425 vecteurs (~60% de la base) - -### Priorité: 🔴 HAUTE - -Cette correction est **critique** pour exploiter l'architecture à deux niveaux de Library RAG. - -**ROI attendu**: +30% précision, recherche hiérarchique fonctionnelle, 60% de la base vectorielle activée. - ---- - -**Dernière mise à jour**: 2026-01-03 -**Auteur**: Analyse automatisée -**Version**: 1.0 diff --git a/generations/library_rag/COMPLETE_SESSION_RECAP.md b/generations/library_rag/COMPLETE_SESSION_RECAP.md deleted file mode 100644 index f5dcf01..0000000 --- a/generations/library_rag/COMPLETE_SESSION_RECAP.md +++ /dev/null @@ -1,445 +0,0 @@ -# Session Complète - RAG Library Optimization - -**Date**: 2026-01-03 -**Durée**: Session complète -**Objectif**: Résoudre le problème de dominance des chunks Peirce et intégrer une solution dans Flask - ---- - -## 📋 Table des Matières - -1. [Problème Initial](#problème-initial) -2. [Travaux Préliminaires](#travaux-préliminaires) -3. [Solution Développée](#solution-développée) -4. [Intégration Flask](#intégration-flask) -5. [Livrables](#livrables) -6. [Résultats](#résultats) - ---- - -## Problème Initial - -### État de la Base de Données -- **Chunk Collection**: 5,230 chunks total - - Peirce: 5,068 chunks (97%) - - Autres: 162 chunks (3%) - -### Impact -- Recherche directe dans Chunks: **10% de visibilité** pour documents riches -- Même sur requêtes ultra-spécifiques (ex: "What is the Turing test?"), Peirce domine 88% des résultats -- Haugeland n'apparaît que dans 10% des résultats sur son propre domaine (IA) - -### Contrainte Utilisateur -> **"NE SUPPRIME PAS LES CHUNKLS D EPEIRCE BORDEL"** - -❌ Pas de suppression des chunks Peirce permise - ---- - -## Travaux Préliminaires - -### Phase 1: Génération des Résumés (Déjà Effectué) - -| Document | Résumés | Coût | Statut | -|----------|---------|------|--------| -| Tiercelin | 43 | $0.63 | ✅ | -| Platon | 12 | $0.14 | ✅ | -| La logique de la science | 1 | $0.02 | ✅ | -| Haugeland | 50 | $0.44 | ✅ | -| **TOTAL** | **106** | **$1.23** | ✅ | - -### Phase 2: Nettoyage de la Base - -1. ✅ Suppression de 7 doublons vides (Tiercelin) -2. ✅ Suppression de 8,313 résumés vides Peirce - - Avant: 10% de visibilité - - Après: 63% → 90% (avec Haugeland) - -### Phase 3: Tests de Validation - -**Scripts créés**: -- `test_summaries_validation.py` - Validation complète -- `test_real_queries.py` - 15 requêtes réelles -- `test_hierarchical_search.py` - Test Summary → Chunks -- `test_haugeland_ai.py` - Test domaine IA spécifique - -**Résultats**: -- Summary search: **90% de visibilité** ✅ -- Chunk search: **10% de visibilité** ❌ - ---- - -## Solution Développée - -### Option A: Summary-First Interface (Sélectionnée) - -**Principe**: Utiliser la collection Summary (équilibrée, haute qualité) comme point d'entrée principal. - -**Avantages**: -- ✅ 90% de visibilité démontrée -- ✅ Coût: $0 (réutilise résumés existants) -- ✅ Respecte la contrainte (pas de suppression) -- ✅ Performance immédiate - -**Alternatives Considérées**: -- Option B: Système hybride (nécessite développement UI) -- Option C: Régénération résumés Peirce (~$45-50, 15-20h) - -### Architecture Summary Collection - -``` -Summary Collection (114 résumés) -├─ Tiercelin: 51 résumés (LLM-generated) -├─ Haugeland: 50 résumés (LLM-generated) -├─ Platon: 12 résumés (LLM-generated) -└─ Logique: 1 résumé (LLM-generated) - -Vectorisation: BAAI/bge-m3 -- Dimensions: 1024 -- Context window: 8192 tokens -- Multilingual: EN, FR, Latin, Greek -``` - ---- - -## Intégration Flask - -### Fichiers Créés/Modifiés - -#### 1. Backend (`flask_app.py`) -**Ajouts**: -- `search_summaries_backend()` - Fonction de recherche -- `@app.route("/search/summary")` - Route Flask -- Logique d'icônes par document (🟣🟢🟡🔵⚪) - -**Lignes**: 2907-3046 (~140 lignes) - -#### 2. Template (`templates/search_summary.html`) -**Caractéristiques**: -- Interface cohérente avec design existant -- Bannière d'info sur performance (90% vs 10%) -- Cartes de résumés avec animations -- Badges de concepts -- Suggestions pré-remplies -- Bouton bascule vers recherche classique - -**Taille**: ~320 lignes HTML/CSS/Jinja2 - -#### 3. Navigation (`templates/base.html`) -**Modification**: -- Ajout lien "📚 Recherche Résumés" dans sidebar -- Badge "90%" de performance -- Active state highlighting - -**Lignes modifiées**: 709-713 - -### Tests d'Intégration - -**Script**: `test_flask_integration.py` - -**Résultats**: ✅ 100% de réussite (12/12 checks) - -``` -Test 1: What is the Turing test? -✅ Found Haugeland icon 🟣 -✅ Results displayed -✅ Similarity scores displayed -✅ Concepts displayed - -Test 2: Can virtue be taught? -✅ Found Platon icon 🟢 -✅ Results displayed -✅ Similarity scores displayed -✅ Concepts displayed - -Test 3: What is pragmatism? -✅ Found Tiercelin icon 🟡 -✅ Results displayed -✅ Similarity scores displayed -✅ Concepts displayed - -Test 4: Navigation link -✅ Link present -✅ Label found -``` - ---- - -## Livrables - -### Documentation (7 fichiers) - -1. **ANALYSE_RAG_FINAL.md** (15 KB) - - Analyse complète du système - - État de la base de données - - Performance par type de recherche - - Solutions proposées - -2. **search_summary_interface.py** (8 KB) - - Script standalone pour ligne de commande - - Mode interactif + single query - - Fonction `search_summaries()` - -3. **README_SEARCH.md** (7 KB) - - Guide d'utilisation complet - - Exemples d'utilisation - - Architecture technique - - Prochaines étapes - -4. **SESSION_SUMMARY.md** (5 KB) - - Résumé exécutif de la session - - Métriques de performance - - Recommandation finale - -5. **INTEGRATION_SUMMARY.md** (10 KB) - - Détails de l'intégration Flask - - Tests de validation - - Architecture technique - - Support et débogage - -6. **QUICKSTART_SUMMARY_SEARCH.md** (6 KB) - - Guide de démarrage rapide - - Exemples de recherche - - Troubleshooting - - Conseils d'utilisation - -7. **COMPLETE_SESSION_RECAP.md** (ce fichier) - - Vue d'ensemble complète - - Chronologie des travaux - - Tous les résultats - -### Code (3 fichiers) - -1. **flask_app.py** (modifié) - - +140 lignes de code - - Fonction backend + route - -2. **templates/search_summary.html** (nouveau) - - ~320 lignes HTML/CSS/Jinja2 - - Interface complète - -3. **templates/base.html** (modifié) - - Navigation mise à jour - - Badge performance - -### Tests (2 fichiers) - -1. **test_flask_integration.py** (nouveau) - - 4 tests automatisés - - Validation complète - -2. **search_summary_interface.py** (réutilisable) - - CLI pour tests manuels - - Peut être importé - ---- - -## Résultats - -### Métriques de Performance - -| Métrique | Avant (Chunk) | Après (Summary) | Amélioration | -|----------|---------------|-----------------|--------------| -| Visibilité documents riches | 10% | 90% | **+800%** | -| Haugeland sur requêtes IA | 10% | 100% | **+900%** | -| Platon sur requêtes Vertu | 20% | 100% | **+400%** | -| Tiercelin sur Pragmatisme | 0% | 100% | **∞** | -| Temps de réponse | ~300ms | ~300ms | = | - -### Tests de Précision - -**15 requêtes réelles testées** (5 domaines): - -1. **Pragmatisme/Peirce**: 3/3 ✅ -2. **Platon/Vertu**: 3/3 ✅ -3. **IA/Esprit**: 3/3 ✅ -4. **Sémiotique**: 3/3 ✅ -5. **Épistémologie**: 3/3 ✅ - -**Résultat Global**: 100% de précision sur tous les tests - -### Coûts - -| Poste | Montant | Détail | -|-------|---------|--------| -| Génération résumés (déjà fait) | $1.23 | 106 résumés LLM | -| Développement interface | $0 | Temps de développement | -| Infrastructure | $0 | Weaviate existant | -| **Total projet** | **$1.23** | Coût total | - -### Accessibilité - -**URL**: `http://localhost:5000/search/summary` - -**Navigation**: Menu ☰ → "📚 Recherche Résumés" - -**Paramètres**: -- Nombre de résultats: 5, 10, 15, 20 -- Seuil de similarité: 60%, 65%, 70%, 75% - ---- - -## Impact Utilisateur - -### Avant (Recherche Chunk) - -**Expérience**: -``` -Query: "What is the Turing test?" - -Résultats: -1. ⚪ Peirce CP 4.162 - 73.5% - "This idea of discrete quantity..." -2. ⚪ Peirce CP 5.520 - 73.5% - "Doctor X. Yours seemed marked..." -3. ⚪ Peirce CP 2.143 - 73.5% - "All these tests, however..." -4. ⚪ Peirce CP 5.187 - 73.3% - "We thus come to the test..." -5. ⚪ Peirce CP 7.206 - 73.2% - "Having, then, by means of..." - -❌ 0/5 résultats pertinents -``` - -### Après (Recherche Summary) - -**Expérience**: -``` -Query: "What is the Turing test?" - -Résultats: -1. 🟣 Haugeland - 69.5% - Computers and intelligence - "This section examines Turing's 1950 prediction..." - Concepts: Turing test, AI, computation... - 📄 1 passage détaillé - -2. 🟣 Haugeland - 68.8% - Computer Science as Empirical Inquiry - "Newell and Simon present computer science..." - Concepts: empirical inquiry, symbolic system... - 📄 1 passage détaillé - -3. 🟣 Haugeland - 66.6% - The Turing test - "This section explores two foundational..." - Concepts: Turing test, intentionality... - 📄 1 passage détaillé - -✅ 3/3 résultats pertinents (100%) -``` - ---- - -## Recommandations - -### Court Terme ✅ - -1. **Promouvoir la Recherche Summary** comme interface principale - - Mettre en avant dans la navigation (déjà fait) - - Badge "90%" de performance (déjà fait) - -2. **Former les utilisateurs** - - Guide QUICKSTART disponible - - Suggestions de recherche intégrées - -3. **Monitorer l'usage** - - Logs Flask pour analytics - - Feedback utilisateurs - -### Moyen Terme (Optionnel) - -1. **Améliorer l'interface** - - Bouton "Voir chunks détaillés" sur chaque résumé - - Route `/summary//chunks` pour expansion - -2. **Ajouter des fonctionnalités** - - Filtres par auteur/document - - Historique de recherche - - Export résultats - -3. **Mode hybride** - - Toggle Summary/Chunk - - Comparaison côte-à-côte - -### Long Terme (Si Besoin) - -1. **Régénération Peirce** (~$45-50) - - Seulement si nécessaire - - Améliorerait aussi la recherche Chunk - -2. **Analytics avancés** - - Graphe de concepts - - Suggestions intelligentes - - Recherches liées - ---- - -## Conclusion - -### Objectifs Atteints ✅ - -1. ✅ Problème de visibilité résolu (10% → 90%) -2. ✅ Contrainte respectée (pas de suppression Peirce) -3. ✅ Solution production-ready implémentée -4. ✅ Documentation complète fournie -5. ✅ Tests validés (100% de précision) -6. ✅ Intégration Flask fonctionnelle - -### État Final - -**Base de Données**: -- Summary: 114 résumés (106 riches) -- Chunk: 5,230 chunks (intacts) -- Performance Summary: 90% ✅ -- Performance Chunk: 10% ❌ (mais toujours disponible) - -**Application Flask**: -- Route `/search/summary` opérationnelle -- Navigation intégrée avec badge "90%" -- Interface moderne et responsive -- Tests automatisés passants - -**Documentation**: -- 7 fichiers de documentation -- Guides utilisateur complets -- Documentation technique détaillée - -### Recommandation Finale - -**Utiliser `/search/summary` comme interface de recherche principale.** - -La recherche Summary offre: -- 📊 **90% de visibilité** vs 10% en recherche directe -- 🎯 **100% de précision** sur tests -- ⚡ **Performance identique** (~300ms) -- 📚 **Métadonnées riches** (concepts, auteur, résumés) -- 🚀 **Meilleure UX** pour découverte de documents - -La recherche Chunk reste disponible via `/search` pour les cas d'usage spécifiques nécessitant des citations exactes. - ---- - -## Fichiers de Référence Rapide - -| Besoin | Fichier | -|--------|---------| -| Démarrage rapide | `QUICKSTART_SUMMARY_SEARCH.md` | -| Intégration technique | `INTEGRATION_SUMMARY.md` | -| Analyse complète | `ANALYSE_RAG_FINAL.md` | -| Guide utilisateur | `README_SEARCH.md` | -| Vue d'ensemble | Ce fichier | - ---- - -**Auteur**: Claude Sonnet 4.5 -**Date**: 2026-01-03 -**Durée**: Session complète -**Statut**: ✅ Projet Complet et Fonctionnel - -**ROI**: +800% de visibilité pour $1.23 d'investissement initial - ---- - -*Fin du rapport de session* diff --git a/generations/library_rag/EXPLICATION_SUMMARY_CHUNK.md b/generations/library_rag/EXPLICATION_SUMMARY_CHUNK.md deleted file mode 100644 index 95e1196..0000000 --- a/generations/library_rag/EXPLICATION_SUMMARY_CHUNK.md +++ /dev/null @@ -1,739 +0,0 @@ -# Lien entre Summary et Chunk - Explication Complète - -**Date**: 2026-01-03 -**Fichiers analysés**: `utils/weaviate_ingest.py`, `schema.py`, `pdf_pipeline.py` - ---- - -## 📋 Table des Matières - -1. [Vue d'Ensemble](#1-vue-densemble) -2. [Lien Théorique entre Summary et Chunk](#2-lien-théorique-entre-summary-et-chunk) -3. [Comment les Summary sont Créés](#3-comment-les-summary-sont-créés) -4. [Pourquoi les Summary sont Vides](#4-pourquoi-les-summary-sont-vides) -5. [Comment Corriger le Problème](#5-comment-corriger-le-problème) - ---- - -## 1. Vue d'Ensemble - -### Architecture Hiérarchique - -``` -Document (ex: Peirce Collected Papers) - │ - ├─► TOC (Table des Matières) - │ └─ Structure hiérarchique des sections - │ - ├─► Summary (8,425 objets) - MACRO - │ └─ Un résumé pour chaque section de la TOC - │ └─ Vectorisé pour recherche sémantique chapitres - │ - └─► Chunk (5,404 objets) - MICRO - └─ Fragments de texte (200-800 chars) - └─ Vectorisé pour recherche sémantique fine -``` - -### Lien entre Summary et Chunk - -Le lien devrait être **par sectionPath** : - -```python -Summary: - sectionPath: "Peirce: CP 5.314 > La sémiose et les catégories" - chunksCount: 23 # ← Nombre de chunks dans cette section - text: "Ce passage explore la théorie de la sémiose..." - -Chunk 1: - sectionPath: "Peirce: CP 5.314 > La sémiose et les catégories" - text: "Un signe, ou representamen, est quelque chose..." - -Chunk 2: - sectionPath: "Peirce: CP 5.314 > La sémiose et les catégories" - text: "La sémiose est l'action du signe..." - -... (21 autres chunks) - -Chunk 23: - sectionPath: "Peirce: CP 5.314 > La sémiose et les catégories" - text: "Ainsi la relation triadique est irréductible..." -``` - -**Principe**: Tous les Chunks avec le même `sectionPath` appartiennent au Summary correspondant. - ---- - -## 2. Lien Théorique entre Summary et Chunk - -### 2.1 Modèle de Données - -#### Summary (Résumé de Section) - -**Fichier**: `utils/weaviate_ingest.py:86-100` - -```python -class SummaryObject(TypedDict): - """Structure d'un Summary dans Weaviate.""" - - sectionPath: str # "Peirce: CP 5.314 > La sémiose" - title: str # "La sémiose et les catégories" - level: int # 2 (profondeur hiérarchique) - text: str # "Ce passage explore..." (RÉSUMÉ LLM) - concepts: List[str] # ["sémiose", "triade", "signe"] - chunksCount: int # 23 (nombre de chunks dans cette section) - document: { - sourceId: str # "peirce_collected_papers_fixed" - } -``` - -**Champs vectorisés**: -- ✅ `text` → Vectorisé avec BGE-M3 (1024-dim) -- ✅ `concepts` → Vectorisé avec BGE-M3 - -**Champs de filtrage**: -- `sectionPath` → Pour lier avec Chunks -- `level` → Pour hiérarchie (1=chapitre, 2=section, 3=subsection) -- `chunksCount` → Pour navigation - -#### Chunk (Fragment de Texte) - -**Fichier**: `schema.py:216-280` - -```python -{ - "text": str, # Contenu du fragment (200-800 chars) - "keywords": List[str], # ["sémiose", "triade"] - - "sectionPath": str, # "Peirce: CP 5.314 > La sémiose" (LIEN AVEC SUMMARY) - "sectionLevel": int, # 2 - "chapterTitle": str, # "La sémiose et les catégories" - "orderIndex": int, # 42 (position dans le document) - "unitType": str, # "argument", "définition", etc. - - "work": { - "title": str, # "Collected Papers" - "author": str, # "Peirce" - }, - "document": { - "sourceId": str, # "peirce_collected_papers_fixed" - "edition": str, # "Hartshorne & Weiss" - } -} -``` - -### 2.2 Comment le Lien Fonctionne - -**Lien par sectionPath** (chaîne de caractères): - -```python -# Recherche dans Summary -summary_result = summaries.query.near_text(query="sémiose", limit=3) -top_section = summary_result.objects[0].properties['sectionPath'] -# → "Peirce: CP 5.314 > La sémiose et les catégories" - -# Récupérer tous les Chunks de cette section -chunks = client.collections.get("Chunk") -chunk_result = chunks.query.fetch_objects( - filters=Filter.by_property("sectionPath").like(f"{top_section}*"), - limit=100 -) -# → Retourne les 23 chunks appartenant à cette section -``` - -**Avantages de ce design** (vs cross-references): -- ✅ Pas besoin de UUID references -- ✅ Requête unique (pas de jointures) -- ✅ Filtrage simple avec LIKE ou EQUAL -- ✅ Lisible et debuggable - -**Inconvénients**: -- ⚠️ Sensible aux typos dans sectionPath -- ⚠️ Pas de validation d'intégrité référentielle - ---- - -## 3. Comment les Summary sont Créés - -### 3.1 Fonction d'Ingestion - -**Fichier**: `utils/weaviate_ingest.py:632-731` - -```python -def ingest_summaries( - client: WeaviateClient, - doc_name: str, - toc: List[Dict[str, Any]], # Table des matières - summaries_content: Dict[str, str], # ← RÉSUMÉS LLM (actuellement vide !) -) -> int: - """Insert section summaries into the Summary collection.""" - - summaries_to_insert: List[SummaryObject] = [] - - def process_toc(items: List[Dict[str, Any]], parent_path: str = "") -> None: - """Parcourt récursivement la TOC pour créer des Summary.""" - for item in items: - title: str = item.get("title", "") - level: int = item.get("level", 1) - path: str = f"{parent_path} > {title}" if parent_path else title - - summary_obj: SummaryObject = { - "sectionPath": path, - "title": title, - "level": level, - - # ⚠️ PROBLÈME ICI : Si summaries_content est vide, - # on utilise juste le titre comme texte ! - "text": summaries_content.get(title, title), - - "concepts": item.get("concepts", []), - - # ⚠️ PROBLÈME : Toujours 0, jamais calculé ! - "chunksCount": 0, - - "document": { - "sourceId": doc_name, - }, - } - summaries_to_insert.append(summary_obj) - - # Traiter les sous-sections récursivement - if "children" in item: - process_toc(item["children"], path) - - process_toc(toc) - - # Insertion batch dans Weaviate - summary_collection.data.insert_many(summaries_to_insert) - return len(summaries_to_insert) -``` - -### 3.2 Appel dans le Pipeline - -**Fichier**: `utils/weaviate_ingest.py:844-845` - -```python -# Dans la fonction ingest_document() -if ingest_summary_collection and toc: - ingest_summaries(client, doc_name, toc, {}) # ← {} = VIDE ! -``` - -**PROBLÈME** : Le dictionnaire `summaries_content` passé est **VIDE** (`{}`). - -**Résultat** : Ligne 686 → `summaries_content.get(title, title)` retourne juste `title` ! - -**Exemple**: -```python -title = "Peirce: CP 5.314" -summaries_content = {} # VIDE - -text = summaries_content.get(title, title) -# → text = "Peirce: CP 5.314" (car title pas dans dict vide) - -# Attendu: -# text = "Ce passage explore la théorie de la sémiose comme processus triadique..." -``` - -### 3.3 Source de la TOC - -La TOC vient de l'extraction LLM : - -**Fichier**: `utils/llm_toc.py` (étape 5 du pipeline) - -```python -def extract_toc_from_markdown(markdown_text: str, ...) -> List[TOCEntry]: - """Extrait la TOC via LLM (Ollama ou Mistral). - - Résultat: - [ - { - "title": "Peirce: CP 5.314", - "level": 1, - "page": null, - "children": [ - { - "title": "La sémiose et les catégories", - "level": 2, - "page": null - } - ] - }, - ... - ] - """ -``` - -**Note**: La TOC contient **seulement les titres**, pas les résumés. - ---- - -## 4. Pourquoi les Summary sont Vides - -### 4.1 Problème #1 : Pas de Génération de Résumés LLM - -**Constat**: Le pipeline PDF ne génère **jamais** de résumés pour les sections. - -**Étapes du pipeline actuel** (`utils/pdf_pipeline.py`): -``` -[1] OCR → Texte brut -[2] Markdown → Markdown structuré -[3] Images → Extraction images -[4] Metadata → Titre, auteur, année -[5] TOC → Table des matières (TITRES SEULEMENT) -[6] Classify → Classification sections -[7] Chunking → Découpage en chunks -[8] Cleaning → Nettoyage chunks -[9] Validation → Validation + concepts -[10] Ingestion → Insertion Weaviate -``` - -**Manque** : Étape de génération de résumés par section ! - -**Ce qui devrait exister** : -``` -[5.5] Summarization → Générer résumé LLM pour chaque section TOC - Input: Section text (tous les chunks d'une section) - Output: {"Peirce: CP 5.314": "Ce passage explore..."} -``` - -### 4.2 Problème #2 : chunksCount Toujours à 0 - -**Constat**: Le champ `chunksCount` est hardcodé à 0. - -**Fichier**: `utils/weaviate_ingest.py:688` - -```python -"chunksCount": 0, # ← Hardcodé, jamais calculé ! -``` - -**Ce qui devrait être fait** : - -```python -def calculate_chunks_count(chunks: List[Dict], section_path: str) -> int: - """Compte combien de chunks appartiennent à cette section.""" - count = 0 - for chunk in chunks: - if chunk.get("sectionPath", "").startswith(section_path): - count += 1 - return count - -# Dans process_toc(): -chunks_count = calculate_chunks_count(all_chunks, path) - -summary_obj: SummaryObject = { - ... - "chunksCount": chunks_count, # ← Calculé dynamiquement - ... -} -``` - -**Pourquoi ce n'est pas fait** : -- La fonction `ingest_summaries()` n'a pas accès à la liste des chunks -- Les chunks sont insérés APRÈS les summaries dans le pipeline -- Ordre incorrect : devrait être Chunks → Summaries (pour compter) - -### 4.3 Problème #3 : Concepts Vides - -**Constat**: Le champ `concepts` est toujours vide. - -**Fichier**: `utils/weaviate_ingest.py:687` - -```python -"concepts": item.get("concepts", []), # ← TOC n'a jamais de concepts -``` - -**Explication**: La TOC extraite par LLM ne contient que `{title, level, page}`, pas de concepts. - -**Ce qui devrait être fait** : - -Les concepts devraient être extraits lors de la génération du résumé : - -```python -# Étape 5.5 - Summarization (à créer) -def generate_section_summary(section_text: str) -> Dict[str, Any]: - """Génère résumé + concepts via LLM.""" - - prompt = f"""Résume cette section et extrais les concepts clés. - - Section: - {section_text} - - Réponds en JSON: - {{ - "summary": "Résumé en 100-200 mots...", - "concepts": ["concept1", "concept2", ...] - }} - """ - - response = llm.generate(prompt) - return json.loads(response) - -# Résultat: -{ - "summary": "Ce passage explore la théorie de la sémiose...", - "concepts": ["sémiose", "triade", "signe", "interprétant", "représentamen"] -} -``` - ---- - -## 5. Comment Corriger le Problème - -### 5.1 Solution Complète : Ajouter Étape de Summarization - -**Créer nouveau module** : `utils/llm_summarizer.py` - -```python -"""LLM-based section summarization for Library RAG. - -Generates summaries and extracts concepts for each section in the TOC. -""" - -from typing import Dict, List, Any -from utils.llm_structurer import get_llm_client -import json - -def generate_summaries_for_toc( - toc: List[Dict[str, Any]], - chunks: List[Dict[str, Any]], - llm_provider: str = "ollama" -) -> Dict[str, Dict[str, Any]]: - """Generate LLM summaries for each section in the TOC. - - Args: - toc: Table of contents with hierarchical structure. - chunks: All document chunks with sectionPath. - llm_provider: "ollama" or "mistral". - - Returns: - Dict mapping section title to {summary, concepts}. - - Example: - >>> summaries = generate_summaries_for_toc(toc, chunks) - >>> summaries["Peirce: CP 5.314"] - { - "summary": "Ce passage explore la sémiose...", - "concepts": ["sémiose", "triade", "signe"] - } - """ - - llm = get_llm_client(llm_provider) - summaries_content: Dict[str, Dict[str, Any]] = {} - - def process_section(item: Dict[str, Any], parent_path: str = "") -> None: - title = item.get("title", "") - path = f"{parent_path} > {title}" if parent_path else title - - # Collecter tous les chunks de cette section - section_chunks = [ - chunk for chunk in chunks - if chunk.get("sectionPath", "").startswith(path) - ] - - if not section_chunks: - # Pas de chunks, utiliser juste le titre - summaries_content[title] = { - "summary": title, - "concepts": [] - } - else: - # Générer résumé via LLM - section_text = "\n\n".join([c.get("text", "") for c in section_chunks[:10]]) # Max 10 chunks - - prompt = f"""Résume cette section philosophique en 100-200 mots et extrais les 5-10 concepts clés. - -Section: {title} - -Texte: -{section_text} - -Réponds en JSON: -{{ - "summary": "Résumé de la section...", - "concepts": ["concept1", "concept2", ...] -}} -""" - - try: - response = llm.generate(prompt, max_tokens=500) - result = json.loads(response) - summaries_content[title] = result - except Exception as e: - print(f"Erreur génération résumé pour {title}: {e}") - summaries_content[title] = { - "summary": title, - "concepts": [] - } - - # Traiter sous-sections récursivement - if "children" in item: - for child in item["children"]: - process_section(child, path) - - for item in toc: - process_section(item) - - return summaries_content -``` - -**Modifier le pipeline** : `utils/weaviate_ingest.py` - -```python -def ingest_document( - doc_name: str, - chunks: List[Dict[str, Any]], - metadata: Dict[str, Any], - ..., - ingest_summary_collection: bool = False, -) -> IngestResult: - - # ... (code existant pour chunks) - - # NOUVEAU : Générer résumés APRÈS avoir les chunks - if ingest_summary_collection and toc: - from utils.llm_summarizer import generate_summaries_for_toc - - # Générer résumés LLM pour chaque section - summaries_content = generate_summaries_for_toc(toc, chunks, llm_provider="ollama") - - # Transformer en format pour ingest_summaries - summaries_text = { - title: content["summary"] - for title, content in summaries_content.items() - } - - # Ajouter concepts dans la TOC - def enrich_toc_with_concepts(items: List[Dict]) -> None: - for item in items: - title = item.get("title", "") - if title in summaries_content: - item["concepts"] = summaries_content[title]["concepts"] - if "children" in item: - enrich_toc_with_concepts(item["children"]) - - enrich_toc_with_concepts(toc) - - # Insérer avec vrais résumés - ingest_summaries(client, doc_name, toc, summaries_text) -``` - -### 5.2 Solution Rapide : Calculer chunksCount Dynamiquement - -**Modifier** : `utils/weaviate_ingest.py:ingest_summaries()` - -```python -def ingest_summaries( - client: WeaviateClient, - doc_name: str, - toc: List[Dict[str, Any]], - summaries_content: Dict[str, str], - chunks: List[Dict[str, Any]] = [], # ← NOUVEAU paramètre -) -> int: - - summaries_to_insert: List[SummaryObject] = [] - - def count_chunks_for_section(section_path: str) -> int: - """Compte chunks appartenant à cette section.""" - count = 0 - for chunk in chunks: - if chunk.get("sectionPath", "").startswith(section_path): - count += 1 - return count - - def process_toc(items: List[Dict[str, Any]], parent_path: str = "") -> None: - for item in items: - title: str = item.get("title", "") - level: int = item.get("level", 1) - path: str = f"{parent_path} > {title}" if parent_path else title - - summary_obj: SummaryObject = { - "sectionPath": path, - "title": title, - "level": level, - "text": summaries_content.get(title, title), - "concepts": item.get("concepts", []), - - # ✅ CORRECTIF : Calculer dynamiquement - "chunksCount": count_chunks_for_section(path), - - "document": { - "sourceId": doc_name, - }, - } - summaries_to_insert.append(summary_obj) - - if "children" in item: - process_toc(item["children"], path) - - process_toc(toc) - - # ... (reste du code) -``` - -**Modifier appel** : `utils/weaviate_ingest.py:844-845` - -```python -if ingest_summary_collection and toc: - # ✅ Passer les chunks pour calcul de chunksCount - ingest_summaries(client, doc_name, toc, {}, chunks) -``` - -### 5.3 Solution Minimale : Ré-injecter avec Vraies Données - -Si vous avez déjà les résumés dans les JSON : - -```python -# Script de correction rapide -import json -import weaviate -from pathlib import Path - -# Charger le JSON avec les résumés -chunks_file = Path("output/peirce_collected_papers_fixed/peirce_collected_papers_fixed_chunks.json") -with open(chunks_file, 'r', encoding='utf-8') as f: - data = json.load(f) - -# Vérifier s'il y a des résumés -if 'summaries' in data: - print(f"Trouvé {len(data['summaries'])} résumés dans le JSON") - - # Connecter à Weaviate - client = weaviate.connect_to_local() - - # Supprimer anciens Summary - summaries = client.collections.get("Summary") - summaries.data.delete_many( - where=Filter.by_property("document").by_property("sourceId").equal("peirce_collected_papers_fixed") - ) - - # Réinsérer avec vrais résumés - from utils.weaviate_ingest import ingest_summaries - - toc = data['metadata']['toc'] - chunks = data['chunks'] - - # Extraire résumés du JSON - summaries_content = { - s['title']: s['text'] - for s in data['summaries'] - } - - # Réinjecter - count = ingest_summaries(client, "peirce_collected_papers_fixed", toc, summaries_content, chunks) - print(f"Réinséré {count} résumés") - - client.close() -else: - print("❌ Pas de résumés dans le JSON - il faut les générer avec LLM") -``` - ---- - -## 6. Résumé Visual - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ PIPELINE ACTUEL (CASSÉ) │ -└─────────────────────────────────────────────────────────────────┘ - -PDF → OCR → Markdown → TOC Extraction (LLM) - │ - └─► toc = [ - {"title": "Peirce: CP 5.314", "level": 1}, - {"title": "La sémiose", "level": 2} - ] - - ↓ - -Chunking (LLM) → chunks = [ - {"text": "Un signe...", "sectionPath": "Peirce: CP 5.314 > La sémiose"}, - {"text": "La sémiose...", "sectionPath": "Peirce: CP 5.314 > La sémiose"}, - ... - ] - - ↓ - -Ingestion → ingest_summaries(client, doc_name, toc, {}) ← VIDE ! - │ - └─► Summary créés avec: - - text: "Peirce: CP 5.314" (juste le titre) - - concepts: [] - - chunksCount: 0 - - -┌─────────────────────────────────────────────────────────────────┐ -│ PIPELINE CORRIGÉ (ATTENDU) │ -└─────────────────────────────────────────────────────────────────┘ - -PDF → OCR → Markdown → TOC Extraction → Chunking - │ - ↓ - Summarization (LLM) ← NOUVEAU ! - │ - └─► summaries_content = { - "Peirce: CP 5.314": { - "summary": "Ce passage explore...", - "concepts": ["sémiose", "triade"] - } - } - - ↓ - -Ingestion → ingest_summaries(client, doc_name, toc, summaries_content, chunks) - │ - └─► Summary créés avec: - - text: "Ce passage explore la théorie de la sémiose..." ✅ - - concepts: ["sémiose", "triade", "signe"] ✅ - - chunksCount: 23 ✅ -``` - ---- - -## 7. Conclusion - -### État Actuel - -**Summary → Chunk** : ❌ LIEN CASSÉ - -| Aspect | Actuel | Attendu | Status | -|--------|--------|---------|--------| -| **text** | "Peirce: CP 5.314" | "Ce passage explore..." | ❌ Vide | -| **concepts** | `[]` | `["sémiose", "triade"]` | ❌ Vide | -| **chunksCount** | 0 | 23 | ❌ Faux | -| **sectionPath** | ✅ Correct | ✅ Correct | ✅ OK | - -### Lien Théorique vs Réel - -**Théorique** (design prévu): -``` -Summary.sectionPath = "Peirce: CP 5.314 > La sémiose" - ↓ LIEN -Chunk.sectionPath = "Peirce: CP 5.314 > La sémiose" -Chunk.sectionPath = "Peirce: CP 5.314 > La sémiose" -... (23 chunks) -``` - -**Réel** (implémentation actuelle): -``` -Summary.sectionPath = "Peirce: CP 5.314" ✅ OK -Summary.chunksCount = 0 ❌ FAUX -Summary.text = "Peirce: CP 5.314" ❌ VIDE - -Chunk.sectionPath = "Peirce: CP 5.314" ✅ OK -Chunk.text = "Un signe, ou representamen..." ✅ OK -``` - -**LIEN** : ⚠️ Existe techniquement (sectionPath identique) mais inutilisable car Summary vides. - -### Actions Requises - -**Priorité 1** : Générer résumés LLM (créer `llm_summarizer.py`) -**Priorité 2** : Calculer `chunksCount` dynamiquement -**Priorité 3** : Extraire concepts pour Summary - -**ROI** : Activer recherche hiérarchique Summary → Chunk (+30% précision) - ---- - -**Dernière mise à jour**: 2026-01-03 -**Auteur**: Analyse du code source -**Version**: 1.0 diff --git a/generations/library_rag/FIX_HIERARCHICAL.md b/generations/library_rag/FIX_HIERARCHICAL.md deleted file mode 100644 index c936159..0000000 --- a/generations/library_rag/FIX_HIERARCHICAL.md +++ /dev/null @@ -1,280 +0,0 @@ -# Fix - Recherche Hiérarchique - -**Date**: 2026-01-03 -**Problème**: Mode hiérarchique n'affichait aucun résultat -**Statut**: ✅ Résolu et testé - ---- - -## Problème Identifié - -Le mode hiérarchique retournait **0 résultats** pour toutes les requêtes. - -**Symptôme**: -``` -Mode: 🌳 Hiérarchique -Résultat: "Aucun résultat trouvé" -``` - -## Cause Racine - -**Fichier**: `flask_app.py` -**Fonction**: `hierarchical_search()` -**Lignes**: 338-344 - -### Code Problématique - -```python -summaries_result = summary_collection.query.near_text( - query=query, - limit=sections_limit, - return_metadata=wvq.MetadataQuery(distance=True), - return_properties=[ - "sectionPath", "title", "text", "level", "concepts" - ], # ❌ N'inclut PAS "document" (nested object) -) -``` - -**Problème**: Le paramètre `return_properties` **excluait** le nested object `"document"`. - -### Conséquence - -```python -# Ligne 363-366 -doc_obj = props.get("document") # ← Retourne None ou {} -source_id = "" -if doc_obj and isinstance(doc_obj, dict): - source_id = doc_obj.get("sourceId", "") # ← source_id reste vide - -# Ligne 374 -"document_source_id": source_id, # ← Vide! - -# Ligne 385-387 -for section in sections_data: - source_id = section["document_source_id"] - if not source_id: - continue # ← Toutes les sections sont SKIPPÉES! - -# Ligne 410-421 -if not sections_data: - return { - "mode": "hierarchical", - "sections": [], - "results": [], - "total_chunks": 0, # ← 0 résultats! - } -``` - -**Résultat**: Toutes les sections étaient filtrées → 0 résultats - ---- - -## Solution Appliquée - -**Suppression de `return_properties`** pour laisser Weaviate retourner **tous** les properties automatiquement, y compris les nested objects. - -### Code Corrigé - -```python -summaries_result = summary_collection.query.near_text( - query=query, - limit=sections_limit, - return_metadata=wvq.MetadataQuery(distance=True), - # Note: Don't specify return_properties - let Weaviate return all properties - # including nested objects like "document" which we need for source_id -) -``` - -**Changement**: Ligne 342-344 - Suppression du paramètre `return_properties` - -### Pourquoi ça fonctionne? - -En **Weaviate v4**, quand on ne spécifie pas `return_properties`: -- ✅ Weaviate retourne **automatiquement** tous les properties -- ✅ Les **nested objects** comme `document` sont inclus -- ✅ Le `source_id` est correctement récupéré -- ✅ Les sections ne sont plus filtrées -- ✅ Les résultats s'affichent - ---- - -## Tests de Validation - -### ✅ Test Automatisé - -**Script**: `test_hierarchical_fix.py` - -```python -query = "What is the Turing test?" -mode = "hierarchical" -``` - -**Résultat**: -``` -✅ Mode hiérarchique détecté -✅ 13 cartes de passage trouvées -✅ 4 groupes de sections -✅ Headers de section présents -✅ Textes de résumé présents -✅ Concepts affichés - -RÉSULTAT: Mode hiérarchique fonctionne! -``` - -### ✅ Test Manuel - -**URL**: `http://localhost:5000/search?q=What+is+the+Turing+test&mode=hierarchical` - -**Résultat attendu**: -- Badge "🌳 Recherche hiérarchique (N sections)" -- Groupes de sections avec résumés -- Chunks regroupés par section -- Concepts affichés -- Metadata complète - ---- - -## Comparaison Avant/Après - -### Avant (Bugué) - -``` -Query: "What is the Turing test?" -Mode: Hiérarchique - -Étape 1 (Summary): 3 sections trouvées ✓ -Étape 2 (Filter): 0 sections après filtrage ✗ - (source_id vide → toutes skippées) - -Résultat: "Aucun résultat trouvé" ❌ -``` - -### Après (Corrigé) - -``` -Query: "What is the Turing test?" -Mode: Hiérarchique - -Étape 1 (Summary): 3 sections trouvées ✓ -Étape 2 (Filter): 3 sections valides ✓ - (source_id récupéré → sections conservées) -Étape 3 (Chunks): 13 chunks trouvés ✓ - -Résultat: 4 sections avec 13 passages ✅ -``` - ---- - -## Impact - -### Code -- **1 ligne modifiée** (flask_app.py:342-344) -- **0 régression** (autres modes inchangés) -- **0 effet secondaire** - -### Fonctionnalité -- ✅ Mode hiérarchique opérationnel -- ✅ Summary → Chunks fonctionnel -- ✅ Sections regroupées correctement -- ✅ Metadata complète affichée - -### Performance -- **Temps de réponse**: Identique (~500ms) -- **Qualité résultats**: Excellente -- **Visibilité**: Variable (dépend de la requête) - ---- - -## Modes Disponibles (État Final) - -| Mode | Collection | Étapes | Statut | Performance | -|------|------------|--------|--------|-------------| -| **Auto** | Détection | 1-2 | ✅ OK | Variable | -| **Simple** | Chunk | 1 | ✅ OK | 10% visibilité | -| **Hiérarchique** | Summary → Chunk | 2 | ✅ **CORRIGÉ** | Variable | -| **Summary** | Summary | 1 | ✅ OK | 90% visibilité | - ---- - -## Leçon Apprise - -### ❌ Erreur Commune - -**NE PAS** spécifier `return_properties` quand on a besoin de nested objects: - -```python -# MAUVAIS -results = collection.query.near_text( - query=query, - return_properties=["field1", "field2"] # ❌ Exclut nested objects -) -``` - -### ✅ Bonne Pratique - -**LAISSER** Weaviate retourner automatiquement tous les properties: - -```python -# BON -results = collection.query.near_text( - query=query, - # Pas de return_properties → tous les properties retournés ✓ -) -``` - -**Alternative** (si vraiment nécessaire): - -```python -# ACCEPTABLE -results = collection.query.near_text( - query=query, - return_properties=["field1", "field2", "nested_object"] # ✓ Inclure nested -) -``` - -Mais la **meilleure approche** reste de **ne pas spécifier** `return_properties` quand on utilise des nested objects, pour éviter ce genre de bug. - ---- - -## Vérification Finale - -### Checklist de Test - -- [x] Mode auto-détection fonctionne -- [x] Mode simple fonctionne -- [x] Mode hiérarchique fonctionne ✅ **CORRIGÉ** -- [x] Mode summary fonctionne -- [x] Filtres auteur/work fonctionnent -- [x] Affichage correct pour tous modes -- [x] Pas de régression - -### Commande de Test - -```bash -# Démarrer Flask -python flask_app.py - -# Tester mode hiérarchique -curl "http://localhost:5000/search?q=What+is+the+Turing+test&mode=hierarchical" - -# Ou avec script -python test_hierarchical_fix.py -``` - ---- - -## Conclusion - -✅ **Le mode hiérarchique est maintenant complètement fonctionnel.** - -Le bug était subtil mais critique : l'exclusion du nested object `document` par `return_properties` rendait impossible la récupération du `source_id`, ce qui causait le filtrage de toutes les sections. - -La solution simple (supprimer `return_properties`) résout le problème sans effets secondaires. - -**Tous les modes de recherche fonctionnent désormais correctement!** - ---- - -**Fichier modifié**: `flask_app.py` (ligne 342-344) -**Tests**: `test_hierarchical_fix.py` -**Statut**: ✅ Résolu et validé diff --git a/generations/library_rag/INTEGRATION_SUMMARY.md b/generations/library_rag/INTEGRATION_SUMMARY.md deleted file mode 100644 index bae8b36..0000000 --- a/generations/library_rag/INTEGRATION_SUMMARY.md +++ /dev/null @@ -1,297 +0,0 @@ -# Intégration Recherche Summary - Résumé - -**Date**: 2026-01-03 -**Statut**: ✅ Intégration complète et testée - ---- - -## Fichiers Modifiés/Créés - -### 1. Backend (flask_app.py) -**Modifications**: -- ✅ Ajout de la fonction `search_summaries_backend()` (lignes 2907-2999) -- ✅ Ajout de la route `@app.route("/search/summary")` (lignes 3002-3046) - -**Fonctionnalités**: -- Recherche sémantique dans la collection Summary -- Filtrage par seuil de similarité configurable -- Icônes de documents automatiques (🟣🟢🟡🔵⚪) -- Métadonnées riches (auteur, année, concepts, résumé) - -### 2. Template (templates/search_summary.html) -**Statut**: ✅ Créé (nouveau fichier) - -**Caractéristiques**: -- Interface cohérente avec le design existant -- Bannière d'information sur la performance (90% vs 10%) -- Cartes de résumés avec dégradés et animations -- Badges de concepts clés -- Suggestions de recherche pré-remplies -- Bouton de bascule vers recherche classique - -### 3. Navigation (templates/base.html) -**Modifications**: -- ✅ Ajout du lien "Recherche Résumés" dans la sidebar (lignes 709-713) -- ✅ Badge "90%" pour indiquer la performance -- ✅ Icône 📚 distincte - ---- - -## Tests de Validation - -### ✅ Tests Fonctionnels (4/4 PASS) - -#### Test 1: Requête IA (Haugeland) -``` -Query: "What is the Turing test?" -✅ PASS - Found Haugeland icon 🟣 -✅ PASS - Results displayed -✅ PASS - Similarity scores displayed -✅ PASS - Concepts displayed -``` - -#### Test 2: Requête Vertu (Platon) -``` -Query: "Can virtue be taught?" -✅ PASS - Found Platon icon 🟢 -✅ PASS - Results displayed -✅ PASS - Similarity scores displayed -✅ PASS - Concepts displayed -``` - -#### Test 3: Requête Pragmatisme (Tiercelin) -``` -Query: "What is pragmatism according to Peirce?" -✅ PASS - Found Tiercelin icon 🟡 -✅ PASS - Results displayed -✅ PASS - Similarity scores displayed -✅ PASS - Concepts displayed -``` - -#### Test 4: Navigation -``` -✅ PASS - Navigation link present -✅ PASS - Summary search label found -``` - -**Résultat Global**: 100% de réussite (12/12 checks passés) - ---- - -## Accès à la Fonctionnalité - -### URL Directe -``` -http://localhost:5000/search/summary -``` - -### Via Navigation -1. Cliquer sur le menu hamburger (☰) en haut à gauche -2. Cliquer sur "📚 Recherche Résumés" (badge 90%) -3. Entrer une question et rechercher - -### Paramètres URL -``` -/search/summary?q=votre+question&limit=10&min_similarity=0.65 -``` - -**Paramètres disponibles**: -- `q` (string): Question de recherche -- `limit` (int): Nombre de résultats (5, 10, 15, 20) -- `min_similarity` (float): Seuil 0-1 (0.60, 0.65, 0.70, 0.75) - ---- - -## Performance Démontrée - -### Recherche Summary (Nouvelle Interface) -- ✅ 90% de visibilité des documents riches -- ✅ 100% de précision sur tests (3/3) -- ✅ Temps de réponse: ~200-500ms -- ✅ Métadonnées riches affichées - -### Recherche Chunk (Ancienne Interface) -- ❌ 10% de visibilité des documents riches -- ⚠️ Dominée par chunks Peirce (97%) -- ✅ Toujours disponible via `/search` - ---- - -## Comparaison Visuelle - -### Nouvelle Interface (Summary) -``` -┌─────────────────────────────────────────┐ -│ 📚 Recherche par Résumés │ -│ │ -│ ┌─────────────────────────────────────┐ │ -│ │ 🟣 Haugeland - 69.5% similaire │ │ -│ │ Computers and intelligence │ │ -│ │ │ │ -│ │ This section examines Turing's... │ │ -│ │ │ │ -│ │ Concepts: Turing test, AI, ... │ │ -│ │ 📄 1 passage détaillé │ │ -│ └─────────────────────────────────────┘ │ -└─────────────────────────────────────────┘ -``` - -### Ancienne Interface (Chunk) -``` -┌─────────────────────────────────────────┐ -│ 🔍 Recherche sémantique │ -│ │ -│ ┌─────────────────────────────────────┐ │ -│ │ ⚪ Peirce - 73.5% similaire │ │ -│ │ "This idea of discrete quantity..." │ │ -│ │ │ │ -│ │ Section: CP 4.162 │ │ -│ └─────────────────────────────────────┘ │ -│ [4 autres résultats Peirce...] │ -└─────────────────────────────────────────┘ -``` - ---- - -## Architecture Technique - -### Backend Flow -``` -User Query - ↓ -@app.route("/search/summary") - ↓ -search_summaries_backend() - ↓ -Weaviate Summary.query.near_text() - ↓ -Format results (icons, metadata) - ↓ -render_template("search_summary.html") - ↓ -HTML Response to Browser -``` - -### Collection Summary -- **Total**: 114 résumés -- **Riches**: 106 résumés (>100 chars) -- **Vecteurs**: BAAI/bge-m3 (1024-dim) -- **Documents**: Tiercelin (51), Haugeland (50), Platon (12), Logique (1) - ---- - -## Utilisation Recommandée - -### Cas d'Usage Summary (Recommandé) -- ✅ Questions générales sur un sujet -- ✅ Découverte exploratoire -- ✅ Vue d'ensemble d'un document/section -- ✅ Identification de sections pertinentes - -**Exemples**: -- "What is the Turing test?" -- "Can virtue be taught?" -- "What is pragmatism?" - -### Cas d'Usage Chunk (Ancienne) -- Citations précises nécessaires -- Recherche très spécifique dans le texte -- Analyse détaillée d'un passage - -**Exemples**: -- "Exact quote about X" -- Requêtes avec mots-clés très précis - ---- - -## Prochaines Étapes (Optionnel) - -### Court Terme -- [ ] Ajouter bouton "Voir chunks détaillés" sur chaque résumé -- [ ] Route `/summary//chunks` pour expansion -- [ ] Export résultats (JSON/CSV) - -### Moyen Terme -- [ ] Mode hybride avec toggle Summary/Chunk -- [ ] Filtres par auteur/document -- [ ] Historique de recherche -- [ ] Sauvegarde de recherches favorites - -### Long Terme -- [ ] Suggestions de recherche basées sur l'historique -- [ ] Graphe de relations entre concepts -- [ ] Visualisation des sections les plus consultées - ---- - -## Maintenance - -### Dépendances -- Flask 3.0+ -- Weaviate Python client v4 -- Jinja2 (inclus avec Flask) - -### Monitoring -- Logs Flask: Recherches effectuées dans stdout -- Weaviate: Métriques via `http://localhost:8080/v1/meta` - -### Mise à Jour -Si nouveaux résumés générés: -1. Les résumés sont automatiquement vectorisés dans Summary -2. Aucune modification de code nécessaire -3. Nouveaux résumés apparaissent immédiatement dans recherche - ---- - -## Support et Débogage - -### Vérifier que Weaviate tourne -```bash -docker ps | grep weaviate -# Devrait montrer: Up X hours -``` - -### Vérifier les résumés en base -```bash -python -c " -import weaviate -client = weaviate.connect_to_local() -summaries = client.collections.get('Summary') -print(f'Total summaries: {len(list(summaries.iterator()))}') -client.close() -" -``` - -### Logs Flask -```bash -# Le serveur affiche les requêtes dans stdout -127.0.0.1 - - [DATE] "GET /search/summary?q=... HTTP/1.1" 200 - -``` - -### Test Manuel -```bash -# Test rapide -curl "http://localhost:5000/search/summary?q=test&limit=5" -# Devrait retourner HTML avec résultats -``` - ---- - -## Conclusion - -✅ **Intégration complète et fonctionnelle** -- Backend: Fonction + Route -- Frontend: Template + Navigation -- Tests: 100% de réussite -- Performance: 90% de visibilité démontrée - -La nouvelle interface de recherche Summary est maintenant disponible dans l'application Flask et offre une expérience utilisateur nettement supérieure pour la découverte de documents philosophiques. - -**Recommandation**: Promouvoir la recherche Summary comme interface principale et garder la recherche Chunk pour les cas d'usage spécifiques. - ---- - -**Auteur**: Claude Sonnet 4.5 -**Date**: 2026-01-03 -**Version**: 1.0 -**Status**: ✅ Production Ready diff --git a/generations/library_rag/PLAN_LLM_SUMMARIZER.md b/generations/library_rag/PLAN_LLM_SUMMARIZER.md deleted file mode 100644 index 1cc21fa..0000000 --- a/generations/library_rag/PLAN_LLM_SUMMARIZER.md +++ /dev/null @@ -1,1112 +0,0 @@ -# Plan d'Implémentation - llm_summarizer.py - -**Date**: 2026-01-03 -**Objectif**: Créer un module de génération de résumés LLM pour les sections du document -**Priorité**: 🔴 HAUTE (corrige 60% de la base vectorielle inutilisée) - ---- - -## 📋 Table des Matières - -1. [Objectifs et Spécifications](#1-objectifs-et-spécifications) -2. [Architecture du Module](#2-architecture-du-module) -3. [Implémentation Détaillée](#3-implémentation-détaillée) -4. [Intégration au Pipeline](#4-intégration-au-pipeline) -5. [Tests et Validation](#5-tests-et-validation) -6. [Plan de Déploiement](#6-plan-de-déploiement) - ---- - -## 1. Objectifs et Spécifications - -### 1.1 Objectif Principal - -Générer des résumés LLM de qualité pour chaque section de la table des matières afin de : -- ✅ Remplir le champ `Summary.text` avec un vrai résumé (100-500 caractères) -- ✅ Extraire les concepts clés pour `Summary.concepts` (5-15 mots-clés) -- ✅ Activer la recherche hiérarchique Summary → Chunk - -### 1.2 Contraintes - -**Performance** : -- Traiter 8,425 sections (base actuelle) en temps raisonnable -- Budget : ~1 section/seconde avec Ollama, ~3 sections/seconde avec Mistral API -- Temps total estimé : 2-3h avec Ollama, 45min avec Mistral API - -**Coût** : -- Ollama (local) : GRATUIT mais lent -- Mistral API : ~$0.001-0.002 par section (~$8-16 pour 8,425 sections) - -**Qualité** : -- Résumés cohérents et informatifs (pas de hallucinations) -- Concepts pertinents extraits du texte réel -- Support multilingue (français, anglais, grec, latin) - -### 1.3 Cas d'Usage - -**Cas 1** : Premier traitement d'un document PDF -```python -# Dans pdf_pipeline.py, après chunking -summaries_content = generate_summaries_for_toc( - toc=toc, - chunks=chunks, - llm_provider="ollama" -) -# → Génère résumés pour toutes les sections -``` - -**Cas 2** : Re-traitement d'un document existant -```python -# Script standalone pour régénérer résumés -python regenerate_summaries.py --doc peirce_collected_papers_fixed --provider mistral -``` - -**Cas 3** : Génération incrémentale (nouvelles sections uniquement) -```python -# Générer résumés seulement pour sections manquantes -summaries_content = generate_missing_summaries( - doc_name="peirce_collected_papers_fixed", - toc=new_toc, - chunks=chunks -) -``` - ---- - -## 2. Architecture du Module - -### 2.1 Structure du Fichier - -``` -utils/llm_summarizer.py -│ -├─ Imports et Configuration -│ └─ llm_structurer, types, logging -│ -├─ Type Definitions -│ ├─ SummaryResult (TypedDict) -│ └─ SummarizationConfig (TypedDict) -│ -├─ Fonctions Utilitaires -│ ├─ collect_chunks_for_section() -│ ├─ truncate_text_for_llm() -│ └─ parse_llm_summary_response() -│ -├─ Génération de Résumés -│ ├─ generate_summary_for_section() ← CORE -│ ├─ generate_summaries_for_toc() ← PUBLIC API -│ └─ generate_missing_summaries() -│ -└─ Batch Processing - ├─ batch_generate_summaries() - └─ resume_failed_summaries() -``` - -### 2.2 Dépendances - -```python -# Dépendances internes -from utils.llm_structurer import get_llm_client, LLMProvider -from utils.types import TOCEntry, ChunkData - -# Dépendances externes -import json -import logging -from typing import List, Dict, Any, Optional, TypedDict -from pathlib import Path -``` - -### 2.3 Types Définis - -```python -class SummaryResult(TypedDict): - """Résultat de la génération d'un résumé.""" - title: str - summary: str # Résumé LLM (100-500 chars) - concepts: List[str] # 5-15 concepts clés - chunks_count: int # Nombre de chunks dans cette section - success: bool - error: Optional[str] - -class SummarizationConfig(TypedDict, total=False): - """Configuration pour la génération de résumés.""" - llm_provider: LLMProvider # "ollama" | "mistral" - model: Optional[str] # Modèle spécifique (optionnel) - max_chunks_per_section: int # Limite de chunks à résumer (default: 20) - summary_length: str # "short" | "medium" | "long" - language: str # "fr" | "en" | "auto" - batch_size: int # Taille des batches (default: 10) - cache_results: bool # Cacher résultats (default: True) - output_file: Optional[Path] # Fichier de sauvegarde intermédiaire -``` - ---- - -## 3. Implémentation Détaillée - -### 3.1 Fonction Core : generate_summary_for_section() - -**Signature** : -```python -def generate_summary_for_section( - section_title: str, - section_path: str, - section_chunks: List[Dict[str, Any]], - config: SummarizationConfig, -) -> SummaryResult: - """Génère un résumé LLM pour une section donnée. - - Args: - section_title: Titre de la section (ex: "La sémiose et les catégories") - section_path: Chemin hiérarchique (ex: "Peirce: CP 5.314 > La sémiose") - section_chunks: Liste des chunks appartenant à cette section - config: Configuration de génération - - Returns: - SummaryResult avec summary, concepts, et chunks_count - - Example: - >>> result = generate_summary_for_section( - ... "La sémiose", - ... "Peirce: CP 5.314 > La sémiose", - ... chunks, - ... {"llm_provider": "ollama", "language": "fr"} - ... ) - >>> print(result['summary']) - "Ce passage explore la théorie de la sémiose..." - """ -``` - -**Pseudo-code** : -```python -def generate_summary_for_section(...): - # 1. Collecter et limiter les chunks - chunks_to_summarize = section_chunks[:config.max_chunks_per_section] - - if not chunks_to_summarize: - return { - "title": section_title, - "summary": section_title, # Fallback sur titre - "concepts": [], - "chunks_count": 0, - "success": False, - "error": "No chunks found" - } - - # 2. Construire le texte à résumer - section_text = "\n\n".join([ - chunk.get("text", "") for chunk in chunks_to_summarize - ]) - - # 3. Tronquer si trop long (limite token LLM) - section_text = truncate_text_for_llm(section_text, max_tokens=3000) - - # 4. Construire le prompt - prompt = build_summary_prompt(section_title, section_text, config) - - # 5. Appeler le LLM - try: - llm = get_llm_client(config["llm_provider"]) - response = llm.generate(prompt, max_tokens=600) - - # 6. Parser la réponse JSON - result = parse_llm_summary_response(response) - - return { - "title": section_title, - "summary": result["summary"], - "concepts": result["concepts"], - "chunks_count": len(section_chunks), - "success": True, - "error": None - } - - except Exception as e: - logger.error(f"LLM summarization failed for {section_title}: {e}") - return { - "title": section_title, - "summary": section_title, # Fallback - "concepts": [], - "chunks_count": len(section_chunks), - "success": False, - "error": str(e) - } -``` - -### 3.2 Fonction Utilitaire : build_summary_prompt() - -**Prompt Engineering** : - -```python -def build_summary_prompt( - section_title: str, - section_text: str, - config: SummarizationConfig -) -> str: - """Construit le prompt LLM pour la génération de résumé.""" - - language = config.get("language", "fr") - summary_length = config.get("summary_length", "medium") - - # Mapper summary_length vers nombre de mots - word_counts = { - "short": "50-100 mots", - "medium": "100-200 mots", - "long": "200-400 mots" - } - word_count = word_counts[summary_length] - - # Prompts selon langue - if language == "fr": - prompt = f"""Tu es un expert en philosophie et sémiotique. Résume la section suivante d'un texte philosophique. - -Titre de la section: {section_title} - -Texte de la section: -{section_text} - -Tâches: -1. Résume le contenu principal en {word_count} en français -2. Extrais les 5-10 concepts clés les plus importants -3. Réponds UNIQUEMENT en JSON valide avec cette structure: - -{{ - "summary": "Résumé de la section en français...", - "concepts": ["concept1", "concept2", "concept3", ...] -}} - -Consignes: -- Le résumé doit capturer les arguments principaux et thèses développées -- Les concepts doivent être des termes techniques ou notions philosophiques clés -- Reste fidèle au texte, n'invente rien -- Si le texte est en grec/latin, résume quand même en français -""" - - elif language == "en": - prompt = f"""You are an expert in philosophy and semiotics. Summarize the following section from a philosophical text. - -Section title: {section_title} - -Section text: -{section_text} - -Tasks: -1. Summarize the main content in {word_count} in English -2. Extract the 5-10 most important key concepts -3. Respond ONLY with valid JSON using this structure: - -{{ - "summary": "Summary of the section in English...", - "concepts": ["concept1", "concept2", "concept3", ...] -}} - -Guidelines: -- The summary should capture main arguments and theses -- Concepts should be technical terms or key philosophical notions -- Stay faithful to the text, don't invent anything -- If text is in Greek/Latin, still summarize in English -""" - - else: # auto - # Détecter langue du texte et adapter - prompt = f"""[Auto-detect language and summarize accordingly...]""" - - return prompt -``` - -### 3.3 Fonction Principale : generate_summaries_for_toc() - -**Signature** : -```python -def generate_summaries_for_toc( - toc: List[Dict[str, Any]], - chunks: List[Dict[str, Any]], - llm_provider: LLMProvider = "ollama", - config: Optional[SummarizationConfig] = None, -) -> Dict[str, Dict[str, Any]]: - """Génère des résumés LLM pour toutes les sections de la TOC. - - Parcourt récursivement la TOC et génère un résumé pour chaque section. - Supporte le batch processing et la sauvegarde intermédiaire. - - Args: - toc: Table des matières hiérarchique - chunks: Tous les chunks du document avec sectionPath - llm_provider: "ollama" (local, gratuit) ou "mistral" (API, payant) - config: Configuration optionnelle (utilise defaults si None) - - Returns: - Dict mapping section title → {summary, concepts} - - Example: - >>> summaries = generate_summaries_for_toc(toc, chunks, "ollama") - >>> summaries["Peirce: CP 5.314"] - { - "summary": "Ce passage explore la théorie de la sémiose...", - "concepts": ["sémiose", "triade", "signe", "interprétant"], - "chunks_count": 23, - "success": True - } - """ -``` - -**Implémentation** : -```python -def generate_summaries_for_toc(toc, chunks, llm_provider="ollama", config=None): - # Configuration par défaut - default_config: SummarizationConfig = { - "llm_provider": llm_provider, - "max_chunks_per_section": 20, - "summary_length": "medium", - "language": "fr", - "batch_size": 10, - "cache_results": True, - "output_file": None, - } - - # Merger avec config utilisateur - final_config = {**default_config, **(config or {})} - - # Résultats accumulés - summaries_content: Dict[str, Dict[str, Any]] = {} - - # Collecter toutes les sections à traiter (aplatir la TOC) - all_sections = flatten_toc(toc) - - logger.info(f"Generating summaries for {len(all_sections)} sections using {llm_provider}...") - - # Traiter par batches pour sauvegarde intermédiaire - batch_size = final_config["batch_size"] - - for batch_idx in range(0, len(all_sections), batch_size): - batch = all_sections[batch_idx:batch_idx + batch_size] - - logger.info(f"Processing batch {batch_idx//batch_size + 1}/{(len(all_sections) + batch_size - 1)//batch_size}") - - for section_item in batch: - title = section_item["title"] - path = section_item["path"] - level = section_item["level"] - - # Collecter chunks de cette section - section_chunks = collect_chunks_for_section(chunks, path) - - # Générer résumé - result = generate_summary_for_section( - section_title=title, - section_path=path, - section_chunks=section_chunks, - config=final_config - ) - - summaries_content[title] = result - - # Log progression - if result["success"]: - logger.info(f" ✓ {title} ({result['chunks_count']} chunks, {len(result['summary'])} chars)") - else: - logger.warning(f" ✗ {title} - Error: {result['error']}") - - # Sauvegarde intermédiaire - if final_config["cache_results"] and final_config["output_file"]: - save_intermediate_results(summaries_content, final_config["output_file"]) - - # Statistiques finales - success_count = sum(1 for s in summaries_content.values() if s["success"]) - logger.info(f"Summary generation complete: {success_count}/{len(summaries_content)} successful") - - return summaries_content - - -def flatten_toc(toc: List[Dict], parent_path: str = "") -> List[Dict]: - """Aplatit une TOC hiérarchique en liste de sections avec chemins.""" - sections = [] - - for item in toc: - title = item.get("title", "") - level = item.get("level", 1) - path = f"{parent_path} > {title}" if parent_path else title - - sections.append({ - "title": title, - "path": path, - "level": level - }) - - # Récursif pour children - if "children" in item: - sections.extend(flatten_toc(item["children"], path)) - - return sections - - -def collect_chunks_for_section(chunks: List[Dict], section_path: str) -> List[Dict]: - """Collecte tous les chunks appartenant à une section.""" - return [ - chunk for chunk in chunks - if chunk.get("sectionPath", "").startswith(section_path) - ] -``` - -### 3.4 Fonctions Utilitaires Supplémentaires - -```python -def truncate_text_for_llm(text: str, max_tokens: int = 3000) -> str: - """Tronque le texte pour ne pas dépasser la limite de tokens LLM. - - Estimation: 1 token ≈ 4 caractères - """ - max_chars = max_tokens * 4 - - if len(text) <= max_chars: - return text - - # Tronquer au dernier point avant la limite - truncated = text[:max_chars] - last_period = truncated.rfind('.') - - if last_period > max_chars * 0.8: # Si point trouvé après 80% du texte - return truncated[:last_period + 1] + "..." - else: - return truncated + "..." - - -def parse_llm_summary_response(response: str) -> Dict[str, Any]: - """Parse la réponse JSON du LLM. - - Supporte différents formats de réponse (avec/sans markdown code blocks). - """ - # Nettoyer markdown code blocks - cleaned = response.strip() - if cleaned.startswith("```json"): - cleaned = cleaned[7:] - if cleaned.startswith("```"): - cleaned = cleaned[3:] - if cleaned.endswith("```"): - cleaned = cleaned[:-3] - cleaned = cleaned.strip() - - try: - result = json.loads(cleaned) - - # Validation - if "summary" not in result or "concepts" not in result: - raise ValueError("Missing required fields in LLM response") - - # Nettoyer concepts (enlever doublons, vides) - concepts = [c.strip() for c in result["concepts"] if c.strip()] - concepts = list(dict.fromkeys(concepts)) # Enlever doublons en préservant l'ordre - - return { - "summary": result["summary"].strip(), - "concepts": concepts[:15] # Limiter à 15 concepts max - } - - except json.JSONDecodeError as e: - logger.error(f"Failed to parse LLM JSON response: {e}\nResponse: {response[:200]}") - raise - - -def save_intermediate_results( - summaries_content: Dict[str, Dict[str, Any]], - output_file: Path -) -> None: - """Sauvegarde les résultats intermédiaires en JSON.""" - output_file.parent.mkdir(parents=True, exist_ok=True) - - with open(output_file, 'w', encoding='utf-8') as f: - json.dump(summaries_content, f, ensure_ascii=False, indent=2) - - logger.debug(f"Intermediate results saved to {output_file}") -``` - ---- - -## 4. Intégration au Pipeline - -### 4.1 Modification de weaviate_ingest.py - -**Fichier**: `utils/weaviate_ingest.py` - -**Ligne 844-845** - AVANT : -```python -if ingest_summary_collection and toc: - ingest_summaries(client, doc_name, toc, {}) # ← VIDE ! -``` - -**Ligne 844-850** - APRÈS : -```python -if ingest_summary_collection and toc: - from utils.llm_summarizer import generate_summaries_for_toc - - logger.info("Generating LLM summaries for TOC sections...") - - # Générer résumés LLM - summaries_results = generate_summaries_for_toc( - toc=toc, - chunks=chunks, - llm_provider=llm_provider, # Utilise le provider du pipeline - config={ - "summary_length": "medium", - "language": language, - "cache_results": True, - "output_file": Path(f"output/{doc_name}/{doc_name}_summaries.json") - } - ) - - # Extraire juste le texte pour ingest_summaries - summaries_text = { - title: result["summary"] - for title, result in summaries_results.items() - } - - # Enrichir TOC avec concepts - enrich_toc_with_concepts(toc, summaries_results) - - # Insérer dans Weaviate avec vrais résumés - ingest_summaries(client, doc_name, toc, summaries_text, chunks) -``` - -**Fonction helper à ajouter** : -```python -def enrich_toc_with_concepts( - toc: List[Dict[str, Any]], - summaries_results: Dict[str, Dict[str, Any]] -) -> None: - """Enrichit la TOC avec les concepts extraits par LLM.""" - - def process_item(item: Dict[str, Any]) -> None: - title = item.get("title", "") - - if title in summaries_results: - item["concepts"] = summaries_results[title].get("concepts", []) - - if "children" in item: - for child in item["children"]: - process_item(child) - - for item in toc: - process_item(item) -``` - -### 4.2 Modification de ingest_summaries() - -**Ligne 632** - Ajouter paramètre `chunks` : -```python -def ingest_summaries( - client: WeaviateClient, - doc_name: str, - toc: List[Dict[str, Any]], - summaries_content: Dict[str, str], - chunks: List[Dict[str, Any]] = [], # ← NOUVEAU -) -> int: -``` - -**Ligne 688** - Calculer chunksCount dynamiquement : -```python -def count_chunks_for_section(section_path: str) -> int: - """Compte chunks dans cette section.""" - count = 0 - for chunk in chunks: - if chunk.get("sectionPath", "").startswith(section_path): - count += 1 - return count - -# Dans process_toc(): -summary_obj: SummaryObject = { - ... - "chunksCount": count_chunks_for_section(path) if chunks else 0, # ← CORRECTIF - ... -} -``` - -### 4.3 Paramètre dans pdf_pipeline.py - -**Ajouter option** `generate_summaries` : - -```python -def process_pdf( - pdf_path: Path, - *, - use_llm: bool = True, - llm_provider: LLMProvider = "ollama", - ingest_to_weaviate: bool = True, - generate_summaries: bool = True, # ← NOUVEAU - summary_config: Optional[Dict] = None, # ← NOUVEAU - ... -) -> PipelineResult: - """ - Args: - ... - generate_summaries: Generate LLM summaries for sections (default: True) - summary_config: Custom summarization config (optional) - """ - - # ... (code existant) - - # Étape 10: Ingestion Weaviate - if ingest_to_weaviate: - result = ingest_document( - doc_name=doc_name, - chunks=chunks, - metadata=metadata, - language=metadata.get("language", "fr"), - toc=toc, - hierarchy=hierarchy, - pages=pages, - ingest_document_collection=True, - ingest_summary_collection=generate_summaries, # ← Utilise le paramètre - llm_provider=llm_provider, - summary_config=summary_config, - ) -``` - ---- - -## 5. Tests et Validation - -### 5.1 Tests Unitaires - -**Fichier**: `tests/utils/test_llm_summarizer.py` - -```python -"""Tests pour le module llm_summarizer.""" - -import pytest -from utils.llm_summarizer import ( - generate_summary_for_section, - generate_summaries_for_toc, - truncate_text_for_llm, - parse_llm_summary_response, - collect_chunks_for_section, -) - - -def test_collect_chunks_for_section(): - """Test collection de chunks par section.""" - chunks = [ - {"sectionPath": "Chapitre 1 > Section A", "text": "Text 1"}, - {"sectionPath": "Chapitre 1 > Section A", "text": "Text 2"}, - {"sectionPath": "Chapitre 1 > Section B", "text": "Text 3"}, - ] - - result = collect_chunks_for_section(chunks, "Chapitre 1 > Section A") - - assert len(result) == 2 - assert result[0]["text"] == "Text 1" - - -def test_truncate_text_for_llm(): - """Test troncature de texte.""" - text = "A" * 15000 # 15k chars - - truncated = truncate_text_for_llm(text, max_tokens=1000) - - assert len(truncated) <= 4000 # 1000 tokens * 4 chars - assert truncated.endswith("...") - - -def test_parse_llm_summary_response_valid_json(): - """Test parsing réponse JSON valide.""" - response = ''' - { - "summary": "Ce passage explore la sémiose", - "concepts": ["sémiose", "triade", "signe"] - } - ''' - - result = parse_llm_summary_response(response) - - assert result["summary"] == "Ce passage explore la sémiose" - assert len(result["concepts"]) == 3 - - -def test_parse_llm_summary_response_with_markdown(): - """Test parsing réponse avec code blocks markdown.""" - response = '''```json - { - "summary": "Test summary", - "concepts": ["concept1"] - } - ```''' - - result = parse_llm_summary_response(response) - - assert result["summary"] == "Test summary" - - -def test_generate_summary_for_section_no_chunks(): - """Test génération résumé sans chunks.""" - result = generate_summary_for_section( - section_title="Test Section", - section_path="Test > Section", - section_chunks=[], - config={"llm_provider": "ollama"} - ) - - assert result["success"] is False - assert result["chunks_count"] == 0 - assert result["error"] == "No chunks found" - - -@pytest.mark.integration -def test_generate_summaries_for_toc_integration(): - """Test intégration complète (nécessite Ollama running).""" - toc = [ - { - "title": "Introduction", - "level": 1, - "children": [ - {"title": "Contexte", "level": 2} - ] - } - ] - - chunks = [ - { - "sectionPath": "Introduction", - "text": "Ceci est une introduction à la philosophie." - }, - { - "sectionPath": "Introduction > Contexte", - "text": "Le contexte historique de cette œuvre..." - } - ] - - summaries = generate_summaries_for_toc(toc, chunks, "ollama") - - assert "Introduction" in summaries - assert summaries["Introduction"]["success"] is True - assert len(summaries["Introduction"]["summary"]) > 50 -``` - -### 5.2 Test Manuel - -**Script** : `test_summarizer_manual.py` - -```python -#!/usr/bin/env python3 -"""Test manuel du llm_summarizer sur un vrai document.""" - -from pathlib import Path -import json -from utils.llm_summarizer import generate_summaries_for_toc - -# Charger document existant -doc_file = Path("output/peirce_collected_papers_fixed/peirce_collected_papers_fixed_chunks.json") - -with open(doc_file, 'r', encoding='utf-8') as f: - data = json.load(f) - -toc = data['metadata']['toc'] -chunks = data['chunks'] - -print(f"TOC sections: {len(toc)}") -print(f"Chunks: {len(chunks)}") - -# Tester sur 3 premières sections -toc_sample = toc[:3] - -print("\nGenerating summaries for first 3 sections...") -summaries = generate_summaries_for_toc( - toc=toc_sample, - chunks=chunks, - llm_provider="ollama", - config={ - "summary_length": "medium", - "language": "fr", - "max_chunks_per_section": 10 - } -) - -# Afficher résultats -for title, result in summaries.items(): - print(f"\n{'='*80}") - print(f"Section: {title}") - print(f"Success: {result['success']}") - print(f"Chunks count: {result['chunks_count']}") - print(f"\nSummary ({len(result['summary'])} chars):") - print(result['summary']) - print(f"\nConcepts: {', '.join(result['concepts'])}") -``` - ---- - -## 6. Plan de Déploiement - -### 6.1 Phase 1 : Développement (2-3 jours) - -**Jour 1** : Implémentation core -- [ ] Créer `utils/llm_summarizer.py` avec fonctions de base -- [ ] Implémenter `generate_summary_for_section()` -- [ ] Implémenter `generate_summaries_for_toc()` -- [ ] Tests unitaires - -**Jour 2** : Intégration -- [ ] Modifier `weaviate_ingest.py` pour appeler llm_summarizer -- [ ] Modifier `pdf_pipeline.py` pour activer/désactiver summarization -- [ ] Ajouter gestion erreurs et retry logic -- [ ] Tests d'intégration - -**Jour 3** : Optimisation -- [ ] Batch processing -- [ ] Sauvegarde intermédiaire (cache) -- [ ] Gestion timeouts LLM -- [ ] Documentation finale - -### 6.2 Phase 2 : Test en Production (1 jour) - -**Test sur petit document** (50-100 sections) : -```bash -# Test avec Ollama (gratuit) -python test_summarizer_manual.py - -# Vérifier qualité des résumés -python test_resume.py # Devrait avoir meilleurs scores maintenant -``` - -**Test sur Peirce Collected Papers** (8,425 sections) : -```bash -# Option 1: Régénérer tout (long ~3h) -python regenerate_summaries.py --doc peirce_collected_papers_fixed --provider ollama - -# Option 2: Utiliser Mistral API (rapide ~45min, coût ~$10) -python regenerate_summaries.py --doc peirce_collected_papers_fixed --provider mistral -``` - -### 6.3 Phase 3 : Migration Base Complète (1 jour) - -**Étapes** : - -1. **Backup Weaviate** : - ```bash - # Exporter Summary collection avant modification - python backup_summaries.py --output backups/summaries_old.json - ``` - -2. **Supprimer anciennes Summary** : - ```python - import weaviate - client = weaviate.connect_to_local() - summaries = client.collections.get("Summary") - summaries.data.delete_many(where={}) # Supprimer toutes - ``` - -3. **Régénérer avec LLM** : - ```bash - # Pour chaque document - for doc in peirce_collected_papers_fixed platon_menon ...; do - python regenerate_summaries.py --doc $doc --provider ollama - done - ``` - -4. **Validation** : - ```bash - python test_resume.py # Vérifier scores améliorés - python validate_summaries.py # Vérifier chunksCount > 0 - ``` - -### 6.4 Script de Régénération - -**Fichier** : `regenerate_summaries.py` - -```python -#!/usr/bin/env python3 -"""Régénère les résumés LLM pour un document existant.""" - -import argparse -import json -from pathlib import Path -import weaviate -from weaviate.classes.query import Filter - -from utils.llm_summarizer import generate_summaries_for_toc -from utils.weaviate_ingest import ingest_summaries - - -def regenerate_summaries(doc_name: str, llm_provider: str = "ollama") -> None: - """Régénère les Summary pour un document.""" - - # 1. Charger document existant - doc_file = Path(f"output/{doc_name}/{doc_name}_chunks.json") - - if not doc_file.exists(): - raise FileNotFoundError(f"Document file not found: {doc_file}") - - with open(doc_file, 'r', encoding='utf-8') as f: - data = json.load(f) - - toc = data['metadata']['toc'] - chunks = data['chunks'] - - print(f"Document: {doc_name}") - print(f"Sections: {len(toc)}") - print(f"Chunks: {len(chunks)}") - - # 2. Générer résumés LLM - print(f"\nGenerating summaries using {llm_provider}...") - - summaries_results = generate_summaries_for_toc( - toc=toc, - chunks=chunks, - llm_provider=llm_provider, - config={ - "summary_length": "medium", - "language": "fr", - "cache_results": True, - "output_file": Path(f"output/{doc_name}/{doc_name}_summaries_new.json") - } - ) - - # 3. Supprimer anciennes Summary - print("\nDeleting old summaries from Weaviate...") - - client = weaviate.connect_to_local() - summaries_collection = client.collections.get("Summary") - - delete_result = summaries_collection.data.delete_many( - where=Filter.by_property("document").by_property("sourceId").equal(doc_name) - ) - print(f"Deleted {delete_result.successful} old summaries") - - # 4. Insérer nouvelles Summary - print("\nInserting new summaries into Weaviate...") - - summaries_text = { - title: result["summary"] - for title, result in summaries_results.items() - } - - # Enrichir TOC avec concepts - def enrich_toc(items): - for item in items: - title = item.get("title", "") - if title in summaries_results: - item["concepts"] = summaries_results[title].get("concepts", []) - if "children" in item: - enrich_toc(item["children"]) - - enrich_toc(toc) - - count = ingest_summaries(client, doc_name, toc, summaries_text, chunks) - - print(f"\n✓ Inserted {count} new summaries") - - # 5. Statistiques - success_count = sum(1 for r in summaries_results.values() if r["success"]) - print(f"\nSuccess rate: {success_count}/{len(summaries_results)} ({success_count/len(summaries_results)*100:.1f}%)") - - client.close() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--doc", required=True, help="Document name (e.g., peirce_collected_papers_fixed)") - parser.add_argument("--provider", choices=["ollama", "mistral"], default="ollama") - - args = parser.parse_args() - - regenerate_summaries(args.doc, args.provider) -``` - ---- - -## 7. Métriques de Succès - -### 7.1 KPIs à Mesurer - -| Métrique | Avant | Cible | Mesure | -|----------|-------|-------|--------| -| **Summary.text longueur moyenne** | 13 chars | 200-400 chars | `avg(len(text))` | -| **Summary.concepts count moyenne** | 0 | 5-10 | `avg(len(concepts))` | -| **Summary.chunksCount moyenne** | 0 | 10-30 | `avg(chunksCount)` | -| **Similarité recherche Summary** | 0.71 | 0.85+ | test_resume.py | -| **Taux de succès génération** | N/A | 95%+ | `success_count / total` | -| **Temps génération (Ollama)** | N/A | <2s/section | Timer | -| **Coût génération (Mistral)** | N/A | <$0.002/section | API cost tracking | - -### 7.2 Tests d'Acceptation - -**Test 1** : Résumés non vides -```python -# Tous les Summary doivent avoir text > 50 chars -assert all(len(s['text']) > 50 for s in summaries.values()) -``` - -**Test 2** : Concepts pertinents -```python -# Concepts doivent être dans le texte source -for title, result in summaries.items(): - section_text = get_section_text(title) - for concept in result['concepts']: - assert concept.lower() in section_text.lower() -``` - -**Test 3** : chunksCount exact -```python -# chunksCount doit matcher le nombre réel de chunks -for title, result in summaries.items(): - real_count = count_chunks_for_section(chunks, title) - assert result['chunks_count'] == real_count -``` - -**Test 4** : Amélioration scores recherche -```python -# Scores doivent être meilleurs qu'avant -old_scores = [0.723, 0.719, 0.718, ...] # Avant -new_scores = run_search("Peirce et la sémiose") # Après - -assert new_scores[0] > 0.85 # Top-1 devrait être >0.85 -assert avg(new_scores) > avg(old_scores) + 0.10 # +10% minimum -``` - ---- - -## 8. Risques et Mitigation - -| Risque | Impact | Probabilité | Mitigation | -|--------|--------|-------------|------------| -| **LLM hallucinations** | Haut | Moyen | Valider concepts contre texte source | -| **Timeout LLM Ollama** | Moyen | Haut | Retry logic + timeout configurables | -| **Coût Mistral API** | Moyen | Faible | Limite budget + estimation avant run | -| **Crash pendant génération** | Moyen | Moyen | Sauvegarde intermédiaire + resume | -| **Qualité résumés variable** | Moyen | Moyen | Prompt engineering + review sample | - ---- - -## 9. Checklist de Déploiement - -### Avant de Commencer -- [ ] Weaviate running (`docker compose up -d`) -- [ ] Ollama running avec modèle compatible (qwen2.5:7b ou deepseek-r1:14b) -- [ ] Budget Mistral API confirmé si utilisation API (~$10-16) -- [ ] Backup de la base Weaviate actuelle - -### Développement -- [ ] `utils/llm_summarizer.py` créé et testé -- [ ] `tests/utils/test_llm_summarizer.py` tous verts -- [ ] `weaviate_ingest.py` modifié et testé -- [ ] `pdf_pipeline.py` modifié avec nouveau paramètre -- [ ] `regenerate_summaries.py` script créé - -### Validation -- [ ] Test sur petit document (50 sections) réussi -- [ ] Scores de similarité améliorés (>0.85) -- [ ] chunksCount calculés correctement -- [ ] Concepts pertinents extraits - -### Production -- [ ] Migration Peirce Collected Papers complète -- [ ] Migration autres documents complète -- [ ] Tests d'acceptation tous verts -- [ ] Documentation mise à jour - ---- - -**Estimation temps total** : 5-6 jours -**Estimation coût** : $10-50 (selon usage Mistral API) -**ROI** : +30% précision recherche, 60% base vectorielle activée - ---- - -**Prochaine étape** : Voulez-vous que je commence l'implémentation de `llm_summarizer.py` ? 🚀 diff --git a/generations/library_rag/QUICKSTART_SUMMARY_SEARCH.md b/generations/library_rag/QUICKSTART_SUMMARY_SEARCH.md deleted file mode 100644 index 941594d..0000000 --- a/generations/library_rag/QUICKSTART_SUMMARY_SEARCH.md +++ /dev/null @@ -1,239 +0,0 @@ -# Quickstart - Recherche Summary - -Guide rapide pour utiliser la nouvelle interface de recherche optimisée. - ---- - -## 🚀 Démarrage Rapide - -### 1. Démarrer Weaviate (si pas déjà lancé) -```bash -docker compose up -d -``` - -### 2. Démarrer l'application Flask -```bash -cd generations/library_rag -python flask_app.py -``` - -### 3. Accéder à l'interface -Ouvrir dans le navigateur: **http://localhost:5000** - -### 4. Utiliser la Recherche Summary -1. Cliquer sur le menu ☰ (hamburger) en haut à gauche -2. Cliquer sur **"📚 Recherche Résumés"** (badge 90%) -3. Entrer une question et cliquer sur **"🔍 Rechercher"** - ---- - -## 💡 Exemples de Recherche - -### IA et Philosophie de l'Esprit (Haugeland 🟣) -``` -What is the Turing test? -Can machines think? -What is a physical symbol system? -How do connectionist networks work? -``` - -**Résultat attendu**: Résumés de Haugeland avec icône 🟣 - -### Vertu et Connaissance (Platon 🟢) -``` -Can virtue be taught? -What is the theory of recollection? -How does Socrates define virtue? -``` - -**Résultat attendu**: Résumés de Platon (Ménon) avec icône 🟢 - -### Pragmatisme et Sémiotique (Tiercelin 🟡) -``` -What is pragmatism according to Peirce? -How does thought work as a sign? -What is the relationship between doubt and inquiry? -``` - -**Résultat attendu**: Résumés de Tiercelin avec icône 🟡 - ---- - -## 🎨 Interface Visuelle - -### Ce que vous verrez: - -``` -┌──────────────────────────────────────────────────────────┐ -│ 📚 Recherche par Résumés │ -│ │ -│ ┌────────────────────────────────────────────────────┐ │ -│ │ ✨ Nouvelle interface de recherche optimisée │ │ -│ │ Performance: [📊 90% de visibilité] vs [📉 10%] │ │ -│ └────────────────────────────────────────────────────┘ │ -│ │ -│ ┌─ Formulaire de recherche ─────────────────────────┐ │ -│ │ Votre question: [What is the Turing test?] │ │ -│ │ Nombre: [10 résumés ▼] Seuil: [65% ▼] │ │ -│ │ [🔍 Rechercher] [Réinitialiser] [🔄 Classique] │ │ -│ └───────────────────────────────────────────────────┘ │ -│ │ -│ 3 résumés trouvés [📚 Recherche par Résumés] │ -│ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ 🟣 [Haugeland] John Haugeland (2023) ⚡ 69.5% │ │ -│ │ Computers and intelligence │ │ -│ │ │ │ -│ │ "This section examines Turing's 1950 prediction... │ │ -│ │ analyzing the theoretical foundations..." │ │ -│ │ │ │ -│ │ Concepts: Turing test | AI | formal function |... │ │ -│ │ 📄 1 passage détaillé Section: 2.2.3... │ │ -│ └─────────────────────────────────────────────────────┘ │ -│ │ -│ [Plus de résultats...] │ -└──────────────────────────────────────────────────────────┘ -``` - ---- - -## 📊 Comparaison des Modes - -| Fonctionnalité | Summary (Nouveau) | Chunk (Ancien) | -|----------------|-------------------|----------------| -| **Visibilité documents riches** | 90% ✅ | 10% ❌ | -| **Vue d'ensemble** | Résumés de sections | Passages courts | -| **Métadonnées** | Riches (concepts, auteur) | Basiques | -| **Exploration** | Excellente | Difficile | -| **Précision citations** | Moyenne | Excellente | -| **Temps de réponse** | Rapide (~300ms) | Rapide (~300ms) | - -### Quand utiliser Summary? ✅ -- Questions générales -- Découverte de sujets -- Vue d'ensemble d'un document -- Identification de sections pertinentes - -### Quand utiliser Chunk? 🔍 -- Besoin de citations exactes -- Recherche très précise -- Analyse détaillée d'un passage - ---- - -## 🎯 Paramètres Recommandés - -### Exploration Large -``` -Résultats: 15-20 résumés -Seuil: 60-65% (plus large) -``` -**Utilisation**: Découverte de sujets, brainstorming - -### Recherche Précise -``` -Résultats: 5-10 résumés -Seuil: 70-75% (très précis) -``` -**Utilisation**: Questions spécifiques, confirmation d'informations - -### Par Défaut (Recommandé) -``` -Résultats: 10 résumés -Seuil: 65% (équilibré) -``` -**Utilisation**: Usage général, meilleur compromis - ---- - -## 🔧 Troubleshooting - -### "Aucun résumé trouvé" -**Solutions**: -1. Réduire le seuil de similarité (essayer 60%) -2. Reformuler la question en anglais/français -3. Utiliser des termes plus généraux -4. Vérifier que la question porte sur les documents disponibles - -### Page ne charge pas -**Solutions**: -1. Vérifier que Flask tourne: `http://localhost:5000` -2. Vérifier que Weaviate tourne: `docker ps | grep weaviate` -3. Consulter les logs Flask dans le terminal - -### Résultats non pertinents -**Solutions**: -1. Augmenter le seuil de similarité (essayer 70-75%) -2. Réduire le nombre de résultats -3. Être plus spécifique dans la question - ---- - -## 📚 Documents Disponibles - -### 🟣 Haugeland - Mind Design III -**Sujets**: IA, philosophie de l'esprit, Turing test, réseaux de neurones, computation -**Résumés**: 50 sections - -### 🟢 Platon - Ménon -**Sujets**: Vertu, connaissance, réminiscence, Socrate, enseignement -**Résumés**: 12 sections - -### 🟡 Tiercelin - La Pensée-Signe -**Sujets**: Pragmatisme, Peirce, sémiotique, pensée, signes -**Résumés**: 51 sections - -### 🔵 Peirce - La Logique de la Science -**Sujets**: Croyance, doute, méthode scientifique, fixation des croyances -**Résumés**: 1 section - -**Total**: 114 résumés (106 riches) indexés et searchables - ---- - -## 🎓 Conseils d'Utilisation - -### 1. Formuler de Bonnes Questions -✅ **Bon**: "What is the Turing test and what does it tell us about intelligence?" -❌ **Mauvais**: "turing" - -✅ **Bon**: "Can virtue be taught according to Plato?" -❌ **Mauvais**: "plato virtue" - -### 2. Explorer les Concepts -Cliquer sur les concepts affichés pour voir les thèmes principaux d'une section. - -### 3. Ajuster le Seuil -- Trop de résultats non pertinents? → Augmenter le seuil -- Pas assez de résultats? → Réduire le seuil - -### 4. Basculer entre Modes -Utiliser le bouton "🔄 Recherche classique" pour comparer les résultats entre Summary et Chunk. - ---- - -## 🚀 Prochaines Fonctionnalités - -Améliorations prévues: -- [ ] Bouton "Voir les passages détaillés" sur chaque résumé -- [ ] Filtres par auteur/document -- [ ] Historique de recherche -- [ ] Export des résultats (JSON/PDF) -- [ ] Suggestions de recherches liées - ---- - -## 📞 Support - -- **Documentation complète**: `INTEGRATION_SUMMARY.md` -- **Analyse technique**: `ANALYSE_RAG_FINAL.md` -- **Guide d'utilisation**: `README_SEARCH.md` -- **Tests**: `test_flask_integration.py` - ---- - -**Version**: 1.0 -**Date**: 2026-01-03 -**Statut**: ✅ Production Ready - -Bon usage de la recherche optimisée! 🚀 diff --git a/generations/library_rag/README_SEARCH.md b/generations/library_rag/README_SEARCH.md deleted file mode 100644 index e3dedfd..0000000 --- a/generations/library_rag/README_SEARCH.md +++ /dev/null @@ -1,188 +0,0 @@ -# Guide d'Utilisation - Interface de Recherche Optimisée - -## Vue d'Ensemble - -L'interface de recherche optimisée utilise la collection **Summary** comme point d'entrée principal, offrant **90% de visibilité** des documents riches vs 10% pour la recherche directe dans Chunks. - -## Performance Démontrée - -### ✅ Tests Réussis - -#### 1. Requêtes sur l'IA (domaine Haugeland) -```bash -python search_summary_interface.py "What is the Turing test?" -``` -**Résultat**: 7/7 résultats Haugeland (100%) - -#### 2. Requêtes sur la vertu (domaine Platon) -```bash -python search_summary_interface.py "Can virtue be taught?" -``` -**Résultat**: 6/6 résultats Platon (100%) - -#### 3. Requêtes sur le pragmatisme (domaine Peirce/Tiercelin) -```bash -python search_summary_interface.py "What is pragmatism according to Peirce?" -``` -**Résultat**: 5/5 résultats Tiercelin (100%) - -### Comparaison avec Recherche Chunk Directe - -| Approche | Visibilité Documents Riches | Performance | -|----------|----------------------------|-------------| -| **Summary-first** (ce script) | **90%** | ✅ Excellent | -| Chunk directe | 10% | ❌ Dominé par Peirce | - -## Utilisation - -### Mode Requête Unique -```bash -# Requête simple -python search_summary_interface.py "Votre question ici" - -# Avec limite de résultats -python search_summary_interface.py "What is intelligence?" -n 5 - -# Avec seuil de similarité personnalisé -python search_summary_interface.py "Can machines think?" -s 0.7 -``` - -### Mode Interactif -```bash -# Lancer sans arguments -python search_summary_interface.py - -# Interface interactive -INTERFACE DE RECHERCHE RAG - Collection Summary -================================================ -Mode: Summary-first (90% de visibilité démontrée) -Tapez 'quit' pour quitter - -Votre question: What is the Chinese Room argument? -[résultats affichés] - -Votre question: Can virtue be taught? -[résultats affichés] - -Votre question: quit -Au revoir! -``` - -## Options - -| Option | Court | Défaut | Description | -|--------|-------|--------|-------------| -| `query` | - | - | Question de recherche (optionnel) | -| `--limit` | `-n` | 10 | Nombre maximum de résultats | -| `--min-similarity` | `-s` | 0.65 | Seuil de similarité (0-1) | - -## Format des Résultats - -Chaque résultat affiche: -- **Icône + Document**: 🟣 Haugeland, 🟢 Platon, 🟡 Tiercelin, 🔵 Logique, ⚪ Peirce -- **Similarité**: Score 0-1 et pourcentage -- **Titre**: Titre de la section -- **Auteur/Année**: Si disponible -- **Concepts**: Top 5 concepts clés -- **Résumé**: Résumé de la section (max 300 chars) -- **Chunks**: Nombre de chunks disponibles pour lecture détaillée - -### Exemple de Sortie -``` -[1] 🟣 Haugeland - Similarité: 0.695 (69.5%) - Titre: 2.2.3 Computers and intelligence - Auteur: John Haugeland, Carl F. Craver, and Colin Klein (2023.0) - Concepts: Turing test, artificial intelligence, formal input/output function, universal machine, computability (+5 autres) - Résumé: This section examines Turing's 1950 prediction that computers would achieve human-level intelligence by 2000, analyzing the theoretical foundations underlying this forecast... - 📄 1 chunk(s) disponible(s) pour lecture détaillée -``` - -## Fonctionnalités Avancées - -### Récupération des Chunks Détaillés - -Le script inclut la fonction `get_chunks_for_section()` pour récupérer le contenu détaillé: - -```python -from search_summary_interface import get_chunks_for_section - -# Après avoir identifié une section intéressante -chunks = get_chunks_for_section( - document_id="Haugeland_J._Mind_Design_III...", - section_path="2.2.3 Computers and intelligence", - limit=5 -) - -for chunk in chunks: - print(chunk["text"]) -``` - -## Architecture - -### Collection Summary -- 114 résumés total -- 106 résumés riches (>100 chars) -- Documents: Tiercelin (51), Haugeland (50), Platon (12), Logique (1) - -### Vecteurs -- Modèle: BAAI/bge-m3 (1024 dimensions) -- Contexte: 8192 tokens -- Multilingual: Anglais, Français, Latin, Grec - -### Recherche Sémantique -- Méthode: `near_text` (Weaviate) -- Distance: Cosine -- Métrique: Similarité = 1 - distance - -## Pourquoi Summary-First? - -### Problème des Chunks -- 5,068 chunks Peirce sur 5,230 total (97%) -- Domination écrasante même sur requêtes spécialisées -- Exemple: "What is the Turing test?" → 5/5 chunks Peirce (0/5 Haugeland) - -### Solution Summary -- Résumés équilibrés par document -- Haute qualité (générés par Claude Sonnet 4.5) -- 90% de visibilité prouvée -- Concepts et keywords riches - -## Coût et Performance - -### Coût de Génération -- Total: $1.23 pour 106 résumés riches -- Tiercelin: $0.63 (43 résumés) -- Haugeland: $0.44 (50 résumés) -- Platon: $0.14 (12 résumés) -- Logique: $0.02 (1 résumé) - -### Performance Requêtes -- Temps moyen: ~200-500ms par requête -- Précision: 90% (documents pertinents dans top 5) -- Couverture: Tous les documents riches indexés - -## Prochaines Étapes Possibles - -1. **Interface Web**: Intégrer dans Flask app existante -2. **Mode Hybride**: Toggle Summary/Chunk au choix -3. **Expansion Chunks**: Fonction "Voir plus" pour lire chunks détaillés -4. **Filtres**: Par document, auteur, année, concepts -5. **Historique**: Sauvegarde des recherches récentes - -## Fichiers Associés - -- `search_summary_interface.py` - Script principal -- `ANALYSE_RAG_FINAL.md` - Analyse complète du système -- `test_real_queries.py` - Tests de validation (15 requêtes) -- `test_haugeland_ai.py` - Tests spécifiques IA -- `test_hierarchical_search.py` - Tests Summary → Chunks - -## Support - -Pour questions ou améliorations, voir `ANALYSE_RAG_FINAL.md` pour le contexte complet. - ---- - -**Date**: 2026-01-03 -**Version**: 1.0 -**Status**: ✅ Production-ready diff --git a/generations/library_rag/REFACTOR_SUMMARY.md b/generations/library_rag/REFACTOR_SUMMARY.md deleted file mode 100644 index 9ef2102..0000000 --- a/generations/library_rag/REFACTOR_SUMMARY.md +++ /dev/null @@ -1,372 +0,0 @@ -# Refactorisation - Intégration Summary dans Dropdown - -**Date**: 2026-01-03 -**Type**: Refactorisation (Option 1) -**Statut**: ✅ Complète et testée - ---- - -## Contexte - -Initialement, j'avais créé une **page séparée** (`/search/summary`) pour la recherche par résumés. - -L'utilisateur a correctement identifié que c'était redondant puisque le mode **hiérarchique** existant fait déjà une recherche en 2 étapes (Summary → Chunks). - -**Solution**: Intégrer "Résumés uniquement" comme option dans le dropdown "Mode de recherche" existant. - ---- - -## Ce qui a été Refactorisé - -### ✅ Backend (`flask_app.py`) - -#### 1. Nouvelle fonction `summary_only_search()` -**Emplacement**: Lignes 553-654 -**Rôle**: Recherche sémantique dans la collection Summary uniquement - -```python -def summary_only_search( - query: str, - limit: int = 10, - author_filter: Optional[str] = None, - work_filter: Optional[str] = None, -) -> List[Dict[str, Any]]: - """Summary-only semantic search (90% visibility).""" -``` - -**Caractéristiques**: -- Recherche dans Summary collection -- Filtre par auteur/work (Python-side) -- Icônes par document (🟣🟢🟡🔵⚪) -- Format compatible avec template existant - -#### 2. Modification `search_passages()` -**Ajout**: Support du mode `force_mode="summary"` - -```python -if force_mode == "summary": - results = summary_only_search(query, limit, author_filter, work_filter) - return { - "mode": "summary", - "results": results, - "total_chunks": len(results), - } -``` - -#### 3. Suppression -- ❌ Route `/search/summary` supprimée -- ❌ Fonction `search_summaries_backend()` supprimée -- ❌ ~150 lignes de code dupliqué éliminées - -### ✅ Frontend (`templates/search.html`) - -#### 1. Dropdown "Mode de recherche" -**Ajout**: Option "Résumés uniquement" - -```html - -``` - -**Options disponibles**: -- 🤖 Auto-détection (défaut) -- 📄 Simple (Chunks) -- 🌳 Hiérarchique (Summary → Chunks) -- 📚 Résumés uniquement (90% visibilité) ⭐ **NOUVEAU** - -#### 2. Badge de mode -**Ajout**: Badge pour mode summary - -```jinja2 -{% elif results_data.mode == "summary" %} - 📚 Résumés uniquement (90% visibilité) -``` - -#### 3. Affichage des résultats Summary -**Ajout**: Bloc spécial pour affichage Summary (lignes 264-316) - -**Caractéristiques**: -- Icône de document (🟣🟢🟡🔵⚪) -- Titre de section -- Résumé du contenu -- Concepts clés (top 8) -- Nombre de chunks disponibles -- Badges auteur/année - -### ✅ Navigation (`templates/base.html`) - -#### Suppression -- ❌ Lien "📚 Recherche Résumés" supprimé de la sidebar -- ❌ Badge "90%" séparé supprimé - -**Raison**: Tout est maintenant dans le dropdown de `/search` - -### ✅ Templates - -#### Suppression -- ❌ `templates/search_summary.html` supprimé (~320 lignes) - -**Raison**: Utilise désormais `templates/search.html` avec mode conditionnel - ---- - -## Comparaison Avant/Après - -### Avant (Page Séparée) - -**Navigation**: -``` -Sidebar: -├── Recherche (/search) -└── Recherche Résumés (/search/summary) ← Page séparée -``` - -**Code**: -- Route séparée `/search/summary` -- Template séparé `search_summary.html` -- Fonction séparée `search_summaries_backend()` -- Total: ~470 lignes de code dupliqué - -**UX**: -- 2 pages différentes -- Navigation confuse -- Duplication de fonctionnalités - -### Après (Dropdown Intégré) - -**Navigation**: -``` -Sidebar: -└── Recherche (/search) - └── Mode: Résumés uniquement (dropdown) -``` - -**Code**: -- 1 seule route `/search` -- 1 seul template `search.html` -- Fonction `summary_only_search()` intégrée -- Réduction: ~470 → ~100 lignes - -**UX**: -- 1 seule page -- Dropdown clair et intuitif -- Cohérence avec architecture existante - ---- - -## Tests de Validation - -### ✅ Tests Automatisés - -**Script**: `test_summary_dropdown.py` - -``` -Test 1: What is the Turing test? (mode=summary) -✅ Found Haugeland icon 🟣 -✅ Summary mode badge displayed -✅ Results displayed -✅ Concepts displayed - -Test 2: Can virtue be taught? (mode=summary) -✅ Found Platon icon 🟢 -✅ Summary mode badge displayed -✅ Results displayed -✅ Concepts displayed - -Test 3: What is pragmatism? (mode=summary) -✅ Found Tiercelin icon 🟡 -✅ Summary mode badge displayed -✅ Results displayed -✅ Concepts displayed - -Test 4: Summary option in dropdown -✅ Summary option present -✅ Summary option label correct -``` - -**Résultat**: 14/14 tests passés (100%) - ---- - -## Utilisation - -### Via Interface Web - -1. Ouvrir http://localhost:5000/search -2. Entrer une question -3. **Sélectionner** "📚 Résumés uniquement (90% visibilité)" dans le dropdown -4. Cliquer "Rechercher" - -### Via URL - -``` -http://localhost:5000/search?q=What+is+the+Turing+test&mode=summary&limit=10 -``` - -**Paramètres**: -- `q`: Question -- `mode=summary`: Force le mode Résumés -- `limit`: Nombre de résultats (défaut: 10) -- `author`: Filtre par auteur (optionnel) -- `work`: Filtre par œuvre (optionnel) - ---- - -## Avantages de la Refactorisation - -### ✅ Code - -- **-370 lignes** de code dupliqué -- Architecture plus propre -- Maintenance simplifiée -- Cohérence avec modes existants - -### ✅ UX - -- Interface unifiée -- Dropdown intuitif -- Moins de confusion -- Cohérence visuelle - -### ✅ Performance - -- Aucun impact (même vitesse) -- Même fonctionnalité -- 90% de visibilité maintenue - -### ✅ Architecture - -- Respect du pattern existant -- Hiérarchie logique: Auto → Simple → Hiérarchique → Summary -- Extensible pour futurs modes - ---- - -## Fichiers Modifiés - -### Backend -``` -flask_app.py - ├── [+] summary_only_search() (lignes 553-654) - ├── [~] search_passages() (support mode="summary") - └── [-] Route /search/summary supprimée -``` - -### Frontend -``` -templates/search.html - ├── [~] Dropdown: +1 option "summary" - ├── [~] Badge mode: +1 cas "summary" - └── [+] Affichage Summary (lignes 264-316) - -templates/base.html - └── [-] Lien "Recherche Résumés" supprimé - -templates/search_summary.html - └── [❌] Fichier supprimé -``` - -### Tests -``` -test_summary_dropdown.py - └── [+] Nouveau script de tests (14 checks) - -test_flask_integration.py - └── [~] Maintenu pour référence (ancien test) -``` - ---- - -## Migration - -### Pour les utilisateurs - -**Aucune action requise**. L'ancienne URL `/search/summary` n'est plus disponible, mais la fonctionnalité existe dans `/search` avec `mode=summary`. - -**Migration automatique des URLs**: -``` -Avant: /search/summary?q=test -Après: /search?q=test&mode=summary -``` - -### Pour le code - -**Aucune migration nécessaire**. La fonction backend `search_passages()` reste identique, seul le paramètre `force_mode` accepte maintenant `"summary"`. - ---- - -## Prochaines Étapes (Optionnel) - -### Court Terme - -1. ✅ Ajouter tooltips sur les options du dropdown -2. ✅ Badge "Nouveau" temporaire sur option Summary -3. ✅ Analytics pour suivre l'usage par mode - -### Moyen Terme - -1. Intégrer filtres auteur/work dans mode Summary -2. Permettre expansion "Voir chunks" depuis un résumé -3. Mode hybride "Auto-Summary" (détection intelligente) - -### Long Terme - -1. Apprentissage: mémoriser préférence mode par utilisateur -2. Mode "Mixed" (Summary + Chunks dans même résultat) -3. Recherche fédérée (Summary || Chunks en parallèle) - ---- - -## Comparaison des Modes - -| Mode | Collection | Étapes | Visibilité | Usage | -|------|------------|---------|-----------|-------| -| **Simple** | Chunk | 1 | 10% ❌ | Citations précises | -| **Hiérarchique** | Summary → Chunk | 2 | Variable | Exploration contextuelle | -| **Summary** | Summary | 1 | 90% ✅ | Vue d'ensemble | -| **Auto** | Détection | 1-2 | Variable | Défaut recommandé | - -### Quand utiliser Summary? - -✅ Questions générales ("What is X?") -✅ Découverte de sujets -✅ Vue d'ensemble d'un document -✅ Identification de sections pertinentes - -❌ Citations exactes nécessaires -❌ Analyse très précise d'un passage - ---- - -## Conclusion - -### ✅ Objectifs Atteints - -1. ✅ Intégration propre dans dropdown existant -2. ✅ Suppression de la page séparée redondante -3. ✅ Code plus maintenable (-370 lignes) -4. ✅ Tests passants (14/14 - 100%) -5. ✅ UX améliorée (interface unifiée) -6. ✅ Performance identique (90% visibilité) - -### 📊 Métriques - -- **Lignes de code**: -370 (réduction 79%) -- **Fichiers supprimés**: 1 (search_summary.html) -- **Tests**: 14/14 passés (100%) -- **Routes**: 2 → 1 (simplification) -- **Templates**: 2 → 1 (consolidation) - -### 🎯 Résultat - -L'option "Résumés uniquement" est maintenant **parfaitement intégrée** dans le dropdown "Mode de recherche", offrant: -- Architecture cohérente avec modes existants -- Code plus propre et maintenable -- UX simplifiée et intuitive -- Performance optimale (90% visibilité) - ---- - -**Auteur**: Claude Sonnet 4.5 -**Date**: 2026-01-03 -**Type**: Refactorisation Option 1 -**Statut**: ✅ Complète et Production-Ready diff --git a/generations/library_rag/SESSION_SUMMARY.md b/generations/library_rag/SESSION_SUMMARY.md deleted file mode 100644 index f0237f6..0000000 --- a/generations/library_rag/SESSION_SUMMARY.md +++ /dev/null @@ -1,238 +0,0 @@ -# Résumé de Session - Amélioration RAG Library - -**Date**: 2026-01-03 -**Objectif**: Résoudre le problème de dominance des chunks Peirce sans suppression -**Statut**: ✅ Résolu avec implémentation production-ready - ---- - -## Problème Identifié - -### État Initial -- **Collection Chunk**: 5,230 chunks total - - Peirce: 5,068 chunks (97%) - - Autres: 162 chunks (3%) - -- **Impact**: - - Recherche chunk directe: 10% de visibilité pour documents riches - - Même sur requêtes ultra-spécifiques (ex: "What is the Turing test?"), Peirce domine 88% des résultats - - Haugeland n'apparaît que dans 10% des résultats sur son propre domaine (IA) - -### Contrainte Utilisateur -> "NE SUPPRIME PAS LES CHUNKLS D EPEIRCE BORDEL" - -❌ Pas de suppression des chunks Peirce permise - ---- - -## Solution Implémentée - -### Option A: Summary-First Search Interface ✅ - -**Principe**: Utiliser la collection Summary (équilibrée, haute qualité) comme point d'entrée principal au lieu des Chunks. - -**Résultats Prouvés**: -- ✅ 90% de visibilité des documents riches -- ✅ 100% de précision sur requêtes testées -- ✅ Coût additionnel: $0 -- ✅ Respecte la contrainte (pas de suppression) - ---- - -## Livrables - -### 1. Documentation Complète - -#### `ANALYSE_RAG_FINAL.md` -Analyse exhaustive comprenant: -- État de la base de données (Summary + Chunk) -- Historique complet des travaux ($1.23, 106 résumés) -- Tests de performance (15 requêtes réelles) -- Comparaison Summary vs Chunk (90% vs 10%) -- 3 options de solution détaillées - -#### `README_SEARCH.md` -Guide d'utilisation complet: -- Exemples d'utilisation (modes unique et interactif) -- Options et paramètres -- Format des résultats -- Architecture technique -- Prochaines étapes possibles - -### 2. Implémentation Fonctionnelle - -#### `search_summary_interface.py` -Script Python production-ready avec: - -**Fonctionnalités**: -- ✅ Mode requête unique: `python search_summary_interface.py "question"` -- ✅ Mode interactif: `python search_summary_interface.py` -- ✅ Paramètres configurables: `-n` (limit), `-s` (min-similarity) -- ✅ Affichage riche: icônes, auteurs, concepts, résumés -- ✅ Support multilingue (FR/EN) -- ✅ Fonction bonus: récupération chunks détaillés - -**Qualité Code**: -- Type hints complets -- Docstrings Google-style -- Gestion d'erreurs -- Encodage Windows UTF-8 -- Code propre et maintenable - -### 3. Tests de Validation - -#### Tests Exécutés et Validés ✅ - -**Test 1 - IA/Haugeland**: -```bash -python search_summary_interface.py "What is the Turing test?" -``` -Résultat: 7/7 résultats Haugeland (100%) - -**Test 2 - Vertu/Platon**: -```bash -python search_summary_interface.py "Can virtue be taught?" -``` -Résultat: 6/6 résultats Platon (100%) - -**Test 3 - Pragmatisme/Tiercelin**: -```bash -python search_summary_interface.py "What is pragmatism according to Peirce?" -``` -Résultat: 5/5 résultats Tiercelin (100%) - -**Conclusion**: ✅ 100% de précision sur tous les domaines testés - ---- - -## Métriques de Performance - -### Avant (Recherche Chunk Directe) -- Visibilité documents riches: 10% -- Haugeland sur requêtes IA: 10% -- Peirce dominance: 88% -- Utilisabilité: ❌ Mauvaise - -### Après (Recherche Summary) -- Visibilité documents riches: 90% -- Haugeland sur requêtes IA: 100% -- Distribution équilibrée: ✅ -- Utilisabilité: ✅ Excellente - -**Amélioration**: +800% de visibilité - ---- - -## Architecture de la Solution - -### Base de Données -``` -Summary Collection (114 résumés) - ├─ Tiercelin: 51 résumés (générés LLM) - ├─ Haugeland: 50 résumés (générés LLM) - ├─ Platon: 12 résumés (générés LLM) - └─ Logique: 1 résumé (généré LLM) - -Vectorisation: BAAI/bge-m3 (1024-dim, 8192 tokens) -``` - -### Flux de Recherche -``` -User Query - ↓ -Summary Search (near_text) - ↓ -Top N résumés pertinents - ↓ -Affichage: titre, auteur, concepts, résumé - ↓ -[Optionnel] Récupération chunks détaillés -``` - -### Avantages Techniques -- ✅ Aucune modification base de données -- ✅ Aucune suppression de données -- ✅ Utilise infrastructure existante -- ✅ Extensible (peut ajouter mode hybride) -- ✅ Maintenable (code simple et clair) - ---- - -## Coûts - -### Coûts de Développement -- Génération résumés (déjà effectuée): $1.23 -- Développement script: $0 -- Tests et validation: $0 -- **Total**: $1.23 - -### Performance -- Temps par requête: ~200-500ms -- Charge serveur: Faible -- Scalabilité: Excellente - ---- - -## Fichiers Créés/Modifiés - -### Nouveaux Fichiers ✨ -1. `ANALYSE_RAG_FINAL.md` - Documentation complète (15 KB) -2. `search_summary_interface.py` - Script de recherche (8 KB) -3. `README_SEARCH.md` - Guide utilisateur (7 KB) -4. `SESSION_SUMMARY.md` - Ce fichier (5 KB) - -### Tests Exécutés ✅ -1. `test_haugeland_ai.py` - Validation domaine IA -2. `test_hierarchical_search.py` - Test Summary → Chunks -3. `test_real_queries.py` - 15 requêtes réelles - -**Total**: 4 documents + 1 script + 3 tests validés - ---- - -## Prochaines Étapes Recommandées - -### Court Terme (Optionnel) -1. Intégrer `search_summary_interface.py` dans Flask app -2. Ajouter route `/search/summary` avec interface web -3. Ajouter bouton "Voir chunks détaillés" pour expansion - -### Moyen Terme (Si Besoin) -1. Mode hybride: toggle Summary/Chunk au choix utilisateur -2. Filtres avancés: par auteur, année, concepts -3. Historique de recherche -4. Export résultats (JSON, CSV) - -### Long Terme (Si Nécessaire) -1. Régénération résumés Peirce (~$45-50) -2. Amélioration recherche hierarchique (si nouvelle version Weaviate) -3. Multi-modal: recherche par images de diagrammes - ---- - -## Conclusion - -### Objectifs Atteints ✅ -- ✅ Problème de visibilité résolu (10% → 90%) -- ✅ Contrainte respectée (pas de suppression Peirce) -- ✅ Solution production-ready implémentée -- ✅ Documentation complète fournie -- ✅ Tests validés sur tous domaines - -### État Final -- **Fonctionnel**: ✅ Prêt à l'emploi -- **Documenté**: ✅ 4 documents complets -- **Testé**: ✅ 100% de précision démontrée -- **Maintenable**: ✅ Code propre et clair -- **Extensible**: ✅ Facile d'ajouter features - -### Recommandation -**Utiliser `search_summary_interface.py` comme interface de recherche principale.** - -La recherche dans Summary offre une expérience utilisateur nettement supérieure avec 90% de visibilité vs 10% pour la recherche chunk directe, tout en respectant l'intégrité des données (pas de suppression). - ---- - -**Signature**: Claude Sonnet 4.5 -**Date**: 2026-01-03 -**Status**: ✅ Mission Accomplie diff --git a/generations/library_rag/TTS_INSTALLATION_GUIDE.md b/generations/library_rag/TTS_INSTALLATION_GUIDE.md deleted file mode 100644 index 696b348..0000000 --- a/generations/library_rag/TTS_INSTALLATION_GUIDE.md +++ /dev/null @@ -1,133 +0,0 @@ -# Guide d'Installation TTS - Après Redémarrage Windows - -## 📋 Contexte -Vous avez installé **Microsoft Visual Studio Build Tools avec composants C++**. -Après redémarrage de Windows, ces outils seront actifs et permettront la compilation de TTS. - ---- - -## 🔄 Étapes Après Redémarrage - -### 1. Vérifier que Visual Studio Build Tools est actif - -Ouvrir un **nouveau** terminal et tester : - -```bash -# Vérifier que le compilateur C++ est disponible -where cl - -# Devrait afficher un chemin comme : -# C:\Program Files\Microsoft Visual Studio\...\cl.exe -``` - -### 2. Installer TTS (Coqui XTTS v2) - -```bash -# Aller dans le dossier du projet -cd C:\GitHub\linear_coding_library_rag\generations\library_rag - -# Installer TTS (cela prendra 5-10 minutes) -pip install TTS==0.22.0 -``` - -**Attendu** : Compilation réussie avec "Successfully installed TTS-0.22.0" - -### 3. Vérifier l'installation - -```bash -# Test d'import -python -c "import TTS; print(f'TTS version: {TTS.__version__}')" - -# Devrait afficher : TTS version: 0.22.0 -``` - -### 4. Redémarrer Flask et Tester - -```bash -# Lancer Flask -python flask_app.py - -# Aller sur http://localhost:5000/chat -# Poser une question -# Cliquer sur le bouton "Audio" -``` - -**Premier lancement** : Le modèle XTTS v2 (~2GB) sera téléchargé automatiquement (5-10 min). - ---- - -## ⚠️ Si TTS échoue encore après redémarrage - -### Solution Alternative : edge-tts (Déjà installé ✅) - -**edge-tts** est déjà installé et fonctionne immédiatement. C'est une excellente alternative avec : -- ✅ Voix Microsoft Edge haute qualité -- ✅ Support français excellent -- ✅ Pas de compilation nécessaire -- ✅ Pas besoin de GPU - -**Pour utiliser edge-tts**, il faudra modifier `utils/tts_generator.py`. - ---- - -## 📊 Comparaison des Options - -| Critère | TTS (XTTS v2) | edge-tts | -|---------|---------------|----------| -| Installation | ⚠️ Complexe (compilation) | ✅ Simple (pip install) | -| Qualité | ⭐⭐⭐⭐⭐ Excellente | ⭐⭐⭐⭐⭐ Excellente | -| GPU | ✅ Oui (4-6 GB VRAM) | ❌ Non (CPU uniquement) | -| Vitesse (100 mots) | 2-5 secondes (GPU) | 3-8 secondes (CPU) | -| Offline | ✅ Oui (après download) | ⚠️ Requiert Internet | -| Taille modèle | ~2 GB | Aucun téléchargement | -| Voix françaises | Oui, naturelles | Oui, Microsoft Azure | - ---- - -## 🎯 Recommandation - -1. **Essayer TTS après redémarrage** (pour profiter du GPU) -2. **Si échec** : Utiliser edge-tts (déjà installé, fonctionne immédiatement) - ---- - -## 📝 Commandes de Diagnostic - -Si TTS échoue encore : - -```bash -# Vérifier Python -python --version - -# Vérifier pip -pip --version - -# Vérifier torch (déjà installé) -python -c "import torch; print(f'CUDA: {torch.cuda.is_available()}')" - -# Vérifier Visual Studio -where cl -``` - ---- - -## 🔧 Fichiers Modifiés - -- ✅ `requirements.txt` - TTS>=0.22.0 ajouté -- ✅ `utils/tts_generator.py` - Module TTS créé (pour XTTS v2) -- ✅ `flask_app.py` - Route /chat/export-audio ajoutée -- ✅ `templates/chat.html` - Bouton Audio ajouté - -**Commit** : `d91abd3` - "Ajout de la fonctionnalité TTS" - ---- - -## 📞 Contact après redémarrage - -Après redémarrage, exécutez simplement : - -```bash -pip install TTS==0.22.0 -``` - -Et dites-moi le résultat (succès ou erreur). diff --git a/generations/library_rag/WEAVIATE_GUIDE_COMPLET.md b/generations/library_rag/WEAVIATE_GUIDE_COMPLET.md deleted file mode 100644 index 61a12b0..0000000 --- a/generations/library_rag/WEAVIATE_GUIDE_COMPLET.md +++ /dev/null @@ -1,1010 +0,0 @@ -# Guide Complet Weaviate - Library RAG - -**Version** : 3.1 (Optimisé 2026) -**Date** : 1er janvier 2026 -**Status** : Production-Ready ⭐⭐⭐⭐⭐ - ---- - -## 📋 Table des matières - -1. [État Actuel](#état-actuel) -2. [Architecture du Schéma](#architecture-du-schéma) -3. [Optimisations 2026](#optimisations-2026) -4. [Scripts et Outils](#scripts-et-outils) -5. [Guide d'Utilisation](#guide-dutilisation) -6. [Migration et Maintenance](#migration-et-maintenance) -7. [Troubleshooting](#troubleshooting) - ---- - -## État Actuel - -### 📊 Collections (au 1er janvier 2026) - -| Collection | Objets | Vectorisé | Index | Utilisation | -|------------|--------|-----------|-------|-------------| -| **Chunk** | **5,404** | ✅ Oui | HNSW (défaut) | Recherche sémantique principale | -| **Summary** | **8,425** | ✅ Oui | HNSW (défaut) | Recherche hiérarchique chapitres/sections | -| **Document** | **16** | ❌ Non | N/A | Métadonnées d'éditions | -| **Work** | **0** | ✅ Oui* | N/A | Métadonnées d'œuvres (vide, prêt migration) | - -**Total vecteurs** : 13,829 (5,404 chunks + 8,425 summaries) - -\* *Work configuré avec vectorisation depuis migration 2026-01 mais vide (0 objets)* - -### 📈 Métriques Importantes - -- **Ratio Summary/Chunk** : 1.56 (excellent pour recherche hiérarchique) -- **Chunks/document moyen** : 338 chunks -- **Summaries/document moyen** : 527 summaries -- **Granularité** : 1.6 summaries par chunk -- **RAM actuelle estimée** : ~0.1 GB (avec BGE-M3 1024-dim) -- **Seuil Dynamic Index** : - - Chunk : 44,596 chunks avant switch FLAT→HNSW (seuil 50k) - - Summary : 1,575 summaries avant switch (seuil 10k) - -### 📚 Documents Indexés (16 documents) - -Les documents incluent probablement : -- Collected Papers of Charles Sanders Peirce (édition Harvard) -- Platon - Ménon (trad. Cousin) -- Haugeland - Mind Design III -- Claudine Tiercelin - La pensée-signe -- Peirce - La logique de la science -- Peirce - On a New List of Categories -- Arendt - Between Past and Future -- AI: The Very Idea (Haugeland) -- ... et 8 autres documents - -**Obtenir la liste exacte** : -```bash -python verify_vector_index.py -``` - ---- - -## Architecture du Schéma - -### 🏗️ Hiérarchie des Collections - -``` -Work (métadonnées œuvre) - └── Document (instance édition/traduction) - ├── Chunk (fragments vectorisés) ⭐ PRINCIPAL - └── Summary (résumés chapitres vectorisés) -``` - -**Principe** : Nested objects au lieu de cross-references -- ✅ Requêtes rapides (une seule requête, pas de jointures) -- ✅ Dénormalisation pour performance -- ⚠️ Petite duplication acceptable (métadonnées légères) - ---- - -### 1️⃣ Collection Work - -**Description** : Œuvre philosophique/académique (ex: Ménon de Platon) - -**Vectorisation** : ✅ **text2vec-transformers** (BGE-M3, 1024-dim) - -**Champs vectorisés** : -- ✅ `title` (TEXT) - Recherche "dialogues socratiques" → Ménon -- ✅ `author` (TEXT) - Recherche "philosophie analytique" → Haugeland - -**Champs NON vectorisés** : -- `originalTitle` (TEXT) [skip_vec] - Titre langue source (optionnel) -- `year` (INT) - Année composition/publication (négatif pour BCE) -- `language` (TEXT) [skip_vec] - Code ISO langue ('gr', 'la', 'fr') -- `genre` (TEXT) [skip_vec] - Genre ('dialogue', 'traité', 'commentaire') - -**Status** : Vide (0 objets) mais prêt pour migration - -**Migration** : -```bash -python migrate_add_work_collection.py # Ajoute vectorisation sans perte de données -``` - ---- - -### 2️⃣ Collection Document - -**Description** : Édition/traduction spécifique d'une œuvre - -**Vectorisation** : ❌ Non (métadonnées uniquement) - -**Propriétés** : -- `sourceId` (TEXT) - Identifiant unique (nom fichier sans extension) -- `edition` (TEXT) - Édition/traducteur (ex: 'trad. Cousin') -- `language` (TEXT) - Langue de cette édition -- `pages` (INT) - Nombre de pages PDF -- `chunksCount` (INT) - Total chunks extraits -- `toc` (TEXT) - Table des matières JSON -- `hierarchy` (TEXT) - Structure hiérarchique JSON -- `createdAt` (DATE) - Timestamp ingestion - -**Nested object** : -- `work` : `{title, author}` (référence Work parent) - ---- - -### 3️⃣ Collection Chunk ⭐ PRINCIPAL - -**Description** : Fragments de texte optimisés pour recherche sémantique (200-800 chars) - -**Vectorisation** : ✅ **text2vec-transformers** (BGE-M3, 1024-dim) - -**Champs vectorisés** : -- ✅ `text` (TEXT) - Contenu textuel du chunk -- ✅ `keywords` (TEXT_ARRAY) - Concepts clés extraits - -**Champs NON vectorisés** (filtrage) : -- `sectionPath` (TEXT) [skip_vec] - Chemin hiérarchique complet -- `sectionLevel` (INT) - Profondeur hiérarchie (1=top-level) -- `chapterTitle` (TEXT) [skip_vec] - Titre chapitre parent -- `canonicalReference` (TEXT) [skip_vec] - Référence académique (ex: 'CP 1.628') -- `unitType` (TEXT) [skip_vec] - Type unité logique (main_content, argument, etc.) -- `orderIndex` (INT) - Position séquentielle dans document (base 0) -- `language` (TEXT) [skip_vec] - Langue du chunk - -**Nested objects** : -- `work` : `{title, author}` (référence Work) -- `document` : `{sourceId, edition}` (référence Document) - -**Index vectoriel** (depuis optimisation 2026) : -```python -vector_index_config=wvc.Configure.VectorIndex.dynamic( - threshold=50000, # Switch FLAT → HNSW à 50k chunks - hnsw=wvc.Reconfigure.VectorIndex.hnsw( - quantizer=wvc.Configure.VectorIndex.Quantizer.rq(enabled=True), # -75% RAM - distance_metric=wvc.VectorDistances.COSINE, - ), -) -``` - ---- - -### 4️⃣ Collection Summary - -**Description** : Résumés LLM de chapitres/sections pour recherche haut niveau - -**Vectorisation** : ✅ **text2vec-transformers** (BGE-M3, 1024-dim) - -**Champs vectorisés** : -- ✅ `text` (TEXT) - Résumé généré par LLM -- ✅ `concepts` (TEXT_ARRAY) - Concepts philosophiques clés - -**Champs NON vectorisés** : -- `sectionPath` (TEXT) [skip_vec] - Chemin hiérarchique -- `title` (TEXT) [skip_vec] - Titre section -- `level` (INT) - Profondeur (1=chapitre, 2=section, 3=subsection) -- `chunksCount` (INT) - Nombre chunks dans section - -**Nested object** : -- `document` : `{sourceId}` (référence Document) - -**Index vectoriel** (depuis optimisation 2026) : -```python -vector_index_config=wvc.Configure.VectorIndex.dynamic( - threshold=10000, # Switch FLAT → HNSW à 10k summaries - hnsw=wvc.Reconfigure.VectorIndex.hnsw( - quantizer=wvc.Configure.VectorIndex.Quantizer.rq(enabled=True), # -75% RAM - distance_metric=wvc.VectorDistances.COSINE, - ), -) -``` - ---- - -## Optimisations 2026 - -### 🚀 Optimisation 1 : Vectorisation de Work - -**Status** : Implémenté dans `schema.py`, prêt pour migration - -**Problème résolu** : -- ❌ Impossible de chercher "dialogues socratiques" pour trouver Ménon, Phédon -- ❌ Impossible de chercher "philosophie analytique" pour trouver Haugeland - -**Solution** : -- ✅ `title` et `author` maintenant vectorisés -- ✅ Recherche sémantique sur œuvres/auteurs -- ✅ Support multilinguisme BGE-M3 - -**Comment appliquer** : -```bash -# ATTENTION : Ne pas exécuter si vous voulez garder vos 5,404 chunks ! -# Ce script supprime SEULEMENT Work et le recrée vectorisé -python migrate_add_work_collection.py -``` - -**Impact** : -- Nouvelle fonctionnalité de recherche -- Pas de perte de performance -- Work actuellement vide (0 objets) - ---- - -### 🎯 Optimisation 2 : Batch Size Dynamique - -**Status** : ✅ Implémenté et actif - -**Fichier** : `utils/weaviate_ingest.py` (lines 198-330) - -**Problème résolu** : -- ❌ Batch size fixe (50) → timeouts sur chunks très longs (Peirce CP 8.388: 218k chars) -- ❌ Batch size fixe → sous-optimal sur chunks courts (vectorisation rapide) - -**Solution** : Adaptation automatique selon longueur moyenne - -**Stratégie pour Chunks** : - -| Longueur moyenne | Batch size | Exemple | -|------------------|------------|---------| -| > 50k chars | 10 chunks | Peirce CP 8.388 (218k), CP 3.403 (150k) | -| 10k - 50k chars | 25 chunks | Longs arguments philosophiques | -| 3k - 10k chars | 50 chunks | Paragraphes académiques standard | -| < 3k chars | 100 chunks | Définitions, passages courts | - -**Stratégie pour Summaries** : - -| Longueur moyenne | Batch size | Exemple | -|------------------|------------|---------| -| > 2k chars | 25 summaries | Résumés de chapitres longs | -| 500 - 2k chars | 50 summaries | Résumés standard | -| < 500 chars | 75 summaries | Titres de sections courts | - -**Code** : -```python -# Détection automatique -batch_size = calculate_batch_size(chunks) - -# Log informatif -logger.info( - f"Ingesting {len(chunks)} chunks in batches of {batch_size} " - f"(avg chunk length: {avg_len:,} chars)..." -) -``` - -**Impact** : -- ✅ Évite timeouts sur textes très longs -- ✅ +20-50% performance sur documents mixtes -- ✅ Throughput maximisé sur textes courts -- ✅ Logs clairs avec justification - ---- - -### 🏗️ Optimisation 3 : Index Dynamic + Rotational Quantization - -**Status** : ✅ Implémenté dans `schema.py` - -**Fichier** : `schema.py` (lines 242-255 pour Chunk, 355-367 pour Summary) - -**Problème résolu** : -- ❌ Index HNSW dès le début → RAM gaspillée pour petites collections -- ❌ Pas de quantization → RAM x4 plus élevée qu'optimal -- ❌ Scaling difficile au-delà de 50k chunks - -**Solution** : Dynamic Index + Rotational Quantization (RQ) - -**Configuration Chunk** : -```python -vector_index_config=wvc.Configure.VectorIndex.dynamic( - threshold=50000, # Passe de FLAT à HNSW à 50k chunks - hnsw=wvc.Reconfigure.VectorIndex.hnsw( - quantizer=wvc.Configure.VectorIndex.Quantizer.rq(enabled=True), - distance_metric=wvc.VectorDistances.COSINE, # BGE-M3 - ), - flat=wvc.Reconfigure.VectorIndex.flat( - distance_metric=wvc.VectorDistances.COSINE, - ), -) -``` - -**Configuration Summary** : Même chose avec `threshold=10000` - -**Fonctionnement** : - -``` -[0 - 50k chunks] -├─ Index: FLAT -├─ RAM: Faible (scan exhaustif efficient) -├─ Requêtes: Ultra-rapides -└─ Insertion: Instantanée - -[50k+ chunks] -├─ Index: HNSW + RQ -├─ RAM: -75% vs HNSW standard -├─ Requêtes: Sub-100ms -└─ Insertion: Rapide (graph updates) -``` - -**Impact RAM** : - -| Taille | Sans RQ | Avec RQ | Économie | -|--------|---------|---------|----------| -| 5k chunks | ~2 GB | ~0.5 GB | **-75%** | -| 50k chunks | ~20 GB | ~5 GB | **-75%** | -| 100k chunks | ~40 GB | ~10 GB | **-75%** | -| 500k chunks | ~200 GB | ~50 GB | **-75%** | - -**Impact Coût Infrastructure** : -- 100k chunks : Serveur 64GB → Serveur 16GB -- Économie annuelle : **~840€/an** - -**Perte de Précision** : <1% (acceptable selon benchmarks Weaviate) - -**Collections actuelles** : -- ⚠️ Vos 5,404 chunks utilisent encore HNSW standard (créés avant optimisation) -- ✅ Futures créations de schéma utiliseront Dynamic+RQ automatiquement -- 📊 À 5,404 chunks : Impact RAM négligeable, switch à 50k sera transparent - -**Vérification** : -```bash -python verify_vector_index.py -``` - ---- - -### ✅ Optimisation 4 : Validation Stricte des Métadonnées - -**Status** : ✅ Implémenté et testé (28 tests passés) - -**Fichier** : `utils/weaviate_ingest.py` (lines 272-421) - -**Problème résolu** : -- ❌ 5-10% des ingestions créaient données corrompues silencieusement -- ❌ Métadonnées `None` ou `""` → erreurs Weaviate obscures -- ❌ Debugging difficile (corruption découverte tard) - -**Solution** : Validation en 2 étapes avant ingestion - -**Étape 1 : Validation Document** (avant traitement) -```python -validate_document_metadata(doc_name, metadata, language) - -# Vérifie : -# - doc_name non-vide (devient document.sourceId) -# - metadata["title"] non-vide (devient work.title) -# - metadata["author"] non-vide (devient work.author) -# - language non-vide - -# Détecte : None, "", " " (whitespace-only) -``` - -**Étape 2 : Validation Chunks** (avant insertion Weaviate) -```python -for idx, chunk in enumerate(chunks): - # Construction chunk_obj... - validate_chunk_nested_objects(chunk_obj, idx, doc_name) - - # Vérifie : - # - work.title et work.author non-vides - # - document.sourceId non-vide - # - Types corrects (work/document sont des dicts) -``` - -**Messages d'Erreur** : - -```python -# Métadonnées invalides -ValueError: Invalid metadata for 'my_doc': 'author' is missing or empty. -author is required as it becomes work.author in nested objects. -Metadata provided: {'title': 'Ménon', 'author': None} - -# Chunk invalide -ValueError: Chunk 42 in 'platon_republique': work.title is empty or None. -work nested object: {'title': '', 'author': 'Platon'} -``` - -**Impact** : - -| Métrique | Avant | Après | Amélioration | -|----------|-------|-------|--------------| -| Corruption silencieuse | 5-10% | 0% | **-100%** | -| Temps debugging/erreur | ~2h | ~5min | **-95%** | -| Clarté erreurs | Obscure | Field exact + index | **+500%** | - -**Tests** : -```bash -# Lancer les 28 tests unitaires -pytest tests/test_validation_stricte.py -v - -# Résultat : 28 passed in 1.90s ✅ -``` - -**Scénarios couverts** : -- ✅ Métadonnées valides (cas nominal) -- ✅ Champs manquants -- ✅ Valeurs `None` -- ✅ Chaînes vides `""` -- ✅ Whitespace-only `" "` -- ✅ Types invalides (non-dict) -- ✅ Messages d'erreur avec index et doc_name -- ✅ Scénarios réels (Peirce, Platon, LLM raté) - ---- - -## Scripts et Outils - -### 📊 `verify_vector_index.py` - -**Usage** : -```bash -python verify_vector_index.py -``` - -**Fonction** : Vérifier la configuration des index vectoriels - -**Sortie** : -``` -📦 Chunk -───────────────────────────────────────────────── - ✓ Vectorizer: text2vec-transformers - • Index Type: UNKNOWN (default HNSW probable) - ⚠ RQ (Rotational Quantization): NOT DETECTED - - Interpretation: - ⚠ Unknown index configuration (probably default HNSW) - → Collections créées sans config explicite utilisent HNSW par défaut - -📊 STATISTIQUES: - • Chunk 5,404 objets - • Summary 8,425 objets - • Document 16 objets - • Work 0 objets -``` - ---- - -### 🔄 `migrate_add_work_collection.py` - -**Usage** : -```bash -python migrate_add_work_collection.py -``` - -**Fonction** : Ajouter vectorisation à Work SANS toucher Chunk/Document/Summary - -**⚠️ ATTENTION** : Vos collections actuelles sont PRÉSERVÉES - -**Ce qui se passe** : -1. Supprime SEULEMENT Work (actuellement vide, 0 objets) -2. Recrée Work avec vectorisation activée -3. Chunk (5,404), Summary (8,425), Document (16) : **INTACTS** - -**Sortie** : -``` -MIGRATION: Ajouter vectorisation à Work -[1/5] Vérification des collections existantes... - Collections trouvées: ['Chunk', 'Document', 'Summary', 'Work'] - -[2/5] Suppression de Work (si elle existe)... - ✓ Work supprimée - -[3/5] Création de Work avec vectorisation... - ✓ Work créée (vectorisation activée) - -[4/5] Vérification finale... - ✓ Toutes les collections présentes - -MIGRATION TERMINÉE AVEC SUCCÈS! -✓ Work collection vectorisée -✓ Chunk collection PRÉSERVÉE (aucune donnée perdue) -✓ Document collection PRÉSERVÉE -✓ Summary collection PRÉSERVÉE -``` - ---- - -### 📈 `generate_schema_stats.py` - -**Usage** : -```bash -python generate_schema_stats.py -``` - -**Fonction** : Générer statistiques automatiques pour documentation - -**Sortie** : Markdown prêt à copier-coller dans `WEAVIATE_SCHEMA.md` - -```markdown -| Collection | Objets | Vectorisé | Utilisation | -|------------|--------|-----------|-------------| -| Chunk | 5,404 | ✅ | Principal | -| Summary | 8,425 | ✅ | Hiérarchique | -... - -Insights: -- Granularité : 1.6 summaries par chunk -- Taille moyenne : 338 chunks, 527 summaries/doc -- RAM estimée : ~0.1 GB -``` - -**Avantage** : Pas de stats en dur, toujours à jour - ---- - -### 🔌 `test_weaviate_connection.py` - -**Usage** : -```bash -python test_weaviate_connection.py -``` - -**Fonction** : Tester connexion Weaviate basique - -**Sortie** : -``` -Tentative de connexion à Weaviate... -[OK] Connexion etablie! -[OK] Weaviate est pret: True -[OK] Collections disponibles: ['Chunk', 'Document', 'Summary', 'Work'] -[OK] Test reussi! -``` - ---- - -### 🧪 `tests/test_validation_stricte.py` - -**Usage** : -```bash -pytest tests/test_validation_stricte.py -v -``` - -**Fonction** : 28 tests unitaires pour validation stricte - -**Sortie** : -``` -test_validate_document_metadata_valid PASSED -test_validate_document_metadata_empty_doc_name PASSED -test_validate_chunk_nested_objects_valid PASSED -... -===== 28 passed in 1.90s ===== -``` - ---- - -## Guide d'Utilisation - -### 🚀 Démarrer Weaviate - -```bash -# Lancer les conteneurs Docker -docker compose up -d - -# Vérifier que Weaviate est prêt -curl http://localhost:8080/v1/.well-known/ready -# OU -python test_weaviate_connection.py -``` - ---- - -### 📥 Injecter un Document - -**Option 1 : Via Flask** (interface web) -```bash -# Démarrer Flask -python flask_app.py - -# Aller sur http://localhost:5000/upload -# Upload PDF avec options -``` - -**Option 2 : Via Code Python** -```python -from pathlib import Path -from utils.pdf_pipeline import process_pdf - -result = process_pdf( - Path("input/mon_document.pdf"), - skip_ocr=False, # True pour réutiliser markdown existant - use_llm=True, # Extraction métadonnées/TOC/chunking - llm_provider="ollama", # "ollama" (local) ou "mistral" (API) - ingest_to_weaviate=True, # Injecter dans Weaviate -) - -if result["success"]: - print(f"✓ {result['chunks_count']} chunks ingérés") -else: - print(f"✗ Erreur: {result['error']}") -``` - -**Option 3 : Réinjecter depuis JSON** -```python -from pathlib import Path -import json -from utils.weaviate_ingest import ingest_document - -doc_dir = Path("output/platon_republique") -chunks_file = doc_dir / "platon_republique_chunks.json" - -data = json.loads(chunks_file.read_text(encoding='utf-8')) - -result = ingest_document( - doc_name="platon_republique", - chunks=data["chunks"], - metadata=data["metadata"], - language="fr", - pages=data.get("pages", 0), -) - -print(f"✓ {result['count']} chunks insérés") -``` - ---- - -### 🔍 Rechercher dans Weaviate - -**Via Flask** : -``` -http://localhost:5000/search?q=justice+platon -``` - -**Via Code Python** : -```python -import weaviate - -client = weaviate.connect_to_local() - -try: - chunks = client.collections.get("Chunk") - - # Recherche sémantique - response = chunks.query.near_text( - query="qu'est-ce que la justice?", - limit=10, - ) - - for obj in response.objects: - print(f"Score: {obj.metadata.score:.3f}") - print(f"Texte: {obj.properties['text'][:200]}...") - print(f"Œuvre: {obj.properties['work']['title']}") - print() - -finally: - client.close() -``` - ---- - -### 🗑️ Supprimer un Document - -```python -from utils.weaviate_ingest import delete_document_chunks - -result = delete_document_chunks("platon_republique") - -if result["success"]: - print(f"✓ {result['deleted_chunks']} chunks supprimés") - print(f"✓ {result['deleted_summaries']} summaries supprimés") -else: - print(f"✗ Erreur: {result['error']}") -``` - ---- - -### 📊 Vérifier les Statistiques - -```bash -# Vérifier config index -python verify_vector_index.py - -# Générer stats markdown -python generate_schema_stats.py - -# Compter objets via Python -python -c " -import weaviate -client = weaviate.connect_to_local() -chunks = client.collections.get('Chunk') -result = chunks.aggregate.over_all(total_count=True) -print(f'Chunks: {result.total_count}') -client.close() -" -``` - ---- - -## Migration et Maintenance - -### 🔄 Recréer le Schéma (DESTRUCTIF) - -**⚠️ ATTENTION : Supprime TOUTES les données !** - -```bash -# 1. Sauvegarder (optionnel mais recommandé) -curl http://localhost:8080/v1/schema > backup_schema_$(date +%Y%m%d).json - -# 2. Recréer schéma avec optimisations 2026 -python schema.py - -# Résultat : -# [1/4] Suppression des collections existantes... -# ✓ Collections supprimées -# [2/4] Création des collections... -# → Work (métadonnées œuvre)... -# → Document (métadonnées édition)... -# → Chunk (fragments vectorisés)... -# → Summary (résumés de chapitres)... -# ✓ 4 collections créées -# [3/4] Vérification des collections... -# ✓ Toutes les collections créées -# [4/4] Détail des collections créées -# ... -# ✓ Index Vectoriel (Optimisation 2026): -# - Chunk: Dynamic (flat → HNSW @ 50k) + RQ (~75% moins de RAM) -# - Summary: Dynamic (flat → HNSW @ 10k) + RQ -``` - -**Quand l'utiliser** : -- ✅ Nouvelle base de données (première fois) -- ✅ Test sur instance vide -- ❌ **JAMAIS** en production avec données (perte totale) - ---- - -### 🔄 Ajouter Vectorisation Work (SÉCURISÉ) - -**✅ SAFE : Préserve vos 5,404 chunks** - -```bash -python migrate_add_work_collection.py -``` - -**Ce qui se passe** : -- Supprime SEULEMENT Work (0 objets actuellement) -- Recrée Work avec vectorisation -- Chunk, Summary, Document : **INTACTS** - ---- - -### 📝 Mettre à Jour la Documentation - -```bash -# Générer nouvelles stats -python generate_schema_stats.py > new_stats.md - -# Copier-coller dans WEAVIATE_SCHEMA.md -# Section "Contenu actuel" -``` - ---- - -### 🧪 Tester la Validation - -```bash -# Lancer tous les tests -pytest tests/test_validation_stricte.py -v - -# Test spécifique -pytest tests/test_validation_stricte.py::test_validate_document_metadata_valid -v - -# Avec couverture -pytest tests/test_validation_stricte.py --cov=utils.weaviate_ingest -``` - ---- - -## Troubleshooting - -### ❌ "Weaviate connection failed" - -**Symptômes** : -``` -Erreur connexion Weaviate: Failed to connect to localhost:8080 -``` - -**Solutions** : -```bash -# 1. Vérifier que Docker tourne -docker ps - -# 2. Si pas de conteneurs, lancer -docker compose up -d - -# 3. Vérifier les logs -docker compose logs weaviate - -# 4. Tester la connexion -curl http://localhost:8080/v1/.well-known/ready -# OU -python test_weaviate_connection.py -``` - ---- - -### ❌ "Collection Chunk non trouvée" - -**Symptômes** : -``` -Collection Chunk non trouvée: Collection does not exist -``` - -**Solution** : -```bash -# Créer le schéma -python schema.py -``` - ---- - -### ❌ "Validation error: 'author' is missing" - -**Symptômes** : -``` -Validation error: Invalid metadata for 'my_doc': 'author' is missing or empty. -``` - -**Solutions** : -```python -# 1. Vérifier les métadonnées -metadata = { - "title": "Titre complet", # ✅ Requis - "author": "Nom de l'auteur", # ✅ Requis - "edition": "Optionnel", # ❌ Optionnel -} - -# 2. Si LLM rate l'extraction, fallback -if not metadata.get("author"): - metadata["author"] = "Auteur Inconnu" - -# 3. Vérifier le fichier source -chunks_file = Path("output/my_doc/my_doc_chunks.json") -data = json.loads(chunks_file.read_text()) -print(data["metadata"]) # Vérifier author -``` - ---- - -### ⚠️ "Timeout lors de l'ingestion" - -**Symptômes** : -``` -Batch 1 failed: Connection timeout after 60s -``` - -**Causes** : -- Chunks très longs (>100k chars) -- Batch size trop grand - -**Solutions** : -```python -# 1. Vérifier longueur moyenne -avg_len = sum(len(c["text"]) for c in chunks[:10]) / 10 -print(f"Avg length: {avg_len:,} chars") - -# 2. Le batch dynamique devrait gérer automatiquement -# Si problème persiste, forcer batch plus petit: - -# Dans weaviate_ingest.py (temporairement) -batch_size = 5 # Force très petit batch -``` - ---- - -### 🐌 "Requêtes lentes" - -**Symptômes** : -``` -Recherche prend >5 secondes -``` - -**Diagnostics** : -```bash -# 1. Vérifier nombre d'objets -python verify_vector_index.py - -# 2. Si >50k chunks, vérifier index type -# Devrait être HNSW avec RQ - -# 3. Vérifier RAM Docker -docker stats weaviate -``` - -**Solutions** : -```bash -# 1. Augmenter RAM Docker (docker-compose.yml) -mem_limit: 16g # Au lieu de 8g - -# 2. Si >100k chunks, envisager migration vers Dynamic+RQ -# (nécessite recréation schéma) -``` - ---- - -### 🔴 "RAM trop élevée" - -**Symptômes** : -``` -Weaviate OOM (Out of Memory) -``` - -**Diagnostics** : -```bash -# Vérifier RAM utilisée -docker stats weaviate - -# Vérifier nombre de vecteurs -python verify_vector_index.py -``` - -**Solutions** : - -**Court terme** : -```yaml -# docker-compose.yml - Augmenter limites -mem_limit: 16g -``` - -**Long terme** (si >50k chunks) : -```bash -# Migrer vers Dynamic+RQ (-75% RAM) -# 1. Backup données (export chunks JSON) -# 2. Recréer schéma -python schema.py -# 3. Réinjecter données -``` - ---- - -## 📚 Ressources - -### Fichiers Principaux -- `schema.py` - Définitions schéma avec optimisations 2026 -- `utils/weaviate_ingest.py` - Ingestion avec validation stricte -- `utils/types.py` - TypedDict pour type safety -- `docker-compose.yml` - Configuration conteneurs - -### Scripts Utilitaires -- `verify_vector_index.py` - Vérifier config index -- `migrate_add_work_collection.py` - Migration Work sécurisée -- `generate_schema_stats.py` - Stats automatiques -- `test_weaviate_connection.py` - Test connexion basique - -### Documentation -- `WEAVIATE_GUIDE_COMPLET.md` - **Ce fichier** (guide complet) -- `WEAVIATE_SCHEMA.md` - Schéma détaillé avec stats -- `VECTOR_INDEX_OPTIMIZATION.md` - Dynamic+RQ en détail -- `VALIDATION_STRICTE.md` - Validation métadonnées en détail -- `OPTIMIZATIONS_2026_SUMMARY.md` - Résumé optimisations - -### Tests -- `tests/test_validation_stricte.py` - 28 tests validation - -### Documentation Externe -- [Weaviate Best Practices](https://docs.weaviate.io/weaviate/best-practices) -- [Dynamic Index](https://docs.weaviate.io/weaviate/concepts/vector-index#dynamic) -- [Rotational Quantization](https://docs.weaviate.io/weaviate/concepts/vector-quantization#rq) -- [Nested Objects](https://docs.weaviate.io/weaviate/manage-data/collections) - ---- - -## 🎯 Checklist de Démarrage Rapide - -### Première Utilisation -- [ ] Lancer Docker : `docker compose up -d` -- [ ] Vérifier connexion : `python test_weaviate_connection.py` -- [ ] Créer schéma : `python schema.py` -- [ ] Vérifier config : `python verify_vector_index.py` -- [ ] Tester ingestion : Upload PDF via Flask - -### Maintenance Régulière -- [ ] Vérifier stats : `python generate_schema_stats.py` -- [ ] Vérifier RAM : `docker stats weaviate` -- [ ] Backup schéma : `curl http://localhost:8080/v1/schema > backup.json` -- [ ] Tests validation : `pytest tests/test_validation_stricte.py` - -### Avant Production -- [ ] Tests E2E complets -- [ ] Backup complet des données -- [ ] Monitoring RAM/CPU configuré -- [ ] Documentation à jour -- [ ] Auto-schema désactivé : `AUTOSCHEMA_ENABLED: 'false'` - ---- - -**Version** : 3.1 -**Dernière mise à jour** : 1er janvier 2026 -**Status** : Production-Ready ⭐⭐⭐⭐⭐ diff --git a/generations/library_rag/WEAVIATE_SCHEMA.md b/generations/library_rag/WEAVIATE_SCHEMA.md deleted file mode 100644 index 663f428..0000000 --- a/generations/library_rag/WEAVIATE_SCHEMA.md +++ /dev/null @@ -1,323 +0,0 @@ -# Schéma Weaviate - Library RAG - -## Architecture globale - -Le schéma suit une architecture normalisée avec des objets imbriqués (nested objects) pour un accès efficace aux données. - -``` -Work (métadonnées uniquement) - └── Document (instance d'édition/traduction) - ├── Chunk (fragments de texte vectorisés) - └── Summary (résumés de chapitres vectorisés) -``` - ---- - -## Collections - -### 1. Work (Œuvre) - -**Description** : Représente une œuvre philosophique ou académique (ex: Ménon de Platon) - -**Vectorisation** : ✅ **text2vec-transformers** (depuis migration 2026-01) - -**Champs vectorisés** : -- ✅ `title` (TEXT) - Titre de l'œuvre (permet recherche sémantique "dialogues socratiques" → Ménon) -- ✅ `author` (TEXT) - Auteur (permet recherche "philosophie analytique" → Haugeland) - -**Champs NON vectorisés** : -- `originalTitle` (TEXT) [skip_vec] - Titre original dans la langue source (optionnel) -- `year` (INT) - Année de composition/publication (négatif pour avant J.-C.) -- `language` (TEXT) [skip_vec] - Code ISO de langue originale (ex: 'gr', 'la', 'fr') -- `genre` (TEXT) [skip_vec] - Genre ou type (ex: 'dialogue', 'traité', 'commentaire') - -**Note** : Collection actuellement vide (0 objets) mais prête pour migration. Voir `migrate_add_work_collection.py` pour ajouter la vectorisation sans perdre les 5,404 chunks existants. - ---- - -### 2. Document (Édition) - -**Description** : Instance spécifique d'une œuvre (édition, traduction) - -**Vectorisation** : AUCUNE (métadonnées uniquement) - -**Propriétés** : -- `sourceId` (TEXT) - Identifiant unique (nom de fichier sans extension) -- `edition` (TEXT) - Édition ou traducteur (ex: 'trad. Cousin') -- `language` (TEXT) - Langue de cette édition -- `pages` (INT) - Nombre de pages du PDF/document -- `chunksCount` (INT) - Nombre total de chunks extraits -- `toc` (TEXT) - Table des matières en JSON `[{title, level, page}, ...]` -- `hierarchy` (TEXT) - Structure hiérarchique complète en JSON -- `createdAt` (DATE) - Timestamp d'ingestion - -**Objets imbriqués** : -- `work` (OBJECT) - - `title` (TEXT) - - `author` (TEXT) - ---- - -### 3. Chunk (Fragment de texte) ⭐ **PRINCIPAL** - -**Description** : Fragments de texte optimisés pour la recherche sémantique (200-800 caractères) - -**Vectorisation** : `text2vec-transformers` (BAAI/bge-m3, 1024 dimensions) - -**Champs vectorisés** : -- ✅ `text` (TEXT) - Contenu textuel du chunk -- ✅ `keywords` (TEXT_ARRAY) - Concepts clés extraits - -**Champs NON vectorisés** (filtrage uniquement) : -- `sectionPath` (TEXT) [skip_vec] - Chemin hiérarchique complet -- `sectionLevel` (INT) - Profondeur dans la hiérarchie (1=niveau supérieur) -- `chapterTitle` (TEXT) [skip_vec] - Titre du chapitre parent -- `canonicalReference` (TEXT) [skip_vec] - Référence académique (ex: 'CP 1.628', 'Ménon 80a') -- `unitType` (TEXT) [skip_vec] - Type d'unité logique (main_content, argument, exposition, etc.) -- `orderIndex` (INT) - Position séquentielle dans le document (base 0) -- `language` (TEXT) [skip_vec] - Langue du chunk - -**Objets imbriqués** : -- `document` (OBJECT) - - `sourceId` (TEXT) - - `edition` (TEXT) -- `work` (OBJECT) - - `title` (TEXT) - - `author` (TEXT) - ---- - -### 4. Summary (Résumé de section) - -**Description** : Résumés LLM de chapitres/sections pour recherche de haut niveau - -**Vectorisation** : `text2vec-transformers` (BAAI/bge-m3, 1024 dimensions) - -**Champs vectorisés** : -- ✅ `text` (TEXT) - Résumé généré par LLM -- ✅ `concepts` (TEXT_ARRAY) - Concepts philosophiques clés - -**Champs NON vectorisés** : -- `sectionPath` (TEXT) [skip_vec] - Chemin hiérarchique -- `title` (TEXT) [skip_vec] - Titre de la section -- `level` (INT) - Profondeur (1=chapitre, 2=section, 3=sous-section) -- `chunksCount` (INT) - Nombre de chunks dans cette section - -**Objets imbriqués** : -- `document` (OBJECT) - - `sourceId` (TEXT) - ---- - -## Stratégie de vectorisation - -### Modèle utilisé -- **Nom** : BAAI/bge-m3 -- **Dimensions** : 1024 -- **Contexte** : 8192 tokens -- **Support multilingue** : Grec, Latin, Français, Anglais - -### Migration (Décembre 2024) -- **Ancien modèle** : MiniLM-L6 (384 dimensions, 512 tokens) -- **Nouveau modèle** : BAAI/bge-m3 (1024 dimensions, 8192 tokens) -- **Gains** : - - 2.7x plus riche en représentation sémantique - - Meilleur support multilingue - - Meilleure performance sur textes philosophiques/académiques - -### Champs vectorisés -Seuls ces champs sont vectorisés pour la recherche sémantique : -- `Chunk.text` ✅ -- `Chunk.keywords` ✅ -- `Summary.text` ✅ -- `Summary.concepts` ✅ - -### Champs de filtrage uniquement -Tous les autres champs utilisent `skip_vectorization=True` pour optimiser les performances de filtrage sans gaspiller la capacité vectorielle. - ---- - -## Objets imbriqués (Nested Objects) - -Au lieu d'utiliser des cross-references Weaviate, le schéma utilise des **objets imbriqués** pour : - -1. **Éviter les jointures** - Récupération en une seule requête -2. **Dénormaliser les données** - Performance de lecture optimale -3. **Simplifier les requêtes** - Logique de requête plus simple - -### Exemple de structure Chunk - -```json -{ - "text": "La justice est une vertu...", - "keywords": ["justice", "vertu", "cité"], - "sectionPath": "Livre I > Chapitre 2", - "work": { - "title": "La République", - "author": "Platon" - }, - "document": { - "sourceId": "platon_republique", - "edition": "trad. Cousin" - } -} -``` - -### Trade-off -- ✅ **Avantage** : Requêtes rapides, pas de jointures -- ⚠️ **Inconvénient** : Petite duplication de données (acceptable pour métadonnées) - ---- - -## Contenu actuel (au 01/01/2026) - -**Dernière vérification** : 1er janvier 2026 via `verify_vector_index.py` - -### Statistiques par collection - -| Collection | Objets | Vectorisé | Utilisation | -|------------|--------|-----------|-------------| -| **Chunk** | **5,404** | ✅ Oui | Recherche sémantique principale | -| **Summary** | **8,425** | ✅ Oui | Recherche hiérarchique (chapitres/sections) | -| **Document** | **16** | ❌ Non | Métadonnées d'éditions | -| **Work** | **0** | ✅ Oui* | Métadonnées d'œuvres (vide, prêt pour migration) | - -**Total vecteurs** : 13,829 (5,404 chunks + 8,425 summaries) -**Ratio Summary/Chunk** : 1.56 (plus de summaries que de chunks, bon pour recherche hiérarchique) - -\* *Work est configuré avec vectorisation (depuis migration 2026-01) mais n'a pas encore d'objets* - -### Documents indexés - -Les 16 documents incluent probablement : -- Collected Papers of Charles Sanders Peirce (édition Harvard) -- Platon - Ménon (trad. Cousin) -- Haugeland - Mind Design III -- Claudine Tiercelin - La pensée-signe -- Peirce - La logique de la science -- Peirce - On a New List of Categories -- Arendt - Between Past and Future -- AI: The Very Idea (Haugeland) -- ... et 8 autres documents - -**Note** : Pour obtenir la liste exacte et les statistiques par document : -```bash -python verify_vector_index.py -``` - ---- - -## Configuration Docker - -Le schéma est déployé via `docker-compose.yml` avec : -- **Weaviate** : localhost:8080 (HTTP), localhost:50051 (gRPC) -- **text2vec-transformers** : Module de vectorisation avec BAAI/bge-m3 -- **GPU support** : Optionnel pour accélérer la vectorisation - -### Commandes utiles - -```bash -# Démarrer Weaviate -docker compose up -d - -# Vérifier l'état -curl http://localhost:8080/v1/.well-known/ready - -# Voir les logs -docker compose logs weaviate - -# Recréer le schéma -python schema.py -``` - ---- - -## Optimisations 2026 (Production-Ready) - -### 🚀 **1. Batch Size Dynamique** - -**Implémentation** : `utils/weaviate_ingest.py` (lignes 198-330) - -L'ingestion ajuste automatiquement la taille des lots selon la longueur moyenne des chunks : - -| Taille moyenne chunk | Batch size | Rationale | -|---------------------|------------|-----------| -| < 3k chars | 100 chunks | Courts → vectorisation rapide | -| 3k - 10k chars | 50 chunks | Moyens → standard académique | -| 10k - 50k chars | 25 chunks | Longs → arguments complexes | -| > 50k chars | 10 chunks | Très longs → Peirce CP 8.388 (218k) | - -**Bénéfice** : Évite les timeouts sur textes longs tout en maximisant le throughput sur textes courts. - -```python -# Détection automatique -batch_size = calculate_batch_size(chunks) # 10, 25, 50 ou 100 -``` - -### 🎯 **2. Index Vectoriel Optimisé (Dynamic + RQ)** - -**Implémentation** : `schema.py` (lignes 242-255 pour Chunk, 355-367 pour Summary) - -- **Dynamic Index** : Passe de FLAT à HNSW automatiquement - - Chunk : seuil à 50,000 vecteurs - - Summary : seuil à 10,000 vecteurs -- **Rotational Quantization (RQ)** : Réduit la RAM de ~75% -- **Distance Metric** : COSINE (compatible BGE-M3) - -**Impact actuel** : -- Collections < seuil → Index FLAT (rapide, faible RAM) -- **Économie RAM projetée à 100k chunks** : 40 GB → 10 GB (-75%) -- **Coût infrastructure annuel** : Économie de ~840€ - -Voir `VECTOR_INDEX_OPTIMIZATION.md` pour détails. - -### ✅ **3. Validation Stricte des Métadonnées** - -**Implémentation** : `utils/weaviate_ingest.py` (lignes 272-421) - -Validation en 2 étapes avant ingestion : -1. **Métadonnées document** : `validate_document_metadata()` - - Vérifie `doc_name`, `title`, `author`, `language` non-vides - - Détecte `None`, `""`, whitespace-only -2. **Nested objects chunks** : `validate_chunk_nested_objects()` - - Vérifie `work.title`, `work.author`, `document.sourceId` non-vides - - Validation chunk par chunk avec index pour debugging - -**Impact** : -- Corruption silencieuse : **5-10% → 0%** -- Temps debugging : **~2h → ~5min** par erreur -- **28 tests unitaires** : `tests/test_validation_stricte.py` - -Voir `VALIDATION_STRICTE.md` pour détails. - ---- - -## Notes d'implémentation - -1. **Timeout augmenté** : Les chunks très longs (ex: Peirce CP 3.403, CP 8.388: 218k chars) nécessitent 600s (10 min) pour la vectorisation -2. **Batch insertion dynamique** : L'ingestion utilise `insert_many()` avec batch size adaptatif (10-100 selon longueur) -3. **Type safety** : Tous les types sont définis dans `utils/types.py` avec TypedDict -4. **mypy strict** : Le code passe la vérification stricte mypy -5. **Validation stricte** : Métadonnées et nested objects validés avant insertion (0% corruption) - ---- - -## Voir aussi - -### Fichiers principaux -- `schema.py` - Définitions et création du schéma -- `utils/weaviate_ingest.py` - Fonctions d'ingestion avec validation stricte -- `utils/types.py` - TypedDict correspondant au schéma -- `docker-compose.yml` - Configuration des conteneurs - -### Scripts utiles -- `verify_vector_index.py` - Vérifier la configuration des index vectoriels -- `migrate_add_work_collection.py` - Ajouter Work vectorisé (migration sûre) -- `test_weaviate_connection.py` - Tester la connexion Weaviate - -### Documentation des optimisations -- `VECTOR_INDEX_OPTIMIZATION.md` - Index Dynamic + RQ (économie RAM 75%) -- `VALIDATION_STRICTE.md` - Validation métadonnées (0% corruption) - -### Tests -- `tests/test_validation_stricte.py` - 28 tests unitaires pour validation