# 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 `