Aller au contenu

Validation géographique

Sous-pipeline dédié à la résolution géographique des candidats de type place.
Indépendant du LLM, il produit un signal geo_score ∈ [0, 1] consommé par le
Multi-Signal Scorer.

Diagramme

flowchart TD A[Candidat type=place] --> B{Cache Redis ?} B -->|hit positif| Z[GeoResult cached] B -->|hit négatif __NOT_FOUND__| ZR[GeoResult vide] B -->|miss| C[Photon self-host] C --> D[Filtre OSM types] D --> E[Score composite] E --> F[Calcul ambiguïté top1/top2] F --> G{Wikidata activé ?} G -->|non| H[Cache + retour] G -->|oui| I[SPARQL Wikidata via OSM ID] I --> J[Enrichit QID + label + population] J --> H H --> K[GeoResult]

Étape 1 — Lookup Photon

Module : GeoValidator (src/services/reliability/geo_validator.py).

  • Instance Photon self-host, configurée par PHOTON_BASE_URL.
  • Requête lang=fr&q={candidate}&limit=10.
  • Timeout dur : PHOTON_TIMEOUT_MS (défaut 2 s).
  • Concurrence globale limitée par un sémaphore (PHOTON_SEMAPHORE, défaut 10).
  • Retries HTTP : PHOTON_HTTP_RETRY_MAX (défaut 2), backoff exponentiel court.
  • Cache Redis : clé geo:photon:{candidate_normalized}, TTL 7 jours.
  • Cache négatif explicite via sentinelle __NOT_FOUND__ (TTL également 7 jours)
    pour éviter les retries inutiles sur des noms hors-référentiel.

En cas d'indisponibilité Photon, le validator retourne un GeoResult vide avec
geo_score = 0.0 et source = "ABSTAIN" — fail-soft strict.

Étape 2 — Filtrage OSM

Module : geo_ranking.filter_features.

Le pipeline ne conserve que les features dont le type OSM est cohérent avec
une entité géographique discutable en réunion :

Liste Contenu
ALLOWED_TYPES city, town, village, district, locality, municipality, region, state, country, island, quarter, neighbourhood, …
BLOCKED_OSM_VALUES restaurant, cafe, hotel, shop, pharmacy, school, office, house, …

Toute feature filtrée incrémente geo_lookup_total{source="FILTERED"}.

Étape 3 — Scoring composite par feature

Module : geo_ranking.score_feature.

score = TYPE_PRIOR(osm_value) * 0.35
      + jaro_winkler(candidate, feature_name) * 0.50
      + rank_bonus(feature_position) * 0.15
  • TYPE_PRIOR favorise les entités administratives larges (country, state,
    city) sur les petites entités.
  • jaro_winkler est appliqué après normalisation NFKD et lowercase.
  • rank_bonus décroît avec la position retournée par Photon.

Si la variante phonétique métaphone (jellyfish.metaphone) du candidat
correspond à celle de la feature, un bonus additif de 0.05 est appliqué (capé à
1.0).

Étape 4 — Détection d'ambiguïté

Module : geo_ranking.detect_ambiguity.

Si score(top1) - score(top2) < RELIABILITY_GEO_AMBIGUITY_DELTA (défaut 0.10),
le résultat est marqué ambiguous=True. Le signal geo reste calculé mais la
réduction de confiance se fera par la pénalité de variance du scorer.

Étape 5 — Enrichissement Wikidata

Module : WikidataEnricher (src/services/reliability/wikidata_enricher.py).

Quand WIKIDATA_ENABLED=true, une requête SPARQL résout l'identifiant Wikidata
à partir de l'identifiant OSM de la feature top-1.

  • Endpoint : WIKIDATA_SPARQL_URL (défaut public https://query.wikidata.org/sparql).
  • Timeout : WIKIDATA_TIMEOUT_MS (défaut 10 s).
  • Concurrence globale : WIKIDATA_SEMAPHORE (défaut 2, courtoisie envers
    l'endpoint public).
  • Cache Redis : clé geo:wikidata:{osm_id}, TTL 30 jours.
  • Cache négatif via sentinelle __NOT_FOUND__.

Les propriétés P402 (relation OSM → QID) et P11693 (way OSM → QID) sont
utilisées selon le type d'objet OSM. Le résultat ajoute au GeoResult :

  • wikidata_qid (par ex. Q90),
  • libellé français rdfs:label filtré sur FILTER(lang(?label) = "fr"),
  • population wdt:P1082 quand disponible.

Étape 6 — Construction du GeoResult

GeoResult(
    candidate=str,
    feature_name=str,
    osm_id=str,
    osm_type=str,
    lat=float, lon=float,
    geo_score=float,           # [0, 1]
    ambiguous=bool,
    wikidata_qid=str | None,
    wikidata_label=str | None,
    wikidata_population=int | None,
    source=str,                # CACHE | PHOTON | ABSTAIN | TIMEOUT | ERROR | DISABLED
)

Le GeoResult est consommé par :

  • le Multi-Signal Scorer — composant geo du score
    fusionné,
  • l'adaptateur Wikidata qui peut appliquer un boost additif (max +0.05) à
    la confiance LLM si le wikidata_qid est présent et que le libellé
    Wikidata correspond après normalisation NFKD à la suggestion LLM.

Comportements de dégradation

Condition Comportement
RELIABILITY_GEO_ENABLED=false Sous-pipeline non invoqué, GeoResult.source="DISABLED".
Photon indisponible geo_score=0, source="ABSTAIN", suite du pipeline non bloquée.
Photon timeout source="TIMEOUT", métrique geo_lookup_total{source="TIMEOUT"} incrémentée.
Wikidata indisponible GeoResult retourné sans wikidata_qid, boost non appliqué.
Redis indisponible Cache désactivé, requête directe (fail-open).