🗂 Les entités
Mongo est la source de vérité. Typesense en est la projection recherche (full-text + sémantique). Tout est dénormalisé pour la vitesse.
| Candidats | personnes captées (searches, réseau, décideurs). Champs normalisés intel_* : normRole, normFamily, normSeniority, normContract, geoLat/Lon, skills (unified.skills = les vrais). |
| Offres | annonces LinkedIn extraites → norm {role, family, seniority, contract, skills} par Gemma. Embeddées pour la proximité sémantique. |
| Entreprises / Écoles | créées depuis le parcours des candidats + enrichies (logo B2, secteur, insights). Dédupliquées (fusion exact + arbitrage LLM). |
| Connexions | ton réseau 1er degré. Ingérées comme candidats lite inNetwork (DM direct). |
🧗 Les stades de maturité
Un candidat mûrit du froid au chaud. Chaque cran a une action qui le fait avancer.
| Brut | donnée lite (nom/titre). Invisible au matching (pas de skills). |
| 🤝 Tiède | dans ton réseau (inNetwork) → DM direct, 0 invitation. |
| ✓ Qualifié | enrichi via l'extension (enriched=true) → skills/séniorité réels → scorable au matching. |
| 💬 Contacté | conversation ouverte (status=contacted). |
| 🔥 Actif | a répondu / open-to-work / inscrit sur la plateforme = candidat possédé. |
🎯 Le matching (candidat ↔ offre)
Pipeline hybride : retrieval → 5 axes séparés → composite par branche → rerank LLM → apprentissage. Explicable : tu vois chaque axe.
| Poste | rôle exact (100) > adjacent > famille. Affiné par SPÉCIALITÉ (mobile/frontend/backend/devops/security/data) : Android ≠ frontend web même si tous « Software Engineer ». |
| Séniorité | échelle unifiée (junior/mid/senior/lead/exec ↔ Junior/Confirmé/Senior/Lead/Expert). FILTRE DUR : alternance/stage vs confirmé = exclu. |
| Skills | couverture pondérée (overlap ÷ skills requis) — un partiel sur un rôle « à côté » ne gonfle plus. Skills réels + skills probables (via les pairs). |
| Sémantique | proximité vecteur (embedding MiniLM) entre l'ADN candidat (titre+skills) et l'offre. Capte le « à côté » que l'exact rate. |
| Localisation | contextuelle : remote → neutre ; présentiel/hybride → distance (rayon selon le mode). Filtre dur hors zone/pays. |
Poids par branche : Tech = skills/sémantique lourds · Sales/Support = géo/réseau · Product/Finance = séniorité. Apprentissage : les 👍/👎 recalibrent les poids par branche (seuil 20). Rerank LLM : Gemma re-classe le top-K par adéquation réelle, à la demande.
✨ L'enrichissement
Passer un profil lite → qualifié = ouvrir son profil via l'extension → récupérer skills/expériences réels. Coût : 1 ouverture/profil, action plafonnée (budget/jour, risque de flag du compte). D'où le pilotage par la demande : on analyse d'abord ceux qui matchent une offre ouverte (et le réseau ×1.8). Deux niveaux : public/light (topcard + expériences visibles, doux) et Recruiter (profil complet, 1 vue).
🔍 La recherche
Typesense : full-text tolérant aux fautes + facettes. Sémantique (opt-in) : recall par le sens via vecteurs (« cloud native » ↔ Kubernetes). Filtre 🤝 Réseau pour isoler tes profils tièdes. L'index est un upsert non destructif (une maintenance interrompue ne le vide plus).
🧱 Préparation des données (règles)
Règles pour ne jamais dégrader la donnée enrichie (leçons durement apprises) :
- Une source lite n'écrase jamais un champ enrichi avec null (logo, email, skills…).
- Le reindex est upsert (jamais delete-puis-reconstruit).
- Les fusions retirent le fantôme de l'index + ré-indexent le survivant.
- Pas de coquilles (docs sans nom) ; garde-fous sur les purges de liste vide.
🛡 Sécurité & dosage
Toutes les actions LinkedIn passent par l'extension (empreinte navigateur réelle), sérialisées et espacées (gentleFetch). Quotas par compte : ouvertures/jour, vues Recruiter/jour, invitations/semaine. Les médias enrichis sont rapatriés sur B2 (les URLs licdn expirent). Réglages de cadence modifiables à chaud → Supervision.