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¶
É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_PRIORfavorise les entités administratives larges (country,state,
city) sur les petites entités.jaro_winklerest appliqué après normalisation NFKD et lowercase.rank_bonusdé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 publichttps://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:labelfiltré surFILTER(lang(?label) = "fr"), - population
wdt:P1082quand 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
geodu score
fusionné, - l'adaptateur Wikidata qui peut appliquer un boost additif (max +0.05) à
la confiance LLM si lewikidata_qidest 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). |