Import initial de la documentation : statut, roadmap, plans V1/V2, specs verticales (creche, sport), audits securite, plan de test, analyse concurrentielle et maquettes de design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
325 lines
24 KiB
Markdown
325 lines
24 KiB
Markdown
# Écran « Guide IA » — manager-app
|
|
|
|
> Décisions du 2026-08-07. Périmètre **V1**, livré avec le RAG sur contenu CMS.
|
|
> **Maquette de référence : https://claude.ai/code/artifact/c43fe02d-3283-48c2-992f-d687e18acdfa**
|
|
> Elle porte la mise en page, la hiérarchie et **la formulation exacte des libellés** — y compris les exemples de personnalité et les suggestions de messages de repli, qui ne sont pas repris ici. La lire avant d'implémenter.
|
|
> Dépend de : `rag-pgvector-integration-plan.md` (endpoint `/ask`, embeddings).
|
|
|
|
---
|
|
|
|
## État d'implémentation — 2026-08-07
|
|
|
|
### ✅ Livré
|
|
|
|
**Backend** (`dotnet build` : 0 erreur)
|
|
- `Instance` : `GuideName`, `GuidePersonaPrompt`, `GuideVoiceId`, `GuideFallbackMessages` — plus `ToDTO` / `FromDTO`
|
|
- `InstanceDTO` : mêmes champs exposés
|
|
- Conversion JSONB de `GuideFallbackMessages` dans `MyInfoMateDbContext`
|
|
- Migration `20260807143641_AddGuideIaConfigToInstance` — **4 colonnes nullable, purement additive**, aucun risque sur les données existantes
|
|
|
|
**Client généré** — `manager_api_new/lib/model/instance_dto.dart` étendu **à la main** (convention du projet : pas de régénération OpenAPI)
|
|
|
|
**manager-app** (`flutter build web` : OK)
|
|
- Entrée de menu « Guide IA », **conditionnée à `instance.isAssistant`** — même drapeau que la garde d'`AiController`, donc le menu et l'API disent la même chose
|
|
- Écran `Screens/GuideIa/guide_ia_screen.dart` : consommation du mois, disponibilité par canal, identité (nom, personnalité avec 3 exemples cliquables, messages de repli ajoutables/supprimables), choix de voix Viva/Marco avec les 3 messages d'information
|
|
- **35 clés i18n** ajoutées en FR / EN / NL — aucune chaîne en dur
|
|
|
|
### Choix d'implémentation
|
|
|
|
- **Messages de repli** stockés en `List<TranslationDTO>` **à plat** : plusieurs entrées peuvent partager la même langue. Réutilise le type et le convertisseur JSONB existants, sans inventer de type imbriqué. La sauvegarde ne réécrit que la langue d'édition et **préserve les autres langues** intactes.
|
|
- **Consommation affichée en pourcentage + nombre de questions**, jamais en jetons — le gestionnaire ne raisonne pas en tokens. Le ratio jetons → questions est encore approximatif (`/1000`), à caler sur des données réelles.
|
|
- **Canaux en lecture seule** pour l'instant : les activer demande un endpoint de mise à jour d'`ApplicationInstance`. Le sous-titre renvoie explicitement vers la configuration de chaque application, plutôt que d'exposer un interrupteur qui ne ferait rien.
|
|
|
|
### ✅ Configuration branchée sur `AssistantService` — livré le 2026-08-07 (lot 1)
|
|
|
|
> C'était **le** point bloquant : l'écran enregistrait une configuration que personne ne lisait. Ce n'est plus le cas.
|
|
|
|
**Ce qui a été trouvé en ouvrant le fichier** : pas deux blocs de prompt mais **quatre** — scope configuration et scope instance, chacun en vocal et en texte. Le plan n'en annonçait que deux parce que le relevé s'était arrêté au scope configuration.
|
|
|
|
Les quatre écarts constatés, tous corrigés :
|
|
|
|
| Constat | Correction |
|
|
|---|---|
|
|
| Tout le prompt codé en dur, `config?.Label` pour seul élément dynamique — **tous les clients avaient le même guide au même ton tutoyant** | `BuildGuideIdentity()` injecte `GuideName` + `GuidePersonaPrompt` en tête des 4 blocs |
|
|
| Ton imposé (« Tu es chaleureux, naturel ») en conflit avec ce que le client écrit — et c'est le prompt figé qui gagnait | Retiré des 4. Les contraintes **de canal** restent (concision vocale, zéro markdown) : ce sont des contraintes techniques, pas du ton |
|
|
| Repli figé en français tutoyant (règle 12 : « Bonne question, mais là je sèche… ») | `BuildFallbackInstruction()` reprend les `GuideFallbackMessages` du client |
|
|
| **Seconde politique de repli incohérente** ligne 550 (phrase unique, « Je suis uniquement là pour t'accompagner dans ta visite ») | Les 4 blocs partagent désormais la même consigne, issue de la même méthode |
|
|
|
|
**Deux prompts sur quatre n'avaient aucune règle hors-sujet du tout** — les variantes texte. Un visiteur pouvait y demander une recette de tarte. La règle leur a été ajoutée.
|
|
|
|
Corrigé au passage, même fichier : les **4** `new HttpClient()` dans les outils d'agenda (audit sécurité, priorité haute) → `IHttpClientFactory` injecté.
|
|
|
|
`dotnet build` : 0 erreur · `dotnet test` : **124/124**.
|
|
|
|
#### Choix d'implémentation, et pourquoi
|
|
|
|
- **Le tirage aléatoire du repli est fait côté serveur** (`Random.Shared`), pas laissé au modèle. Le plan disait « conserver le tirage aléatoire » ; le faire en C# garantit que la phrase du client est reprise **mot pour mot**, alors que le modèle l'aurait paraphrasée. Un tirage par appel HTTP.
|
|
- **Filtrage strict sur la langue du visiteur.** Sans formulation dans cette langue, on retombe sur le comportement générique (le modèle décline en variant lui-même) plutôt que de servir une phrase française à un visiteur néerlandais. ⚠️ **Corollaire pour l'écran** : un client qui ne remplit ses messages qu'en FR n'a de repli personnalisé qu'en FR — c'est le bouton « traduire automatiquement » qui doit combler ça, et il faut donc qu'il soit visible sur ce champ.
|
|
- **Chargement de l'instance par `Find()`** et non par une requête : `AiController` a déjà chargé l'entité pour le contrôle de quota, dans le même scope EF. Zéro requête supplémentaire.
|
|
- **Pas d'extraction plus poussée des 4 prompts.** L'identité et le repli ont désormais une source unique ; ce qui reste divergent est fonctionnel (outils disponibles, navigation, cartes). Aller plus loin rendrait les prompts illisibles pour un gain nul.
|
|
|
|
### ❌ Pas encore livré, et pourquoi
|
|
|
|
- **Onglet « Ce que demandent vos visiteurs »** — nécessite la table `VisitorQuestion` et le `ConversationId`. Un onglet vide serait exactement le défaut dénoncé plus bas (un réglage sans effet).
|
|
- **« Ce que connaît votre guide »** — nécessite le RAG.
|
|
- **Aperçu de conversation** — plus rien ne le bloque depuis que le persona est injecté : c'est le prochain morceau naturel de cet écran.
|
|
|
|
#### Reprendre ce chantier dans une conversation neuve
|
|
|
|
Ordre de lecture :
|
|
|
|
1. **`DOCS/STATUS.md` §1quater** — l'ordre d'exécution des 4 lots du chantier Guide IA
|
|
2. **Ce document** — l'écran, ses décisions produit, et ce qui a déjà été branché
|
|
3. **`ManagerService/Services/AssistantService.cs`** — `BuildGuideIdentity()` / `BuildFallbackInstruction()` et les 4 blocs de prompt qui les consomment
|
|
4. **`ManagerService/Data/Instance.cs`** — les quatre champs `Guide*`, avec leurs commentaires XML
|
|
|
|
⚠️ **Ce que les documents ne portent pas** : le raisonnement qui a produit ces décisions. Les conclusions et leurs justifications sont écrites ; les fausses pistes écartées en chemin ne le sont pas. Concrètement, ça veut dire qu'une conversation neuve **exécute** bien, mais qu'elle **re-débattra** une décision si on la lui rouvre. Ne pas rouvrir : trancher à nouveau coûte plus cher que de suivre.
|
|
|
|
---
|
|
|
|
## Nom retenu : « Guide IA »
|
|
|
|
« Assistant » décrit comment c'est construit ; **« guide » décrit ce que le visiteur reconnaît** — et dans un lieu culturel, c'est littéralement le métier. Le doc TTS emploie déjà « Configuration du guide IA ».
|
|
|
|
- **UI et client** : « guide »
|
|
- **Vocabulaire technique / code** : `persona`
|
|
|
|
---
|
|
|
|
## V1 — un seul guide, au niveau de l'instance
|
|
|
|
Le guide se configure sur l'**instance**. La recherche RAG est en revanche **limitée à la configuration en cours** : un lieu avec plusieurs parcours ne veut pas que le guide mélange les contenus de l'un et de l'autre.
|
|
|
|
> Le **modèle** part en V1 sous forme de liste (`List<PersonaConfig>`, cf. `tts-pregenerated-plan.md`) même si l'UI n'en expose qu'un. Les guides multiples et le rattachement à une section sont V2 — voir §V2 en bas.
|
|
|
|
---
|
|
|
|
## Contenu de l'écran
|
|
|
|
### 1. Statut & quota
|
|
|
|
- Tokens consommés / plafond du plan, avec la date de remise à zéro
|
|
- État par canal (voir §2)
|
|
|
|
Sans ça, le client dont le quota est épuisé ouvre un ticket « mon guide ne répond plus ».
|
|
|
|
### 2. Activation par canal
|
|
|
|
Le gate réel dans le code est **double** : `Instance.IsAssistant` **et** `ApplicationInstance.isAssistant` par type d'app — c'est déjà ainsi qu'`AiController` et la bulle web fonctionnent.
|
|
|
|
L'écran doit donc montrer **web / mobile / kiosk séparément**. Sinon le client ne comprendra jamais pourquoi le guide répond sur un canal et pas sur l'autre.
|
|
|
|
### 3. Identité du guide
|
|
|
|
| Champ | Contenu |
|
|
|---|---|
|
|
| **Nom** | Libre. « Léon », « Alice », « Le Gardien »… |
|
|
| **Prompt de personnalité** | Ton, spécialité, façon de s'adresser aux visiteurs |
|
|
| **Messages de repli** | **Plusieurs** (2 à 4), tirés au hasard — voir ci-dessous |
|
|
|
|
Le nom libre est cohérent avec le prompt : *« Tu t'appelles Léon, tu parles en belge familier, tu es spécialisé sur le Moyen-Âge. »*
|
|
|
|
**Les messages de repli sont une liste éditable**, pas un champ unique : lignes ajoutables/supprimables, avec des formulations suggérées en un clic. Un guide qui répète mot pour mot la même phrase passe immédiatement pour un automate — et c'est précisément le moment où il faut préserver l'illusion.
|
|
|
|
### Ce qui est traduit, et ce qui ne l'est pas
|
|
|
|
Distinction structurante, à ne pas confondre :
|
|
|
|
| | Nature | Stockage |
|
|
|---|---|---|
|
|
| **Messages de repli** | Texte **vu par le visiteur** | `List<TranslationDTO>`, comme tout le contenu — avec le bouton « traduire automatiquement » existant |
|
|
| **Personnalité** | **Instruction au modèle**, jamais affichée | **Une seule langue.** Le modèle répond dans la langue du visiteur de toute façon |
|
|
|
|
Traduire le prompt de personnalité en 10 langues coûterait des appels pour rien et ferait dériver le ton d'une langue à l'autre. Ce n'est pas du contenu, c'est une consigne.
|
|
|
|
### Langue de l'interface
|
|
|
|
Toutes les chaînes de l'écran passent par **`AppLocalizations`** (FR / NL / EN) — la langue du **gestionnaire**, distincte de celle des contenus.
|
|
|
|
> ⚠️ L'audit manager-app relève que l'i18n est largement contournée : infra complète (373 clés) mais **~100+ chaînes françaises en dur**, et seulement 52 fichiers sur 120 utilisant `AppLocalizations`. Ne pas ajouter à la dette sur un écran neuf.
|
|
|
|
### 4. Sources de connaissance (lecture seule en V1)
|
|
|
|
Liste de ce qui alimente le guide : N sections indexées, dernière indexation. En V2 s'y ajoutent les documents uploadés.
|
|
|
|
#### Une barre de recherche — mais **vectorielle**, pas un filtre texte (arbitré le 2026-08-10)
|
|
|
|
Le client se demande « est-ce que mon guide connaît X ? ». Un `Ctrl+F` sur les chunks ne répond pas à cette question : le guide, lui, cherche par **similarité**. Le client tape « tarif groupe », ne trouve rien parce que sa fiche dit « visites collectives », et conclut à tort qu'il manque une donnée que le guide aurait trouvée. Un filtre texte produit donc de faux négatifs sur la seule question qui compte.
|
|
|
|
La barre lance le **même `SearchAsync`** que le guide et affiche les extraits retournés avec leur score et leur source. Le client voit littéralement ce que le guide voit. Ça transforme le bloc en outil de diagnostic : *« pourquoi mon guide ne parle pas de X »* devient une question à laquelle il répond seul, sans ticket.
|
|
|
|
Coût dérisoire (un embedding de requête). Se marie avec le §5 : l'un teste le persona, l'autre teste la matière.
|
|
|
|
> ❌ **Pas de graphe de connaissance.** Un nuage de nœuds reliés est joli en démo et illisible en usage : à 300 sections c'est une pelote, et surtout il ne répond à aucune question que le client se pose. Le RAG n'est pas un graphe — c'est une liste plate de morceaux dans un espace vectoriel ; le dessiner en réseau **représenterait une structure qui n'existe pas**.
|
|
>
|
|
> S'il faut un visuel, le seul qui informe est une **couverture par section** : liste ou barre montrant combien d'extraits chaque section produit, avec les sections à 0 en tête. C'est ça, l'information actionnable — « ces 12 sections n'apportent rien au guide, elles sont vides ou sans texte ». Un tri, pas un graphe.
|
|
|
|
### 5. Aperçu de conversation
|
|
|
|
Tester le prompt sans device. **Un prompt de personnalité qu'on ne peut pas essayer est inutilisable.**
|
|
|
|
Le travail est déjà à moitié spécifié : `todo-features.md` prévoit un « Mode preview » avec une cible *Assistant / Persona*. **Faire converger les deux plutôt que construire deux fois.**
|
|
|
|
### 6. Voix & assistant vocal
|
|
|
|
Présent dans l'écran **dès la V1**, parce que c'est un argument de démo commerciale.
|
|
|
|
En V1 le client ne choisit **pas une personnalité** — elle vient du prompt libre — mais simplement **une voix**, parmi deux options pré-câblées :
|
|
|
|
| Option | Voix Gemini TTS | Timbre | Mot d'activation |
|
|
|---|---|---|---|
|
|
| **Viva** | `Sulafat` | Warm, middle pitch | « Viva » |
|
|
| **Marco** | `Umbriel` | Easy-going, lower middle pitch | « Marco » |
|
|
|
|
C'est volontairement binaire : la voix et le mot d'activation forment un couple indissociable, parce que les mots d'activation sont des **modèles OpenWakeWord pré-entraînés**, pas des chaînes de caractères qu'on saisit.
|
|
|
|
> **Voix figées le 2026-08-07.** Elles sont **multilingues** : la même voix parle les 10 langues, donc le guide garde une identité vocale constante d'une langue à l'autre. Pas de sélection de voix par langue. Critères de choix et voix écartées : `tts-pregenerated-plan.md`.
|
|
>
|
|
> ❓ Reste à valider à l'oreille sur du **vrai contenu long** (2 min d'une fiche, sur haut-parleur de téléphone) et **dans les 4 langues** — une voix excellente en français peut être médiocre en néerlandais.
|
|
|
|
**Trois messages d'info dans cette section :**
|
|
|
|
1. Le **nom du guide** (libre) et le **mot d'activation** (Viva/Marco) sont deux choses distinctes — le visiteur dit « Viva » pour réveiller un guide qui s'appelle Léon.
|
|
2. Une **voix ou un mot d'activation personnalisé** est un **add-on facturable** : collecte de samples, entraînement OpenWakeWord, intégration au build de l'app.
|
|
3. *« Les lunettes connectées fonctionnent via l'application mobile MyInfoMate installée sur le téléphone du visiteur. »* Un client non technique imagine des lunettes autonomes.
|
|
|
|
### 7. Onglet « Ce que demandent vos visiteurs » — **V1**
|
|
|
|
Deuxième onglet de l'écran Guide IA. Décidé le 2026-08-07 : **exploité dès la V1**, pas seulement journalisé. C'est un argument de vente autant qu'un outil.
|
|
|
|
#### Ce qu'on stocke — et ce qui existe déjà dans le code
|
|
|
|
Vérification faite le 2026-08-07 dans `AiController.Chat` et `DTOs/AiChatDTO.cs` : **presque tout est déjà disponible au point d'appel.**
|
|
|
|
```csharp
|
|
public class VisitorQuestion
|
|
{
|
|
public long Id { get; set; }
|
|
public string ConversationId { get; set; } // ← SEUL AJOUT au contrat d'API
|
|
public string InstanceId { get; set; } // déjà dans AiChatRequest
|
|
public string ConfigurationId { get; set; } // déjà
|
|
public AppType AppType { get; set; } // déjà — Web / Mobile / Kiosk / Voice
|
|
public bool IsVoice { get; set; } // déjà
|
|
public string Language { get; set; } // déjà
|
|
public string Question { get; set; } // = request.Message
|
|
public string Reply { get; set; } // = response.Reply
|
|
public long TokensUsed { get; set; } // déjà — response.TokensUsed
|
|
public bool HasAnswer { get; set; } // ← produit par le retrieval RAG
|
|
public double TopScore { get; set; } // ← idem, seuil ajustable
|
|
public List<string> CitedContentIds { get; set; } // ← idem (JSONB)
|
|
public string ThemeId { get; set; } // null jusqu'au job de regroupement
|
|
public DateTime CreatedAt { get; set; }
|
|
}
|
|
```
|
|
|
|
**Une ligne par tour de conversation, groupée par `ConversationId`** — pas le `History` complet à chaque tour, sinon le tour 5 réécrit les tours 1 à 4.
|
|
|
|
**On stocke aussi la réponse.** Pour analyser une question restée sans réponse, il faut voir ce que le guide a effectivement répondu.
|
|
|
|
> **Le seul changement de contrat** : ajouter `ConversationId` (GUID généré par le front, stable sur une session de conversation) à `AiChatRequest`. Aujourd'hui `History` est reconstruit côté client à chaque appel, donc **rien ne relie deux questions d'un même visiteur côté serveur**.
|
|
|
|
L'insertion se fait dans `AiController.Chat`, juste à côté du `RecordUsage(instance, result.TokensUsed)` existant.
|
|
|
|
#### Déjà tranché par le code, sans qu'on le sache
|
|
|
|
- **Guide par instance ou par configuration ?** `AiChatRequest.ConfigurationId` porte déjà le commentaire *« null = scope instance, fourni = scope configuration »*. La question était déjà résolue.
|
|
- **Canal vocal** : `AppType.Voice` existe déjà, avec fallback sur Mobile si aucune `ApplicationInstance` dédiée. Et `IsVoice` fait déjà adapter le prompt par le backend (pas de markdown, pas de navigation, dates en toutes lettres).
|
|
|
|
#### Ce qu'on affiche
|
|
|
|
| Bloc | Contenu | Pourquoi ça vaut quelque chose |
|
|
|---|---|---|
|
|
| **Volume** | N questions ce mois, évolution | Preuve d'usage — utile face à un CA ou un subsidiant |
|
|
| **Questions sans réponse** | Liste, les plus fréquentes en tête | **Le cœur.** C'est un rapport de trous de contenu : la liste exacte de ce qu'il faut ajouter au CMS |
|
|
| **Thèmes** | Regroupement des questions du mois | *« 18 % sur les horaires, 12 % sur l'accessibilité »* |
|
|
| **Langues des questions** | Répartition réelle | Révèle l'écart entre les langues traduites et celles réellement utilisées — un lieu qui a payé 10 traductions pour 94 % de questions FR/NL a une décision à prendre |
|
|
| **Sections les plus citées** | Contenus réellement mobilisés | Quel contenu sert vraiment, par opposition à celui qui a coûté du temps à écrire |
|
|
|
|
Le bloc « questions sans réponse » est celui à mettre en avant : il transforme le guide d'un gadget en **outil de pilotage éditorial**. Il alimente aussi naturellement le rapport PDF prévu dans `plan-import-ia-stats-subsides.md`.
|
|
|
|
#### Regroupement en thèmes
|
|
|
|
Job périodique qui regroupe les questions du mois via Gemini, pas de classification à la volée. Coût négligeable, et ça évite de faire porter à `/ask` un traitement dont le visiteur n'a que faire.
|
|
|
|
> ⚠️ **RGPD — à cadrer avant d'implémenter.** Une question de visiteur est du texte libre qui peut contenir des données personnelles, y compris sensibles (« je suis en fauteuil roulant, c'est accessible ? » = donnée de santé). Donc :
|
|
> - aucun identifiant visiteur au-delà d'un `sessionId` non nominatif, non relié à une personne ;
|
|
> - **rétention courte sur les questions brutes** (90 jours), les agrégats et thèmes étant conservés au-delà ;
|
|
> - purge automatique par job, pas « quand on y pensera » ;
|
|
> - mention explicite dans les CGU et la politique de confidentialité **avant** la mise en service.
|
|
|
|
---
|
|
|
|
## Décisions produit
|
|
|
|
### Les sources : visibles pour le client, pas pour le visiteur
|
|
|
|
**Décision (2026-08-07)** : le petit **i** qui révèle les sources n'apparaît **que dans l'aperçu de manager-app**. Côté visiteur, le guide répond en prose, point.
|
|
|
|
Le raisonnement est le bon : le visiteur se moque de savoir de quelle fiche vient la réponse — il veut la réponse. Celui qui a besoin de vérifier, c'est le client, quand il teste son guide ou quand il conteste une réponse.
|
|
|
|
> **À faire quand même** : renvoyer les sources dans la réponse de `/ask` **dans tous les cas**. C'est gratuit, ça sert au débogage, et ça rend l'affichage visiteur activable plus tard sans toucher au backend. On ne les *rend* simplement pas dans visitapp.
|
|
|
|
### Quand le guide ne trouve rien : un message, pas un booléen
|
|
|
|
**Pas de bascule « répondre avec ses connaissances générales ».**
|
|
|
|
Le client l'activerait — ça paraît plus serviable — et le guide inventerait alors des dates sur ses œuvres. L'erreur retomberait sur MyInfoMate, pas sur le modèle. Pour un lieu culturel, **un guide qui invente est un défaut produit, pas une commodité**.
|
|
|
|
Comportement fixe : le guide dit qu'il ne sait pas et renvoie vers l'accueil. Ce que le client personnalise, c'est **le message**, pas le comportement.
|
|
|
|
> La serviabilité non factuelle (politesse, navigation dans l'app, orientation) se règle dans le prompt de personnalité — elle ne nécessite aucun accès à des connaissances externes.
|
|
|
|
### Ce qui alimente le guide en V1
|
|
|
|
**Tout ce qui est déjà visible par les visiteurs, et rien d'autre.** Pas de case à cocher : un contenu CMS publié est par définition destiné au public.
|
|
|
|
> ⚠️ **Corollaire à implémenter** : le pipeline doit respecter `IsActive`. Une section désactivée ou non publiée ne doit pas être indexée, et la désactiver doit **purger ses embeddings**. Sinon le guide révèle du contenu que le client prépare — le même incident de confidentialité que le PDF interne, mais dès la V1.
|
|
|
|
### Pourquoi `IncludeInAiKnowledge` n'apparaît pas encore
|
|
|
|
La **colonne** part avec la migration (schéma). Le **contrôle dans l'UI** n'apparaît qu'en V2, avec l'ingestion documentaire — parce qu'en V1 aucun fichier n'alimente le guide : la case ne gaterait rien.
|
|
|
|
> Ce projet a déjà trois occurrences du même défaut — `isHiddenInitially`, `meterZoneGPS`, `mapProvider` en web : des réglages exposés au client sans aucun effet. Ne pas en ajouter un quatrième. Le contrôle apparaît le jour où il contrôle quelque chose.
|
|
|
|
---
|
|
|
|
---
|
|
|
|
## Écran Statistiques — onglet « Vocal » (V1)
|
|
|
|
L'écran de statistiques segmente aujourd'hui par canal (web, mobile, kiosk). **Ajouter un onglet « Vocal »** au même endroit.
|
|
|
|
Le travail est plus petit qu'il n'y paraît : **`AppType.Voice` existe déjà** dans le modèle, et `AiChatRequest.IsVoice` distingue déjà les interactions vocales. À vérifier : que `VisitEvent` porte bien l'`AppType`, sinon c'est le seul ajout.
|
|
|
|
Ce que le client doit y voir :
|
|
|
|
- Nombre de visiteurs ayant utilisé le guide vocal, et volume de questions
|
|
- **Ce qui a été réellement écouté** : contenus déclenchés à la voix, POI atteints, durée d'écoute — l'équivalent vocal du « ce que le visiteur a consulté »
|
|
- Part du vocal dans l'usage total, comparée aux autres canaux
|
|
|
|
> **À inclure dans l'export PDF** (`plan-import-ia-stats-subsides.md`) au même titre que les autres canaux. C'est exactement le genre de chiffre qu'un lieu subventionné met dans son rapport d'activité — « X % de nos visiteurs ont utilisé le guide vocal » est un argument de renouvellement de subside.
|
|
|
|
---
|
|
|
|
## V2 — guides multiples & guide thématique
|
|
|
|
Plusieurs guides, assignables à une section ou une configuration (guide thématique).
|
|
|
|
Reporté non pas par charge d'UI, mais parce que deux questions de conception ne sont pas tranchées :
|
|
|
|
1. **Qui répond depuis l'accueil**, quand aucune section n'est ouverte ?
|
|
2. **Sur quel périmètre de contenu** cherche un guide thématique — sa seule section, ou tout, avec un ton différent ?
|
|
|
|
Mal répondre en V1 donnerait un guide qui a l'air cassé. Le modèle en liste étant déjà en place, l'ajout ne coûtera rien de plus plus tard.
|
|
|
|
---
|
|
|
|
## À faire côté commercial quand les lunettes sortent
|
|
|
|
Ajouter à **myinfomate-landing**, en langage client non technique :
|
|
|
|
- **« Un guide vocal qui connaît vos contenus »** — le RAG expliqué sans le mot : le guide répond à partir de *vos* fiches, pas d'Internet.
|
|
- **« Mains libres avec les lunettes connectées »** — le visiteur pose sa question à voix haute, la réponse arrive dans ses oreilles, le téléphone reste dans la poche.
|
|
|
|
> ⚠️ La landing affiche aujourd'hui `req/mois` pour le quota IA (`translations.ts`, clé `features.reqPerMonth`) alors que le backend compte en **tokens**. À corriger **avant** de reprendre les CGU, sinon les CGU héritent de l'erreur.
|