mirror of
https://github.com/JMP-MS/ASPIC.git
synced 2026-10-27 19:27:10 +00:00
69 lines
16 KiB
Markdown
69 lines
16 KiB
Markdown
# 29. Interprétation vocale des contacts par LLM
|
|
|
|
*Section démarrée le 4 septembre 2026, dans le prolongement direct de la section 27 (Pilotage vocal) et de la banque de dictées constituée sur "Gestion des Contacts" (`micro_test.js`/`dictees_test.php`, table `ASP_DicteesTest`, voir V1.010-V1.011). Objectif de cette session : passer de la dictée brute enregistrée à une première interprétation réelle par LLM, sans jamais créer de contact réel — c'est le chantier annoncé en réflexions stratégiques, section 12.*
|
|
|
|
## Principe retenu et périmètre volontairement restreint
|
|
|
|
Jean-Marc a validé dès le départ le principe déjà posé en réflexions stratégiques : un LLM à sortie structurée (JSON Schema strict), pas de RAG/pgvector pour cet usage (réservé à la recherche floue), catalogue d'actions comparé explicitement en prompt plutôt que deviné. Nouvel outil de mise au point, dans la continuité de `contact_ia_test.html`/`.js` (test antérieur, conservé tel quel, aujourd'hui largement dépassé par ce qui suit) : `contact_ia_interprete.php` (appel LLM), `contact_ia_valide.php` (enregistrement), `contact_ia_panel.js` (panneau flottant, remplace `micro_test.js` sur `#micro-desk`).
|
|
|
|
**Garde-fou explicite, rappelé plusieurs fois en session et documenté en tête de chaque fichier : cette chaîne n'écrit jamais dans `ASP_Contacts` ni `ASP_Professions`.** Tout passe par une table à part, `ASP_PropositionsContact` (`PCT_Incr`, `PCT_TexteBrut`, `PCT_Extraction` JSON, `PCT_Valide` JSON, `PCT_Utilisateur`, `PCT_Date`, créée par Jean-Marc directement en base, jamais par Claude). `PCT_Extraction` est la sortie brute et honnête du LLM, jamais retouchée après coup ; `PCT_Valide` est ce que Jean-Marc valide réellement après relecture — les deux ont vocation à diverger dans leur forme, c'est voulu, rien n'est perdu silencieusement (voir plus bas, redesign des actions).
|
|
|
|
## `contact_ia_interprete.php` — extraction structurée
|
|
|
|
Lit à chaque appel `ASP_Statuts`, `ASP_Professions`, `ASP_Agents` (`AGN_Incr, AGN_Nom, AGN_Fonction, AGN_Variantes`, dans cet ordre — voir plus bas pourquoi `AGN_Defaut` n'y figure pas) et `ASP_Actions`, pour construire un JSON Schema strict envoyé à OpenAI (`gpt-5.6-luna`, choisi pour son coût — modèle plus capable `gpt-5.6-terra` documenté en réserve si la qualité d'extraction se révèle insuffisante à l'usage réel, non activé pour l'instant). Le schéma couvre les champs classiques d'un contact, `profession_existante_id`/`profession_nouvelle_libelle`, `assistant_id`, et un tableau `actions[]` (voir ci-dessous). Re-validation défensive côté PHP après réponse du LLM : `CNT_Statut`, `assistant_id` et chaque `action_existante_code` sont revérifiés contre les valeurs réelles de la base avant renvoi au client — le LLM ne peut jamais faire accepter une valeur qui n'existe pas vraiment.
|
|
|
|
**Décision architecturale actée en session : l'attribution d'un assistant par défaut n'est pas le rôle de cette couche.** Premier réflexe de Claude : pré-sélectionner l'assistant par défaut (`AGN_Defaut`, aujourd'hui Matilda) côté serveur ou côté panneau quand aucun nom n'est dicté. Correction de Jean-Marc : cette couche doit rester un enregistreur honnête de ce qui a été réellement dit ; les règles métier (à qui attribuer une tâche sans assistant nommé) relèvent d'un futur traitement n8n qui transformera les propositions validées en vrais post-its, pas de cette interprétation. Revert : colonne `AGN_Defaut` retirée de la requête SQL (redevenue identique à celle d'`agents.php`), toute logique de pré-sélection retirée de `contact_ia_panel.js`, décision documentée en commentaire dans les deux fichiers.
|
|
|
|
**Détection et classement de l'action "ajouter le contact" — confirmée comme une vraie décision du LLM, pas un cas codé en dur** (question posée explicitement par Jean-Marc en fin de session, vérifiée avec lui dans le prompt réel). La consigne du prompt système précise que la création du contact lui-même ("ajoute ce contact", "crée-le", "enregistre ce contact"...) est une action à part entière, sans code dans `ASP_Actions`, à retourner avec le libellé fixe `"Ajouter le contact"` dans `action_nouvelle_libelle` (formulation figée, pour rester comparable d'une dictée à l'autre) — et que, lorsqu'elle est demandée, elle doit toujours être placée en premier dans le tableau `actions`, avant les autres actions éventuelles, car c'est en général la première chose voulue chronologiquement. Un tri côté client dans `contact_ia_panel.js` (voir plus bas) sert uniquement de filet de sécurité si le LLM ne respectait pas l'ordre, pas de mécanisme de détection.
|
|
|
|
## `contact_ia_valide.php` — le seul point d'écriture
|
|
|
|
`INSERT` unique dans `ASP_PropositionsContact`, POST JSON `{texteBrut, extraction, valide}`. Ne relit ni ne valide le contenu de `valide` (colonne JSON opaque). En-tête mis à jour en session pour documenter un changement de forme sans conséquence de schéma : `valide.actions` est passé, le même soir, d'un tableau d'objets `{action_existante_code, action_nouvelle_libelle}` à un simple tableau de libellés texte, suite au redesign de la zone actions (voir ci-dessous) — point à connaître pour qui construira plus tard le consommateur n8n de cette table.
|
|
|
|
## `contact_ia_panel.js` — le panneau flottant, nombreuses itérations dans la session
|
|
|
|
Remplace `micro_test.js` sur `#micro-desk` (`ecran_fond.html`). Construit un panneau flottant (`#panneau-contact-ia`) avec une prompt terminal en deux temps ("attente" curseur clignotant, puis "prêt" — micro ouvert, parlez), la restitution de la dictée brute, et un formulaire d'analyse (identité, statut, profession avec repli "nouvelle profession", coordonnées, assistant, actions, bouton Valider unique).
|
|
|
|
**Simplification de la zone actions.** Premier essai avec un motif complexe (une ligne par action détectée, choix dynamique existant/nouveau par ligne) jugé trop lourd pour un essai vocal rapide — objectif de cette phase étant justement de repérer les actions non encore couvertes par `ASP_Actions`, pas de construire une UI de gestion aboutie. Remplacé par 3 zones de texte libre, pré-remplies avec le texte résolu de chaque action détectée (code existant → son libellé, ou libellé nouveau du LLM), triées pour que "Ajouter le contact" arrive toujours en premier (filet de sécurité, voir plus haut) ; au-delà de 3 actions détectées, les suivantes restent intactes dans `PCT_Extraction` mais non éditables dans l'UI pour cet essai. `collecterActionsValidees()` renvoie désormais un simple tableau de chaînes.
|
|
|
|
**Dimensionnement et défilement du panneau.** Demande de Jean-Marc : que la fenêtre prenne tout de suite sa taille maximale disponible (jusqu'au bas de l'écran sous le bouton micro), l'ascenseur apparaissant ensuite si besoin, sur toute la hauteur de la fenêtre — y compris au prix de faire disparaître les deux premières lignes de prompt en cas de défilement. Résolu en fusionnant l'en-tête de prompt, la zone de restitution et le formulaire de résultat en une seule région scrollable (`.contenu-scrollable`, `flex:1; min-height:0; overflow-y:auto`) à l'intérieur d'une colonne flex de hauteur fixe, celle-ci calculée immédiatement à l'ouverture par `ajusterHauteurPanneau()` (hauteur, pas max-height, basée sur la position réelle du bouton micro moins une marge), recalculée au redimensionnement de fenêtre. Ascenseur discret personnalisé (fin, sombre) sur cette même zone.
|
|
|
|
**Lisibilité des `<option>` des listes déroulantes.** Signalé par Jean-Marc : texte blanc sur fond blanc, et blanc sur fond bleu pour l'option survolée/sélectionnée, sur le statut comme sur la profession. Corrigé par un style explicite (`option{background:#1a1a1a;color:#999}`, `option:checked,option:hover{background:#999;color:#111}`) — réserve honnête : Chrome conserve nativement sa propre surbrillance de survol pour l'option "active" quelle que soit la CSS appliquée (Firefox la respecte mieux), donc le résultat n'est pas identique à 100% d'un navigateur à l'autre, mais le cas bloquant (texte invisible) est bien résolu partout.
|
|
|
|
**Micro fermé après Valider, panneau qui se fermait aussi.** Deux frictions remontées ensemble par Jean-Marc en test réel : *"valider sort de la fenêtre ce qui n'est pas pratique pour en dicter plusieurs à la suite"*, puis *"on revient en écran vierge, mais il ne se passe rien et je pense que le micro est fermé"* (diagnostic confirmé exact). Corrigé en deux temps : le panneau ne se ferme plus jamais après un Valider réussi (retrait de l'appel qui masquait `#panneau-contact-ia`) ; une nouvelle fonction partagée `demarrerEcoute()` (factorisée depuis le clic du bouton micro) est désormais appelée automatiquement 1,8s après un Valider réussi si le micro n'a pas déjà été redémarré manuellement entre-temps — le panneau reste ouvert, prêt pour la dictée suivante, sans manipulation.
|
|
|
|
**Bouton "Abandonner" retiré.** Faisait double emploi avec la touche Échap (déjà en place pour fermer le panneau) — retiré (bouton, CSS, variable, gestionnaire), `abandonnerPanneau()` conservée telle quelle, appelée uniquement par le gestionnaire Échap.
|
|
|
|
**Arrêt automatique sur silence.** Ajout d'un minuteur de silence (`DUREE_SILENCE_ARRET_MS = 3500`, "3-4 secondes" demandées par Jean-Marc), armé seulement après le premier résultat de reconnaissance (pas dès l'ouverture du micro), réinitialisé à chaque nouveau résultat, traité comme un arrêt volontaire propre au bout de 3,5s sans nouvelle parole. Coexiste avec le coupe-circuit anti-boucle déjà en place (5 redémarrages en moins de 800ms) et l'arrêt d'inactivité absolue à 10 minutes (hérités de `micro_test.js`).
|
|
|
|
**Garde défensive ajoutée suite à une erreur en test réel.** Jean-Marc a rencontré `Cannot read properties of undefined (reading 'forEach')` en cours d'essai. Revue statique du code n'a pas permis d'isoler une cause certaine (PDO en `ERRMODE_EXCEPTION`, requête agents identique à celle, déjà fiable, d'`agents.php`) — protection ajoutée par prudence : gardes `(x || [])` sur `remplirStatuts`, `remplirProfessions` et `remplirAssistant`, sur le même principe que celui déjà en place dans `remplirActions`. Aucune récidive rapportée depuis.
|
|
|
|
**Fausse alerte : dropdown assistant vide.** Investigation engagée après un signalement de liste déroulante d'assistants vide malgré une base réellement peuplée (6 agents, 4 actifs) — hypothèse un temps envisagée d'un mauvais routage MIRADOR entre bases. Une option de diagnostic *"(aucun assistant en base)"* a été ajoutée dans `remplirAssistant()` en cours d'investigation (conservée, coût nul). Conclusion de Jean-Marc lui-même : *"c'est une erreur de copier/coller"* de son côté, pas un bug — refermé sans autre correction.
|
|
|
|
### Version applicative
|
|
|
|
`V1.012` pour l'ensemble de cette session (une seule incrémentation, conformément à la règle du journal).
|
|
|
|
## Relecture du corpus réel (11 premiers essais, fin de session)
|
|
|
|
Jean-Marc a transmis un export SQL de `ASP_PropositionsContact` (11 lignes) pour un premier bilan qualité. Constat global positif — Jean-Marc : *"les tests sont vraiment encourageants."* Points relevés par Claude à cette relecture :
|
|
|
|
- **"Ajouter le contact" confirmée fonctionnelle et bien classée** à partir de l'essai n°7 (les tout premiers essais précédaient la mise en place de la règle de classement).
|
|
- **Bug d'extraction identifié, non corrigé** : sur l'essai n°2, l'adresse mail extraite est `"Monsieur Paul lefébure@gmail.com"` — le LLM n'a ni retiré le mot d'appel dicté avant l'adresse ("Monsieur Paul"), ni supprimé l'accent sur "lefébure" pourtant couvert par une consigne explicite du prompt sur les adresses mail sans accent. Jean-Marc a dû corriger à la main. **Piste proposée par Claude, pas encore mise en œuvre** : renforcer la consigne du prompt pour aussi neutraliser les mots parasites/formules de politesse autour d'une adresse dictée, en plus de la règle d'accents déjà en place — en attente d'un feu vert explicite de Jean-Marc.
|
|
- **Coupure possible en fin de dictée** : le texte brut de l'essai n°7 se termine en plein mot ("...aspic son"), ce qui pourrait indiquer une troncature par le nouveau minuteur de silence (3,5s). Question posée à Jean-Marc ; sa réponse évoque une autre piste possible (le chat venant parfois le déranger à son bureau pendant qu'il dicte) sans trancher explicitement si ce cas précis vient du chat ou du minuteur, ni donner d'instruction de changer la valeur. **Point resté ouvert**, `DUREE_SILENCE_ARRET_MS` non modifiée dans l'attente d'un diagnostic plus net.
|
|
- **Profession "Informaticien" apparue deux fois comme nouvelle profession** sur des dictées différentes du corpus. Jean-Marc, interrogé, a confirmé que c'est une situation acceptable et attendue en l'état : une profession peut être marquée "nouvelle" plusieurs fois tant qu'un assistant n'a pas traité le post-it correspondant ; le traitement du post-it devra simplement vérifier si la profession n'a pas déjà été créée entre l'enregistrement de la note vocale et son traitement. Pas de changement de code nécessaire pour ce point.
|
|
- Différences ponctuelles `null` vs chaîne vide entre `PCT_Extraction` et `PCT_Valide` : jugées normales et sans gravité (simplification attendue au passage par le formulaire de validation).
|
|
|
|
## Point de conception pour une prochaine session (noté par Jean-Marc, non conçu ni codé)
|
|
|
|
En clôture, Jean-Marc a formulé une réflexion architecturale à reprendre plus tard : la notion d'action sur un contact devra probablement évoluer pour intégrer formellement "création du contact" comme une action à part entière — dont le classement la place toujours en premier — mais qui ne serait pas accessible **que** par le LLM : l'utilisateur humain doit aussi pouvoir la déclencher depuis un menu ou un raccourci clavier. Il faut cependant que cette action reste connue du LLM, et que le retour de celui-ci vienne s'insérer dans le même classement ordonné, pour que les ordres passés à l'assistant vocal restent cohérents avec ceux disponibles par les écrans classiques. Jean-Marc souhaite généraliser ce principe à l'ensemble des autres actions : une séparation claire entre les actions utilisables par l'utilisateur humain (menu/clavier) et celles utilisables par le LLM de saisie vocale, tout en gardant un vocabulaire d'actions unique et cohérent entre les deux voies d'accès — écho direct du constat de satisfaction qu'il tire de cette journée : *"ce qui me plaît c'est que je vois bien que j'arrive à suivre ma logique : des fonctionnalités accessibles via des menus et écrans de façon tout à fait classique et ces mêmes fonctionnalités accessibles par des ordres vocaux fiables donnés à des assistants !"* Non conçu, non codé — à reprendre dans une prochaine session.
|
|
|
|
## Reste à faire, noté au passage
|
|
|
|
- Décider (avec l'accord explicite de Jean-Marc, pas encore donné) s'il faut renforcer le prompt d'extraction pour neutraliser les mots parasites/formules de politesse dictés autour d'une adresse mail, en plus de la règle d'accents déjà en place.
|
|
- Clarifier l'origine de la coupure en fin de dictée observée sur l'essai n°7 (minuteur de silence à 3,5s vs interruption réelle) avant de décider si `DUREE_SILENCE_ARRET_MS` doit être allongée.
|
|
- Concevoir la séparation "actions utilisables par l'utilisateur humain (menu/clavier)" / "actions utilisables par le LLM de saisie vocale", avec une notion de "création du contact" formalisée comme action à part entière toujours classée en premier, connue des deux voies d'accès et cohérente entre elles (voir ci-dessus) — réflexion actée par Jean-Marc, non conçue, non codée.
|
|
- Poursuivre la constitution du corpus réel sur "Gestion des Contacts" (Jean-Marc prévoit de réutiliser ce même écran dès le lendemain pour des demandes de modification/suppression de contact) et suivre si le taux de doublons de nouvelles professions reste gérable en pratique au fil du volume.
|
|
- Étendre progressivement, comme annoncé par Jean-Marc, ce même principe d'écran (dictée + interprétation LLM + panneau de validation) aux autres applications d'ASPIC.
|
|
- Rappel toujours valable (sections 26/27/28) : `ASP_ToDoList`/`todolist.php` doivent être étendus pour qu'une tâche puisse être réellement attribuée à un assistant avant qu'un vrai post-it puisse être généré à partir d'une proposition validée de cette table — prérequis explicitement posé par Jean-Marc avant d'aller plus loin sur le traitement n8n.
|