Qui parle vraiment à votre chat de support IA ?

saas

Vous avez ajouté un widget de chat IA à votre produit. Il répond à partir de votre centre d’aide, il évite une bonne part des tickets, et toutes les transcriptions sont là, dans une liste. Vous les lisez le vendredi après-midi.

Et chacune d’elles affiche Anonyme.

Vous voyez que quelqu’un a demandé trois fois dans l’après-midi comment changer son e-mail de facturation. Vous ne voyez pas que c’était la même personne, qu’elle a votre plus gros forfait, et qu’elle a fini par abandonner et résilier le lundi suivant.

C’est ce vide que comble l’identité.

Ce qu’un historique anonyme peut vous dire, et ce qu’il ne peut pas

Une transcription anonyme est réellement utile. Elle vous dit ce que les gens demandent, à quelles questions votre documentation ne répond pas et quelles pages les déroutent. C’est toute la base de la boucle « écrire l’article manquant », et elle fonctionne sans connaître un seul nom.

En revanche, elle ne relie une conversation à rien d’autre de ce que vous savez.

  • La fréquence par personne, pas par session. Dix conversations sur la même fonctionnalité peuvent venir de dix utilisateurs perdus, ou d’un seul, très obstiné. Ces deux situations demandent des réponses complètement différentes, et un historique anonyme ne sait pas les distinguer.

  • Qui est concerné. « Trois personnes ont posé une question sur la limite de débit de l’API » ne se lit pas pareil quand l’une d’elles est votre plus gros compte.

  • Le suivi. Un agent du support qui ouvre un ticket ne voit pas ce que l’IA a déjà dit à ce client une heure plus tôt, alors le client explique tout deux fois.

  • Le signal de résiliation. Des questions agacées qui se répètent sur un même compte dans les deux semaines qui précèdent sa résiliation, c’est l’un des signaux les plus clairs que vous recevrez jamais. Sans identité, il reste invisible.

L’idée évidente, et pourquoi elle est dangereuse

Le plus évident est de dire au widget qui est connecté. Votre application le sait déjà.

JavaScript
KookeeChat.identify({ id: user.id, email: user.email, name: user.name });

Le danger, c’est la suite. On est très tenté de raisonner ainsi : le widget sait maintenant de qui il s’agit, donc l’assistant peut répondre aux questions sur son compte. Ses factures. Sa consommation. Ses tickets ouverts.

Ne le faites pas. Tout ce que le navigateur envoie, la personne devant le navigateur peut le modifier. Il suffit d’ouvrir les outils de développement et d’appeler identify() avec l’e-mail de quelqu’un d’autre : si l’assistant fait confiance à ce champ, vous avez construit une fuite de données avec une interface de chat par-dessus.

Cette catégorie de bug n’a rien d’hypothétique. C’est la même erreur que de faire confiance à un identifiant utilisateur fourni par le client dans une requête API, et elle se corrige de la même façon.

L’identité pour étiqueter, l’authentification pour les données

Pour s’en sortir, il faut séparer deux choses qui ont l’air de n’en faire qu’une.

L’identité est une étiquette. Elle indique à qui appartient probablement la conversation, pour que vous puissiez regrouper, rechercher et lire vos transcriptions. Elle n’est pas vérifiée, et elle ne décide de rien.

L’authentification est à part, et elle existe déjà. Votre application authentifie les utilisateurs. Votre API vérifie les sessions. Rien de tout cela ne change parce qu’un widget de chat est apparu.

Donc, quand l’assistant a vraiment besoin de données privées, il ne doit pas les lire dans le champ d’identité. Il doit appeler une fonction qui s’exécute dans le navigateur du visiteur, avec le cookie de session du visiteur, sur votre propre API :

JavaScript
KookeeChat.init({
  apiKey: 'YOUR_API_KEY',
  visitor: { id: user.id, email: user.email }, // une étiquette, pour votre tableau de bord
  tools: [
    {
      name: 'get_my_invoices',
      description: "Look up the signed-in customer's recent invoices.",
      parameters: { type: 'object', properties: {} },
      handler: () => fetch('/api/my/invoices', { credentials: 'include' }).then((r) => r.json()),
    },
  ],
});

Lisez attentivement. Ce n’est pas le champ visitor qui rend ce code sûr, c’est credentials: 'include'. Votre serveur reçoit une requête authentifiée normale et applique les mêmes règles que partout ailleurs. Si quelqu’un falsifie identify(), il obtient quand même ses propres factures, parce que c’est le cookie qui décide.

Dans cet extrait, le champ d’identité fait quelque chose de bien plus modeste : il met un nom sur une ligne de votre tableau de bord.

Ce que tout le monde oublie : la déconnexion

Voici le bug que vous livrerez si vous n’implémentez que le cas nominal.

Quelqu’un se connecte sur un ordinateur de bibliothèque, pose trois questions à l’assistant sur son compte, puis se déconnecte. La personne suivante s’assoit, ouvre le chat, et la conversation précédente est toujours là : le widget l’a gardée dans le navigateur, et personne ne lui a dit que quelque chose avait changé.

L’identité ne se règle pas une fois pour toutes. Elle change, et c’est ce changement qui compte :

JavaScript
// à la connexion
KookeeChat.identify({ id: user.id, email: user.email, name: user.name });

// à la déconnexion — ne sautez pas cette étape
KookeeChat.reset();

Un widget de chat qui gère l’identité doit traiter un changement de personne comme une frontière nette : supprimer la transcription, ouvrir une nouvelle session, ne rien garder. Dans Kookee, cela se fait automatiquement quand l’identité passe à une autre personne ou à personne. Mais le widget ne réagit qu’à ce que vous lui dites : l’appel à reset() à la déconnexion, c’est à vous de l’écrire.

Il y a une subtilité à connaître, parce qu’elle piège même ceux qui ont bien placé l’appel de déconnexion. La plupart des applications ne savent pas qui est l’utilisateur au premier rendu. Elles récupèrent la session, et c’est seulement ensuite qu’elles peuvent identifier quelqu’un. Si « je ne sais pas encore » et « personne n’est connecté » se ressemblent pour le widget, chaque chargement de page jette la conversation avant la fin de votre vérification de session. Il faut donc pouvoir les distinguer : laissez la valeur à undefined pendant le chargement, et passez explicitement null une fois que vous savez qu’il n’y a personne.

Quoi envoyer

Moins que vous ne le pensez.

Il vous faut une valeur stable pour regrouper les conversations. Un identifiant utilisateur tiré de votre propre base de données fait parfaitement l’affaire, et il ne signifie rien pour qui n’a pas accès à cette base. Un e-mail est plus utile pour le support, puisque c’est par lui que vous cherchez quand un client écrit, mais c’est aussi une donnée personnelle, avec une durée de conservation.

Un choix par défaut raisonnable :

  • Envoyez toujours l’identifiant. C’est la clé de regroupement, et il est opaque.

  • Envoyez l’e-mail si votre support cherche par e-mail. C’est le cas de la plupart.

  • Envoyez le nom pour la lisibilité. Le tableau de bord se parcourt alors d’un coup d’œil.

  • N’envoyez rien d’autre. Le forfait, le rôle ou l’ancienneté du compte semblent utiles, mais leur place est dans le contexte que vous donnez à l’assistant, pas dans l’étiquette d’identité. Mélanger les deux crée un stock de données personnelles que vous n’aviez pas prévu de constituer.

Et mentionnez-le dans votre politique de confidentialité. Les transcriptions de chat sont déjà des données personnelles ; y rattacher un e-mail ne change pas leur catégorie, mais rend la chose bien plus visible pour un régulateur, et pour vos utilisateurs.

L’assistant doit-il connaître le nom ?

L’idée est séduisante : si le widget sait que la visiteuse s’appelle Alice, l’assistant pourrait commencer par « Bonjour Alice ».

Kookee ne le fait pas, et c’est voulu. L’identité est stockée avec la conversation et n’est jamais envoyée au modèle. Aucune donnée personnelle n’entre dans une requête au modèle sans que vous l’ayez décidé.

Si vous voulez cette salutation, c’est possible, mais vous devez la demander explicitement, via le champ de contexte distinct, prévu pour ce que l’assistant doit savoir :

JavaScript
KookeeChat.init({
  apiKey: 'YOUR_API_KEY',
  visitor: { id: user.id, email: user.email, name: user.name }, // votre tableau de bord
  appContext: { firstName: user.firstName, plan: user.plan },   // l'assistant
});

Deux champs, deux destinataires. Ça ressemble à un doublon jusqu’au jour où quelqu’un demande quelles données personnelles quittent votre infrastructure, et où vous pouvez répondre avec précision.

Ce que vous y gagnez

Dès que les conversations portent une identité, des questions ordinaires trouvent une réponse.

Cherchez l’e-mail d’un client dans vos transcriptions avant un appel de renouvellement, et vous saurez ce qui l’a dérouté ce trimestre. Repérez un compte qui pose quatre fois la même question, et vous avez trouvé une lacune, soit dans la documentation, soit dans l’onboarding. Donnez à l’agent du support la conversation avec l’IA qui a précédé le ticket, et le client cesse de se répéter.

Rien de tout cela n’exige un assistant plus intelligent. Il faut seulement que l’historique cesse d’afficher Anonyme.


Le widget de chat IA de Kookee gère l’identité des visiteurs, aussi bien dans le package React que dans la simple balise script. Les règles complètes, y compris le moment exact où une conversation est supprimée, sont dans Identifier qui discute dans le chat.