⚠️ URL live hors fonction pour le moment. L'application a été retirée du cluster MacLustr le 2026-09-04 (processus arrêtés, copie du nœud supprimée). Ce dépôt spbgit est désormais la seule source de vérité du projet. Ancienne URL : https://www.ora-ka.com
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.
Un seul point d'entrée pour cinq univers : propriétés à vendre (Immo·Ka), logements à louer (Lou·Ka), produits québécois (Fabri·Ka), véhicules d'occasion (Auto·Ka) et épicerie comparée (Food·Ka). L'utilisateur tape une requête en langage naturel — « condo 2 chambres montréal », « fromage du québec », « VUS hybride récent » — et Ora-Ka répond avec les meilleures inscriptions, tous univers confondus, classées par un score unifié.

· mobile
Captures du 2026-08-25.
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 |
Au 2026-08-28, la prod affiche 410 988 inscriptions (immo 59 255 ·
lou 15 987 · fabri 291 573 · auto ~17,8 k · food ~26,3 k) — le chiffre vit au
rythme des synchronisations, GET /api/overview fait toujours foi.
| 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 |
Visite guidée (captures du 2026-08-28)
Dix captures de la plateforme en production (docs/screenshots/).
1 — Accueil : une barre, cinq univers

La landing « famille Ka × verre » : logo Ora·Ka (badge lime incliné), compteur vivant (« Une barre. 410 988 inscriptions. Tout le Québec. »), barre de recherche unifiée au liseré conic-gradient aux 5 couleurs d'accent des univers, filtres de portée (Tout · Propriétés · Logements · Produits QC · Véhicules · Épicerie), suggestions de requêtes en langage naturel, ticker des compteurs par univers et pilule KA ID (SSO du Groupe KA) en haut à droite.
2 — /immo/ : Immo·Ka, les propriétés à vendre

L'app Immo·Ka reproduite à l'identique sous son préfixe : héro rouge « Toutes les propriétés à vendre, un seul endroit », ticker des agences (RE/MAX, DuProprio, Kijiji, Via Capitale…), compteurs live (59 255 propriétés · 26 agences · 2 847 villes · prix moyen), barre de filtres (ville, type, prix), vues Liste/Carte.
3 — /lou/ : Lou·Ka, les logements à louer

Lou·Ka au complet — recherche par ville/quartier, carte MapLibre (worker rebasé sous le préfixe), fiches enrichies (quartier, POI, géocodage) — servie par son backend d'origine, intact.
4 — /fabri/ : Fabri·Ka, les produits québécois

Le répertoire des produits d'ici : 291 573 produits, 1 263 boutiques, 17 régions, navigation par catégories (mode, bijoux, épicerie fine, café & thé, art & artisanat, maison & déco) et recherche plein texte.
5 — /auto/ : Auto·Ka, les véhicules d'occasion

L'agrégateur de véhicules d'occasion du Québec (concessionnaires et plateformes), avec ses filtres marque/modèle/année/prix et ses fiches détaillées d'origine.
6 — /food/ : Food·Ka, l'épicerie comparée

Le comparateur d'épicerie : produits des grandes bannières agrégés et comparés, catégories, rabais et historique — même app, même UX que le déploiement individuel.
7 — Conditions d'utilisation

/conditions (oraka/legal.py) : nature du service (agrégation
d'inscriptions publiques — chaque inscription demeure la propriété de sa
source), exactitude des données, encadrement de la recherche sémantique
(scores indicatifs, ni classement commercial ni recommandation),
utilisation permise.
8 — Politique de confidentialité

/confidentialite : données traitées, compte KA ID, cookies de session,
absence de revente de données — le pendant confidentialité du cadre légal
commun au Groupe KA.
9 & 10 — Accueil mobile
La landing en format téléphone : mêmes éléments (barre, filtres empilés, suggestions, ticker, footer des cinq univers), disposition en colonne, cibles tactiles conformes au socle mobile Ka (anti-zoom 16 px, safe-areas).
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).
Le pipeline, étape par étape
- Normalisation de la requête : minuscules, accents retirés,
découpage en jetons ≥ 2 caractères, stopwords français exclus
(
_tokensdansoraka/search.py). - Volet exact : chaque univers est interrogé dans sa propre base
SQLite (ouverte en lecture seule
mode=ro) — FTS5 quand la table virtuelle existe, sinonLIKEen AND sur tous les jetons ; chaque hit est mis en forme par un_shape_<univers>(titre, prix, image, ville, lien vers la fiche sous préfixe). - Volet sémantique : la requête est embarquée via l'API OpenAI
(text-embedding-3-small, 256 dimensions, vecteurs stockés en
float16) puis comparée en brute-force numpy (produit scalaire
sur matrices normalisées, entièrement en RAM) aux ~362 k vecteurs des
index
data/semantic/<univers>.npz; seuil plancher de similarité 0,32 (_SEM_FLOOR), top-k par univers. - Fusion : union des candidats exacts et sémantiques, score unifié (cosinus + bonus de couverture/phrase/exactitude + affinité d'univers), coupure dynamique et garantie de diversité inter-univers.
- Repli silencieux : si l'index npz ou la clé OpenAI manquent, le
moteur sert le volet exact seul (
semantic.available()), jamais d'erreur utilisateur.
| « 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 : voir docs/archive/splash.png) |
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).
Le montage FastAPI (app.mount("/<alias>", <alias>_app)) confine le
catch-all SPA de chaque app à son préfixe ; /immo sans barre oblique
redirige en 308 vers /immo/, etc. Le routeur d'authentification KA ID est
inclus avant les montages pour que /api/auth/* soit toujours servi par
l'app racine. run.py watch lance 5 threads de synchronisation (un par
univers, démarrages décalés de 5 s), chacun à la cadence native de son
déploiement individuel — une app en échec ne tue jamais la boucle.
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).
API & routes
Routes servies par l'app maîtresse (oraka/web.py) :
| Route | Méthode | Description |
|---|---|---|
/ |
GET | Landing : barre unifiée, filtres, ticker, splash |
/api/search?q=&scope=&limit= |
GET | Moteur hybride exact + sémantique ; scope optionnel (immo/lou/fabri/auto/food), limit ≤ 120 |
/api/overview |
GET | Compteurs live des 5 univers + total (lecture ro des 5 bases, cache mémoire 5 min) |
/api/favorites |
GET | « Mon univers Ka » : favoris du membre connecté, lus au hub Groupe KA (401 sinon) |
/api/auth/* |
— | SSO KA ID (connexion via le hub groupe-ka.com) |
/healthz |
GET | Healthcheck : présence des 5 bases SQLite |
/conditions · /confidentialite |
GET | Pages légales (HTML servi par oraka/legal.py) |
/immo /lou /fabri /auto /food |
GET | Redirection 308 vers /<alias>/ |
/immo/ /lou/ /fabri/ /auto/ /food/ |
mount | Les 5 apps complètes (leur API interne incluse, ex. /immo/api/...) |
La compression gzip (GZipMiddleware, ≥ 1 Ko) s'applique à toute la
plateforme, montages compris.
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 Réindexation sémantique (run.py index)
oraka/semantic.py construit un corpus texte par univers (titre + ville +
caractéristiques + description tronquée), l'envoie par lots de 800 à
l'API OpenAI (text-embedding-3-small, dimensions=256, retries
exponentiels), normalise les vecteurs et les écrit en float16 dans
data/semantic/<univers>.npz accompagnés d'un <univers>.meta.json
(uids, comptes, horodatage). Le rebuild est incrémental — seules les
inscriptions nouvelles ou modifiées sont ré-embarquées. Le serveur détecte
le nouveau mtime et recharge l'index à chaud, sans redémarrage.
.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.
- Nœud : M4M64a (Mac Studio M4 Max, 16 cœurs, 64 Go) — répertoire
~/apps/ora-ka. - Processus PM2 :
ora-ka-web(serveur :8200),ora-ka-sync(run.py watch, 5 threads),ora-ka-ngrok(tunnelwww.ora-ka.com). - Domaine : https://www.ora-ka.com via ngrok (domaine réservé).
Dépôt & développement (remote-first)
La source de vérité est le repo git sur le nœud M4M64a (~/apps/ora-ka),
pas une copie laptop — comme pour toutes les apps du Groupe Ka. Remote
origin = spbgit (git perso, bare repo ~/srv/git/ora-ka.git sur
M3U96a, alias SSH gitsrv configuré sur les nœuds) :
git remote add origin gitsrv:srv/git/ora-ka.git
git push -u origin mainNe sont pas versionnés (voir .gitignore) : .env*, .venv/,
node_modules/, les bases SQLite (apps/*/data/, data/), les index
sémantiques *.npz et les caches. Les frontend/dist/ (≈ 3,7 Mo au total)
sont versionnés : le repo se déploie sans étape npm.
Écosystème Groupe KA
Ora-Ka est la porte d'entrée unifiée des cinq agrégateurs historiques — chacun reste aussi déployé individuellement :
| Univers | Site individuel | Dans Ora-Ka |
|---|---|---|
| Immo·Ka | www.immo-ka.com | /immo/ |
| Lou·Ka | www.lou-ka.com | /lou/ |
| Fabri·Ka | www.fabri-ka.com | /fabri/ |
| Auto·Ka | www.auto-ka.com | /auto/ |
| Food·Ka | www.food-ka.com | /food/ |
Le Groupe KA compte aussi vrai-prix, trouve-ka, crea-ka, resto-ka, sorti-ka, job-ka, house-ka, rent-ka… — hub et SSO KA ID : https://www.groupe-ka.com.
Contact
- Auteur : Simon-Pierre Boucher — contact@spboucher.ai
- Groupe KA : contact@groupe-ka.com — https://www.groupe-ka.com
© 2026 Groupe KA — Ora-Ka agrège des données publiques ; chaque inscription reste la propriété de sa source.

