Ora·Ka — Cinq agrégateurs. Une barre.
Auteur : Simon-Pierre Boucher — contact@spboucher.ai
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.

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 » |
|---|---|
![]() |
![]() |
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/ |
/lou/ |
/fabri/ |
/auto/ |
/food/ |
splash au boot |
Captures régénérables : node scripts/screenshots.mjs (playwright requis).
Architecture
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
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)
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 8200Healthcheck : 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.


splash au boot