# CLAUDE.md — Trouve-KA **Projet :** Trouve-KA — *Cherche le Québec.* **Mission :** Moteur de recherche web indépendant, Québec-first, avec son propre crawler, index, ranking, API et application web publique. Trouve-KA n'est **pas** un métamoteur. Aucune dépendance à Google, Bing ou Brave pour les résultats primaires. L'objectif : un index structuré et continuellement mis à jour du web québécois. ``` Crawler → Frontier → Fetcher → Parser → Classification Québec → Déduplication → Indexer → Index → Ranking → API → Web App ``` Le crawler fait partie du produit. Tout vit dans un seul monorepo. --- ## 0. Règles non négociables ### 0.1 Header obligatoire dans chaque fichier de code **Chaque fichier source** (TS, JS, Python, SQL, config exécutable, scripts) doit commencer par un header d'auteur : ```ts /** * Trouve-KA — * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ ``` ```python # Trouve-KA — # Author: Simon-Pierre Boucher # Contact: contact@spboucher.ai ``` Adapter la syntaxe de commentaire au langage. Aucune exception. Un lint/check CI doit vérifier la présence du header. ### 0.2 Cible de déploiement - **Node de déploiement : `m2m32`** (32 Go RAM). Tout le stack (Postgres, Redis, backend de recherche, workers, API, web) doit tourner confortablement sur cette machine. - **Exposition publique via ngrok : `www.trouve-ka.com`.** Configurer ngrok avec le domaine réservé; l'app web et l'API doivent fonctionner correctement derrière le tunnel (URLs absolues, cookies, CORS, headers `X-Forwarded-*`). - Docker Compose est l'outil de déploiement. Pas de Kubernetes. - Fournir un script/target `deploy:m2m32` et documenter la procédure complète (compose up + tunnel ngrok) dans le README. - Dimensionner les défauts (concurrence, tailles de heap du moteur de recherche, connexions Postgres) pour 32 Go, avec configuration par variables d'environnement. ### 0.3 Principe cardinal > **Crawl en continu. Indexe immédiatement. Recherche immédiatement. Enrichis en asynchrone.** Le moteur doit être utilisable **dès que le crawl démarre**. Jamais de cycle « crawler tout → construire l'index → lancer la recherche ». Chaque page traitée avec succès devient cherchable en secondes. ``` 12:00:00 démarrage crawler 12:00:04 première page fetchée 12:00:06 score Québec calculé 12:00:07 document indexé 12:00:08 cherchable par l'utilisateur ``` Aucune interdiction plus importante que celle-ci : **ne jamais bloquer l'indexation sur l'enrichissement** (embeddings, entités, scoring avancé = asynchrone, mise à jour du document après coup). --- ## 1. Philosophie produit Trouve-KA est un vrai moteur, pas : un annuaire, une liste curée, un wrapper ChatGPT, un frontend Google CSE, un dataset statique. Il doit de façon autonome : découvrir des sites → les crawler → comprendre le contenu → juger la pertinence québécoise → indexer → ranker → exposer via recherche → découvrir davantage → rafraîchir en continu. **Interdictions absolues (§ fake) :** pas de données de crawl factices, pas de compteurs simulés présentés comme réels, pas de résultats hard-codés, pas d'API de recherche placeholder. Les fixtures sont réservées aux tests. Le dev tourne sur le vrai crawler et le vrai index local. --- ## 2. Architecture du dépôt Monorepo : ``` trouve-ka/ ├── apps/ │ ├── web/ # moteur public │ ├── api/ # search + APIs internes │ └── admin/ # dashboard crawl/index ├── services/ │ ├── crawler/ frontier/ parser/ classifier/ │ ├── indexer/ ranking/ scheduler/ enrichment/ ├── packages/ │ ├── database/ shared/ config/ logging/ │ ├── queue/ types/ search-core/ ├── infrastructure/ │ ├── docker/ migrations/ monitoring/ deployment/ # inclut config ngrok + m2m32 ├── scripts/ │ ├── bootstrap-seeds/ start-crawler/ rebuild-index/ health-check/ ├── docs/ ├── CLAUDE.md README.md docker-compose.yml ``` Claude peut améliorer cette structure sur justification technique solide. --- ## 3. Stack technologique | Couche | Choix | |---|---| | Frontend | Next.js, TypeScript, React, Tailwind, shadcn/ui | | API | FastAPI (Python) ou backend TypeScript si ça réduit la complexité; le crawler peut rester Python même si l'API est TS | | BD relationnelle | PostgreSQL (domaines, URLs, état de crawl, métadonnées, entités, scheduler, bookkeeping) | | Queue | Redis + vraie file de tâches/streams (évaluer avant de choisir) | | Recherche | À sélectionner parmi : OpenSearch, Elasticsearch, Typesense, Meilisearch, Vespa, Tantivy, Quickwit | Critères de sélection du backend de recherche : qualité BM25, indexation incrémentale, facettes, tolérance aux typos, ranking custom, recherche hybride, performance, scaling horizontal, **complexité opérationnelle sur un seul node m2m32**. Pour le MVP : qualité de recherche + indexation incrémentale + opérations simples. Ne pas choisir le plus facile par défaut. --- ## 4. Qualité de recherche progressive Chaque document s'enrichit par étapes; **la disponibilité en recherche ne dépend jamais des étapes suivantes.** - **Étape 1 (immédiat) :** URL, titre, description, corps, headings, domaine, langue, timestamp, score Québec → index BM25. - **Étape 2 (async) :** embedding, classification thématique, organisations, signaux de localisation, entité canonique. - **Étape 3 (async) :** autorité de domaine, score de graphe de liens / PageRank-like, fraîcheur, qualité, spam, intention commerciale, pertinence locale. --- ## 5. Crawler Un vrai crawler : frontier d'URLs, scheduling par domaine, robots.txt, canonicalisation, redirections, retries, cache HTTP, compression, gestion des content-types, budgets de crawl, concurrence par hôte, rate limiting, files de priorité, prévention des doublons, historique, hash de contenu, détection de changements. ### 5.1 Identité User-agent identifiable : `Mozilla-compatible / TrouveKABot`. Page publique `/trouveka-bot` : quoi, pourquoi, UA, contact (**contact@spboucher.ai**), comment bloquer, respect de robots.txt. **Pas de stealth, pas de rotation de proxys par défaut.** IPs stables et comportement poli. ### 5.2 robots.txt et politesse Parsing conforme aux standards, cache des règles, respect de `noindex` / `nofollow` / `canonical` / `X-Robots-Tag`. Politesse par origine : 1–2 requêtes concurrentes max par hôte, budgets indépendants, politiques configurables. Un gros débit global ne doit jamais agresser un site individuel. ### 5.3 Frontier Chaque URL : `url, domain, priority, depth, source_url, discovered_at, last_crawled_at, next_crawl_at, status`. Priorités évolutives selon : pertinence Québec, autorité, source de découverte, profondeur, importance du domaine, fraîcheur, historique de changement, succès, duplication. Fonction de priorité conceptuelle : `P = w_q·Q + w_a·A + w_f·F + w_l·L + w_n·N − w_d·D − w_s·S` (Québec, Autorité, Fraîcheur, Liens, Nouveauté, Doublon, Spam). Poids calibrés par mesures, jamais figés. ### 5.4 Canonicalisation d'URL Extrême prudence. Normaliser : fragments, ports par défaut, slashs, paramètres de tracking/UTM, ordre des paramètres quand approprié, http/https, www, trailing slash, tags canonical. **Ne jamais fusionner deux ressources distinctes par accident.** ### 5.5 Recrawl adaptatif Homepage de nouvelles → minutes; article → heures puis décroissant; page gouvernementale → quotidien; site statique → mensuel; archive inchangée → rarement. Fréquence de changement mesurée empiriquement : page inchangée → intervalle ↑; page volatile → intervalle ↓. ### 5.6 Détection de changement et doublons Stocker `content_hash`, etag, last-modified, `last_changed_at`. Réindexer seulement si changement. Doublons : exacts (hash) d'abord, puis SimHash/MinHash/shingling si justifié. Gérer miroirs, versions imprimables, doublons de paramètres, syndication. Conserver la provenance. ### 5.7 Sécurité du crawl (SSRF) Bloquer : localhost, 127.0.0.0/8, plages privées IPv4/IPv6, endpoints de métadonnées cloud, `file://`, schémas dangereux. Revalider DNS/IP. Chaque réponse a des limites : taille max, redirections max, timeout, temps de parse max, liens extraits max, profondeur max. ### 5.8 Pièges de crawl Détecter : calendriers infinis, session IDs, explosions de navigation à facettes, pagination infinie, paramètres aléatoires, boucles. Limites par pattern/domaine. ### 5.9 Erreurs Codes structurés : DNS, timeout, TLS, 4xx, 5xx, robots refusé, parse échoué, contenu non supporté, trop gros, doublon, spam, non pertinent Québec. L'échec est normal; il est traqué. ### 5.10 Rendu navigateur **Jamais** de Chromium par page. Défaut : fetch HTTP. Le navigateur est un fallback spécialisé par domaine — essentiel pour l'échelle et pour tenir sur m2m32. --- ## 6. Extraction de contenu Extraire : titre, meta description, corps principal, headings, données structurées (JSON-LD, schema.org, microdata, OpenGraph), URL canonique, langue, liens + anchors, dates de publication/modification, auteur, indices d'organisation et d'adresse. Retirer : navigation, menus, bannières cookies, footers répétitifs, scripts, styles, pub. Types au départ : HTML, texte, PDF. Architecture extensible (DOCX/XLSX/PPTX, RSS/Atom) sans que l'extraction coûteuse bloque le crawl HTML. Traiter chaque page crawlée comme **non fiable** : sanitizer HTML, URLs, métadonnées; ne jamais faire confiance aux MIME types distants. --- ## 7. Détection Québec L'innovation clé. Un `.ca` seul ne suffit pas. Calculer `quebec_score ∈ [0,1]` avec **deux scores distincts** : - `domain_quebec_score` - `page_quebec_score` (un article du NYT sur Montréal peut être pertinent sans que le domaine le soit) Signaux : toponymes (Québec, Montréal, Gatineau, Sherbrooke, Trois-Rivières, Saguenay, Laval, Longueuil…), adresses postales QC, province dans les adresses structurées, indicatifs téléphoniques (signal faible), organisations québécoises connues (entreprises, municipalités, universités, médias, gouvernement), langue (le français augmente la probabilité sans la prouver), graphe de domaines (un domaine massivement lié par des domaines québécois gagne du signal), métadonnées structurées, pages contact/footer. --- ## 8. Découverte **Qualité des seeds > quantité.** Démarrer avec des nœuds fortement connectés : gouvernement du Québec, municipalités, universités, cégeps, grands médias, annuaires d'affaires, associations professionnelles, chambres de commerce, tourisme régional, grandes entreprises. Boucle : crawl → extraction des liens sortants → scoring Québec des domaines cibles → ajout au frontier → répéter. Détecter automatiquement sitemaps (`/sitemap.xml`, entrées robots.txt, index de sitemaps) et flux RSS/Atom, sans confiance aveugle. Maintenir une base de domaines : `domain, first_seen, last_crawled, page_count, quebec_score, language_distribution, robots_status, authority_score, inlinks, outlinks, content_change_rate…` Construire le graphe de liens dès le crawl (Page→Page, Domain→Domain, Org→Domain…) — d'abord pour le ranking et la découverte, plus tard pour l'écosystème KA. --- ## 9. Index et ranking Document indexé (cible) : `id, url, canonical_url, domain, title, description, body, headings, language, page_quebec_score, domain_quebec_score, locations, organizations, people, categories, published_at, crawled_at, authority_score, freshness_score, quality_score, spam_score, embedding`. **BM25 d'abord.** Puis fonction de ranking dédiée : `Score(d,q) = w_b·BM25 + w_s·Sémantique + w_q·Québec + w_a·Autorité + w_f·Fraîcheur + w_l·Localité + w_u·Qualité − w_p·Spam` Le ranking est un package/service indépendant, évolutif, aux poids non figés. **Québec-first :** pour `meilleur programme thermopompe`, Hydro-Québec, un programme gouvernemental ou une entreprise CVC québécoise doivent battre un article international générique, à pertinence égale. **Bilingue dès le départ :** comprendre `thermopompe` ↔ `heat pump`. Pas de traduction à l'ingestion; plus tard : embeddings multilingues, expansion de requête, dictionnaires de synonymes. **Pipeline de requête :** normalisation → détection de langue → correction → intention → extraction d'entités/lieux → retrieval lexical → retrieval sémantique → fusion → reranking. **Chaque composant avancé est optionnel; BM25 fonctionne même si tout le reste tombe.** **Localisation :** `plombier Gatineau`, `subvention Sherbrooke` boostent les documents géographiquement pertinents sur preuve géographique explicite, pas juste du keyword matching. --- ## 10. Interface **Homepage = la boîte de recherche.** Rien d'autre d'important. ``` Trouve-KA [ Rechercher... ] Chercher Cherche le Québec. ``` Résultats : favicon, titre, URL/breadcrumb, snippet, badges optionnels (Québec, Gouvernement, Entreprise…). Sensation : rapide, propre, premium, minimal, fiable. Filtres MVP sobres : Tout / Actualités / Gouvernement / Français / English / Québec seulement. **Compteur d'index visible** (« 18 432 pages indexées » le jour 1, c'est parfait — la croissance fait partie du produit) + page `/status` : pages/domaines indexés, débit horaire, état du crawler et de l'index. **Dashboard admin** : longueur des files, débits fetch/parse/index, distribution HTTP, domaines actifs, blocages robots, retries, latences, stockage, taux de doublons, taux d'acceptation Québec, flux live du crawl. Mobile impeccable, accessibilité complète (navigation clavier, HTML sémantique, contrastes, focus states). Design : minimal, moderne, québécois sans clichés — **pas** de fleurs de lys partout, pas de hero marketing, pas de gradients IA. --- ## 11. APIs **Recherche :** `GET /api/search?q=&page=&limit=&language=&location=&category=&freshness=` → résultats structurés (`title, url, display_url, snippet, domain, score`, `took_ms`, `total`). Ne pas exposer le scoring interne brut en prod. **Contrôle du crawler (protégé, jamais public sans auth) :** pause/reprise, ajout de seeds, recrawl URL/domaine, inspection du frontier, priorités, blocage de domaine. **Soumission d'URL** (feature simple) : « Soumettre un site québécois » → frontier. Soumission ≠ inclusion; le crawler valide. --- ## 12. Coûts, LLM et embeddings Le crawl doit être **économique** : bande passante, compute, stockage — pas d'API LLM ni d'API de scraping commerciale par page. Le cœur (fetch, parse, canonicalisation, langue, hash, doublons, BM25) est déterministe. Les LLM enrichissent des pages **sélectionnées** en asynchrone (catégorisation, entités complexes, classification géographique ambiguë). Embeddings : toujours en arrière-plan, jamais bloquants. **Stockage brut :** métadonnées → Postgres; contenu cherchable → index; HTML brut compressé optionnel → object storage. Pas de blobs HTML géants dans Postgres. Analytics de recherche : métriques agrégées et respectueuses de la vie privée (requête, latence, position cliquée, zéro-résultat, langue). **Les requêtes zéro-résultat sont de l'or** : les stocker pour piloter le crawl par la demande. Pas de profils utilisateurs invasifs. --- ## 13. Observabilité et dégradation Logs structurés avec `crawl_id, url_id, domain_id, worker_id, job_id`. Métriques compatibles Prometheus/Grafana/OpenTelemetry (choix pragmatique). **Dégradation gracieuse obligatoire :** embeddings en panne → lexical continue; enrichissement en retard → nouvelles pages cherchables quand même; un worker crash → frontier continue; frontend redémarre → crawler continue. Workers scalables indépendamment (`crawler-worker × N`, `parser-worker × N`, `indexer-worker × N`) — mais **pas de complexité distribuée prématurée** : Postgres + Redis + backend de recherche + quelques workers suffisent au départ. --- ## 14. Provenance, droit d'auteur, avenir - Toujours préserver : URL originale, canonique, timestamp de crawl, domaine source. Trouve-KA renvoie vers les éditeurs originaux — c'est un index, pas un remplacement de contenu. - Snippets raisonnables, attribution, liens sortants. Stockage brut, indexation, cache et affichage conçus séparément. - Futur (non-MVP mais à ne pas bloquer architecturalement) : webmaster tools, verticales (news, entreprises, gouvernement, immobilier…), historique de versions des pages, réponses IA basées **uniquement** sur l'index avec citations, écosystème KA (Person-KA, Service-KA, Entreprise-KA…) alimenté par l'extraction d'entités asynchrone. Pas de collecte de données personnelles invasives. --- ## 15. Configuration et DX Variables d'env validées : `DATABASE_URL, REDIS_URL, SEARCH_URL, CRAWLER_USER_AGENT, CRAWLER_CONTACT_URL, MAX_GLOBAL_CONCURRENCY, DEFAULT_HOST_DELAY, MAX_RESPONSE_BYTES, NGROK_DOMAIN=www.trouve-ka.com, PUBLIC_URL=https://www.trouve-ka.com`. Fournir `.env.example` sans secrets. Expérience développeur cible : ``` git clone … && cd trouve-ka cp .env.example .env docker compose up -d pnpm install && pnpm dev pnpm crawl:seed # → des résultats apparaissent en quelques instants sur localhost:3000 ``` Déploiement m2m32 : même stack via compose + tunnel ngrok vers www.trouve-ka.com, scripté et documenté. --- ## 16. Tests et évaluation Tests unitaires sur la logique critique : normalisation d'URL, robots, scoring Québec, canonical, doublons, scheduling, prévention SSRF, extraction HTML, API de recherche. Tests d'intégration sur un **web de fixtures local** (plombier québécois, université montréalaise, entreprise ontarienne, doublon, redirection, chemin bloqué par robots, article FR, article EN québécois) : fixture → crawler → parser → index → résultat de recherche. Dataset d'évaluation du ranking (« université québec », « plombier gatineau », « subvention thermopompe »…) avec domaines attendus. Les expériences de ranking doivent être mesurables. Scripts de benchmark : fetches/s, parses/s, docs indexés/s, latence de recherche, lag de queue, RAM/CPU/disque — l'optimisation est fondée sur des preuves, pas des intuitions. **Cibles :** recherche p50 < 100 ms, p95 < 300 ms à l'échelle MVP; page HTML normale cherchable en secondes; UI quasi instantanée. --- ## 17. Ordre d'implémentation 1. **Fondation** — monorepo, BD, queue, backend de recherche, web, API, crawler basique. 2. **Boucle complète** — seed → fetch → parse → index → search. **Ne pas continuer tant que ça ne marche pas de bout en bout.** 3. **Découverte continue** — liens sortants, frontier, domaines, scheduler, robots. 4. **Intelligence Québec** — scores, langue, localisation, scoring de domaines. 5. **Qualité de recherche** — BM25 custom, snippets, autorité, fraîcheur, doublons, compréhension de requête. 6. **Échelle** — plus de workers, meilleures queues, recrawl, monitoring. 7. **Couche sémantique** — embeddings, hybride, reranking, entités. 8. **Écosystème KA.** --- ## 18. Milestone critique (non négociable) La première implémentation n'est réussie que si Claude démontre : 1. démarrage du stack; 2. seed de sites québécois; 3. le crawler découvre des URLs; 4. télécharge des pages; 5. le parser extrait du texte utile; 6. les pages reçoivent un score Québec; 7. insertion **immédiate** dans l'index; 8. l'app web publique cherche ces pages; 9. les nouvelles pages deviennent cherchables **sans rien redémarrer**; 10. le frontier continue de découvrir des sites. **Succès Jour 1 :** même avec 10 000 pages, l'architecture se comporte exactement comme elle le fera avec 10 000 000+. Le crawler étend sa connaissance du web québécois pendant que le moteur est déjà opérationnel sur tout ce qui a été découvert. --- ## 19. Méthode de travail de Claude Avant tout code substantiel : analyser cette spec → inspecter le dépôt existant → identifier le réutilisable → rechercher les décisions techniques incertaines → planifier → choisir sur preuves → documenter les décisions d'architecture → **implémenter verticalement** (du logiciel qui marche, pas de l'architecture spéculative). **Autonomie :** décisions raisonnables sans demander en permanence. Plusieurs options valables → analyser, choisir la meilleure, documenter, avancer. Ne bloquer que si c'est réellement bloquant. **Standard de qualité :** code typé, documenté, modulaire, observable, testable, sécurisé, efficace, orienté production. Pas de fichiers géants ni de couplage profond. Schémas migrables (concepts propres : `domains, urls, crawl_attempts, documents, document_versions, links, frontier_items, robots_rules, entities, index_jobs`). Et bien sûr : **header auteur dans chaque fichier** (§0.1). **Documentation :** README excellent (quoi, architecture, quick start, crawl, indexation incrémentale, recherche, layout, env, dev, tests, déploiement m2m32 + ngrok) et docs d'architecture avec diagrammes Mermaid. --- ## 20. Vision L'objectif n'est pas de battre Google sur « Taylor Swift » ou « iPhone ». C'est de devenir extraordinairement bon sur : entreprises, institutions, gouvernement, municipalités, services, personnes, événements, produits, immobilier, documents, nouvelles et savoir local **du Québec**. Chaque page crawlée doit accroître la connaissance propre de Trouve-KA — jamais générer une requête vers l'API de quelqu'un d'autre. Chaque crawl améliore la couverture, le graphe de liens, la classification Québec, l'autorité, la fraîcheur, les priorités futures. **La valeur du dataset se compose dans le temps.** > **Crawl en continu. Indexe immédiatement. Recherche immédiatement. Améliore en asynchrone.** > Le web ne « finit » jamais. Trouve-KA non plus.