Livrable 16
Reco et planificateur
17 — Planificateur (IA)
Reco MVP — pas de ML
Score = combinaison linéaire des termes de la recherche, restreinte par des hard filters :
- même ville ;
- non expiré ;
- si le contexte est « maintenant » : ouvert ou event actif ou offre active ;
- si budget fourni : écarter les prix connus au-dessus ; ne pas écarter
price_unknown(les garder en fin de liste, label « prix non communiqué ») ; - si distance max : écarter au-delà, seulement si un point d'origine existe.
Explainabilité, une raison vraie max trois puces :
- « Ouvert maintenant » (si le calcul d'horaire le dit)
- « À X min à pied » uniquement si un routeur a répondu ; sinon « à environ N m » si haversine, libellé « distance directe »
- « Dans votre budget » si prix connu et sous le seuil
- « Concert ce soir » si event lié publié
- « Déclaré par l'établissement il y a … »
Interdit : « recommandé pour vous » sans préférence stockée. Si pas de personnalisation : « correspond à la recherche ».
Personnalisation (P2) : favoris, follows, clics — opt-in personalization_enabled, désactivable, sans âge exact, sans catégorie sensible.
Embeddings / collaboratif : P2, et seulement après volume réel. pgvector pas au schéma MVP.
Planificateur
Pipeline obligatoire :
requête → intent (règles ou LLM) → contraintes → retrieval SQL → ranking → enchaînement glouton (pas un TSP prétendu optimal) → phrases à partir des champs réels
Interdit : prompt « invente une soirée à Orléans ».
Séquence
Types d'étapes demandés (sequence: restaurant, bar, event, club) ou défaut configurable par DNA si l'utilisateur dit « une soirée » sans détail : ne pas appliquer un défaut silencieux. Demander le créneau ou proposer les filtres vides.
Pour chaque étape, créneau estimé :
- durée par défaut affichée comme hypothèse de planification (« créneau proposé : 1 h 30 »), paramètre, pas une vérité sur le service ;
- next start = fin + déplacement ;
- le lieu doit être ouvert sur tout le créneau ou l'event doit couvrir le créneau ;
- déplacement = routeur si un fournisseur répond ; sinon distance directe sans temps inventé. Pas d'estimation « 5 km/h » présentée comme une durée de marche.
THIRD-PARTYsi API routing.
Chaque étape : primary + jusqu'à 2 alternatives réelles. S'il en manque : « pas d'autre option vérifiée ».
Changement (annulation, closed_until) : recalcul de cette étape.
Mode nuit : le plan stocké, prochaine étape en tête, lien itinéraire, alternative. Pas de tracking de position en fond. La géoloc est redemandée au moment de l'itinéraire.
Groupe
Tables prévues (08). Pas d'UI MVP. Pas de chat de groupe.
Météo
Si un fournisseur est branché plus tard : infléchir terrasse vs intérieur si l'attribut terrasse est sourcé. Si météo down : ignorer. Ne pas bloquer. Aucun fournisseur choisi. THIRD-PARTY. Ne pas inventer la météo du jour.
Coût IA
- Cache d'intent par hash de requête normalisée, TTL court.
- Petit modèle pour l'intent ; pas d'appel pour le ranking.
- Rate limit par session.
- Si pas de clé : règles seules, fonction identique en aval.
XAI_API_KEYserveur uniquement, jamais dans le client.
Sécurité IA
Le modèle ne renvoie que des slots. Les noms de lieux dans sa sortie sont ignorés. Injection dans le champ recherche : traité comme du texte de requête, pas comme une instruction système (le prompt système est fixe ; la requête est data).
Décisions
D1 — Explainable linear, hard constraints
- Alternatives : boîte noire ; tri par popularité (interdite sans signal, et le signal n'est pas de la foule).
- Risque : résultats « plats ». Acceptable.
- Multi-ville : poids et durées de créneau overridables dans la DNA, filtres identiques.
D2 — LLM optionnel et tardif dans le pipeline
- Choix : la base est la vérité opérationnelle.
- Risque : texte fluide qui sur-promet. Le template de rendu ne peut imprimer que des champs (
name,starts_at,provenance). Pas de phrase libre du modèle sur l'ambiance si l'ambiance n'est pas un attribut sourcé.