spb/trouve-ka Public
Trouve-KA — moteur de recherche web indépendant, Québec-first. Crawler distribué, index OpenSearch, ranking bilingue, galerie d'images. En prod : www.trouve-ka.com
Python 76.8%
TypeScript 15.7%
SQL 3.9%
Shell 1.4%
CSS 1.3%
Dockerfile 0.7%
1# Trouve-KA — Décisions d'architecture23Author: Simon-Pierre Boucher — Contact: contact@spboucher.ai45## D1 — Backend de recherche : OpenSearch 2.x67**Candidats évalués :** OpenSearch, Elasticsearch, Typesense, Meilisearch, Vespa, Tantivy, Quickwit (CLAUDE.md §3).89**Choix : OpenSearch 2.17**, parce que :10- **BM25 réel et paramétrable** par champ (multi_match, boosts), critère n°1 du MVP.11- **Indexation incrémentale** near-real-time : `refresh_interval: 1s` → une page indexée est cherchable en ~1 s (principe cardinal §0.3).12- **`function_score`** : la fonction de ranking Québec-first custom (§9) s'exprime nativement, poids ajustables sans réindexation.13- **Analyzers FR/EN** + `synonym_graph` search-time (bilinguisme sans traduction à l'ingestion).14- **Highlighting** natif pour les snippets, **facettes** pour les filtres, **k-NN natif** pour l'hybride sémantique futur (§17.7) sans changer de moteur.15- Licence Apache 2.0, image ARM64, tourne en 2 Go de heap sur un seul node m2m32.1617**Rejetés :** Meilisearch/Typesense (ranking custom trop rigide pour `Score(d,q)` pondéré; c'était le choix « facile » interdit par la spec), Tantivy (librairie Rust, il faudrait construire le serveur), Quickwit (orienté logs append-only, pas de mise à jour de documents), Vespa (excellent mais complexité opérationnelle démesurée pour un node unique), Elasticsearch (équivalent fonctionnel d'OpenSearch, licence moins permissive).1819## D2 — Backend Python unique (FastAPI + pipeline)2021Crawler, parser, classifier, indexer, ranking, scheduler, enrichment et API partagent un seul langage (Python 3.12) et un seul packaging (`pyproject.toml` racine, namespace `trouveka.*` mappé sur `packages/` et `services/`). Réduit la complexité (§3 : « le crawler peut rester Python même si l'API est TS » — ici tout le backend est Python, seul le web est TS).2223## D3 — Frontier dans Postgres, coordination dans Redis2425- **Frontier = Postgres** : état relationnel (priorités, retries, scheduling), claims multi-workers via `FOR UPDATE SKIP LOCKED` — N workers sans coordinateur.26- **Redis** : politesse par hôte (`SET NX PX`, un fetch par hôte par fenêtre, tous workers confondus), drapeau pause, **Redis Streams** pour l'enrichissement asynchrone (consumer groups, ack explicite).27- Pas de Celery/RQ : la file d'enrichissement est un stream nu, observable (`XLEN`), sans dépendance lourde.2829## D4 — Pipeline inline (fetch→parse→score→index) dans le crawler-worker3031Le chemin rapide est exécuté inline par le worker : c'est la garantie la plus simple du « cherchable en secondes ». La scalabilité passe par le nombre de workers (`--scale crawler-worker=N`), pas par une séparation prématurée fetch/parse/index (§13 : pas de complexité distribuée prématurée). Les étapes 2-3 (§4) passent par le stream d'enrichissement et ne bloquent jamais.3233## D5 — apps/admin fusionné dans apps/web (route /admin)3435Un seul runtime Next.js sur m2m32 au lieu de deux (~150 Mo RSS économisés), même design system, même proxy API. La séparation reste possible plus tard (le dashboard est un groupe de composants isolés sous `components/admin/`). Justification prévue par §2 (« Claude peut améliorer cette structure sur justification technique solide »).3637## D6 — Graphe de liens au niveau domaine d'abord3839`domain_links` agrégé (from, to, count) plutôt que page→page : suffisant pour l'autorité de domaine et la découverte au MVP, volume contrôlé. Le graphe page→page viendra quand le ranking l'exigera (§8).4041## D7 — Réseau : le web proxifie /api4243Next.js rewrite `/api/*` → API interne. Derrière ngrok (www.trouve-ka.com), un seul port exposé (3000), pas d'URL absolues côté client, pas de CORS en prod.44