SPB Git

spb/ora-ka Public

Ora-Ka — cinq agrégateurs Ka, une barre de recherche hybride (exact + sémantique)

Python 80% TypeScript 12.9% CSS 6.8%
7.7 KB

# Ora·Ka — Cinq agrégateurs. Une barre.

Auteur : Simon-Pierre Boucher — contact@spboucher.ai

Live Python FastAPI React SQLite Embeddings Inscriptions Node

Ora-Ka unifie les cinq agrégateurs automatisés de la famille Ka en une seule plateforme : une barre de recherche, un moteur hybride exact + sémantique, et les cinq applications d'origine reproduites fidèlement sous leur propre préfixe — sans modifier une ligne de leurs backends.

Landing Ora-Ka

# Métriques (prod, 2026-08-13)

Univers Préfixe Contenu Inscriptions Sources Accent
Immo·Ka /immo/ Propriétés à vendre (QC) 48 487 33 #e23744
Lou·Ka /lou/ Logements à louer 16 563 255 #d9f26b
Fabri·Ka /fabri/ Produits québécois 291 132 3 224 boutiques #c4532e
Auto·Ka /auto/ Véhicules d'occasion 17 997 138 #ff5a2a
Food·Ka /food/ Épicerie comparée 26 319 30 #1f7a4d
Total 400 498
Dimension Valeur
Index sémantique 362 508 vecteurs · text-embedding-3-small · 256 d float16 · ≈ 164 Mo npz
Bases SQLite ≈ 1,4 Go (5 bases + 2 bases quartier)
Moteur oraka/ 2 090 lignes Python (8 modules)
Backends embarqués 339 fichiers Python · 65 852 lignes (inchangés)
Frontends 5 SPA React 18 + Vite, rebuildées sous préfixe
Cadence d'ingestion immo 180 min · lou 60 · auto 120 · fabri/food 360
Latence recherche brute-force numpy sur npz en RAM, hot-reload sur mtime

# La recherche hybride

Une seule barre (/api/search) interroge les cinq univers avec un score unifié par candidat — pas de cascade exact-puis-sémantique :

  • Candidats : correspondance exacte par jetons AND (LIKE/FTS5) ∪ top-k sémantique par univers.
  • Score : cosinus (lookup par uid, même pour les hits exacts) + 0,22·couverture-jetons + 0,10·phrase + 0,05·exact + affinité d'univers data-driven.
  • Coupure dynamique (best − 0,22) + garantie de diversité : chaque univers ≥ best − 0,30 est épinglé dans le top 12.
  • Affichage : pertinence relative en % (score/best), jauge, surlignage des jetons (préfixe en début de mot, stopwords exclus).
« condo 2 chambres montréal » « fromage du québec »
Recherche condo Recherche fromage

L'index (oraka/semantic.py) vit dans data/semantic/<univers>.npz ; le serveur le recharge à chaud sur changement de mtime — un simple rsync des npz suffit, sans redémarrage. Rebuild incrémental : run.py index (~10 min, ~0,25 $ ; à relancer après les grosses syncs sinon l'index dérive).

# Les cinq univers

Chaque app est montée telle quelle sous son préfixe — mêmes fiches, mêmes cartes, mêmes filtres que l'app d'origine :

Immo·Ka /immo/ Lou·Ka /lou/
Fabri·Ka /fabri/ Auto·Ka /auto/
Food·Ka /food/ Splash Groupe KA splash au boot

Captures régénérables : node scripts/screenshots.mjs (playwright requis).

# Architecture

text
ora-ka/
  run.py               # serve [port] | sync [app ...] | watch | index
  oraka/
    web.py             # app maîtresse : landing + /api/overview + /healthz + 5 mounts
    landing.py         # landing « famille Ka × verre » (splash, ticker, cartes)
    search.py          # /api/search — score unifié exact ∪ sémantique
    semantic.py        # index npz par univers, hot-reload, OpenAI embeddings
    auth.py            # SSO « KA ID » via le hub groupe-ka.com
    hubfav.py          # favoris synchronisés au hub
    legal.py           # conditions + confidentialité
  apps/
    immo/  { immoka/,  data/{immoka.db, quartier.db, sources.json…}, frontend/ }
    lou/   { louka/,   data/{louka.db,  quartier.db, sources.json},  frontend/ }
    fabri/ { fabrika/, data/{fabrika.db, stores.json},               frontend/ }
    auto/  { autoka/,  data/{autoka.db, sources.json},               frontend/ }
    food/  { foodka/,  data/{foodka.db, sources.json},               frontend/ }
  data/semantic/       # <univers>.npz + .meta.json (non versionnés)

Chaque app conserve son arborescence d'origine (<package>/, data/, frontend/dist/ frères) — ses chemins internes calculés via __file__ restent valides, aucun code backend n'a été modifié. Les frontends React/Vite ont uniquement été rebuildés avec base: '/<préfixe>/' + basename react-router + API_BASE = import.meta.env.BASE_URL (+ cas spéciaux : worker MapLibre de lou, logos de food).

Design de la landing : hybride « famille Ka × verre » — papier grainé #f5f3ee, encre #141814, lime #d9f26b, JetBrains Mono uppercase, bordures encre + ombres décalées dures, badge « Ka » incliné −2°, ticker, splash Groupe KA (1,6 s, sessionStorage).

# Lancement

bash
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# builds frontend (une fois) : cd apps/<x>/frontend && npm install && npm run build
.venv/bin/python run.py serve 8200   # plateforme complète
.venv/bin/python run.py sync         # ingestion des 5 apps (ou : sync immo lou …)
.venv/bin/python run.py watch        # boucle permanente, cadence native par app
.venv/bin/python run.py index        # (re)construit l'index sémantique

.env (non versionné) : OPENAI_API_KEY (index sémantique), FIRECRAWL_API_KEY, SCRAPFLY_KEY, SCRAPFLY_API_KEY (ingestion seulement, jamais le serveur) ; KA_SSO_SECRET, KA_HUB_URL (SSO KA ID).

# Production (MacLustr — nœud M4M64a, port 8200)

bash
pm2 start .venv/bin/python --name ora-ka-web  --interpreter none --cwd ~/apps/ora-ka -- run.py serve 8200
pm2 start .venv/bin/python --name ora-ka-sync --interpreter none --cwd ~/apps/ora-ka -- run.py watch
pm2 start /opt/homebrew/bin/ngrok --name ora-ka-ngrok --interpreter none -- http --url=www.ora-ka.com 8200

Healthcheck : GET /healthz — vérifie la présence des 5 bases SQLite. Stats agrégées en direct : GET /api/overview.


© 2026 Groupe KA — Ora-Ka agrège des données publiques ; chaque inscription reste la propriété de sa source.