Un client, une fiche : résolution par e-mail
Les assistants IA dupliquent les contacts car les noms varient. Historis résout les contacts d'abord par e-mail : un client, une fiche, garanti par la base.
Demandez à votre assistant de consigner un appel et il nommera l'appelant : « Jean Dupont » aujourd'hui, « J. Dupont » demain, juste « Dupont » la semaine prochaine. Les noms sont la façon dont les gens et les modèles se désignent ; ce sont de très mauvaises clés d'identité. Un CRM qui crée un contact pour chaque orthographe finit avec trois copies du même client, chacune détenant une tranche de l'historique. Historis traite cela comme un problème d'identité. Sur le chemin de capture MCP, une référence de personne se résout d'abord par e-mail ; une correspondance de nom peut enrichir une fiche mais jamais en écraser une ; et un nom qui ressemble à un contact existant devient une question plutôt qu'une création aveugle. Un client, une fiche, le tout imposé sous le modèle comme toutes les autres garanties d'écriture.
Un nom est une référence, pas une identité
Le mode de défaillance est invisible au moment de l'écriture. L'agent capture « appel avec Dupont au sujet de la reprise » ; aucun contact ne porte exactement ce nom, alors il en crée un poliment. Pas d'erreur, rien à relire. Juste une bifurcation silencieuse. Deux semaines plus tard, la moitié de l'histoire vit sur « Dupont », l'autre sur « Jean Dupont », et la timeline censée être la mémoire de ce client se lit comme l'histoire de deux inconnus.
Une référence de capture dans Historis est donc { name, email? }, et les deux champs n'ont pas le même rôle : le nom relève de l'affichage ; l'e-mail, quand vous l'avez, est l'identité.
L'e-mail décide, le nom enrichit
// person_refs: [{ name: "J. Dupont", email: "jean@..." }]
// 1. L'adresse décide, quoi que dise la variante du nom
const byEmail = await findVisiblePersonByEmail(org, email)
if (byEmail) return link(byEmail)
// 2. Un nom identique ne peut qu'ENRICHIR un contact encore sans adresse
const sameName = await findPersonByName(org, name)
if (sameName && !sameName.email) {
await db.from('persons').update({ email })
.eq('id', sameName.id)
.is('email', null) // verrouille la course check-then-set
return link(sameName)
}
// 3. Même nom, autre adresse ? Une autre personne : créer.
L'étape 2 est volontairement étroite. Un « Jean Dupont » sans e-mail, créé par une capture antérieure au nom seul, est presque sûrement le même client, donc l'adresse est reportée sur cette fiche et toutes les captures futures se résolvent directement par e-mail. Mais un « Jean Dupont » qui possède déjà une adresse différente est présumé être quelqu'un d'autre. L'e-mail surclasse le nom dans les deux sens : une correspondance d'adresse gagne quel que soit le nom, et une correspondance de nom n'écrase jamais une adresse. Chaque recherche est aussi bornée par la visibilité, comme tout sur la surface agent. Une référence ne peut se résoudre que vers un contact que vous pourriez voir vous-même.
Deux agents peuvent-ils créer deux fois le même contact ?
Non. Et la résolution doit survivre à ce test, parce que la capture est exactement la charge où les écritures se croisent : deux assistants sur la même boîte mail, une synchro rejouée, un import en masse à côté d'une capture en direct. La clé e-mail est un index unique par organisation, donc la base tranche la course de la même façon qu'elle rend les écritures d'événements idempotentes. Une insertion gagne ; l'autre récupère la fiche gagnante et s'y lie. Quand ce contact est hors de la visibilité de l'appelant, l'insertion perdante saute le lien plutôt que de lier à tort. Dans tous les cas, une seconde fiche pour la même adresse ne peut pas exister.
La même règle tient au sein d'un même appel, où deux références portant la même adresse sont la même personne quoi que disent leurs noms. Un contact résolu est aussi mémorisé sous son vrai nom d'affichage ; un payload au sujet d'un client déjà suivi, mêlant { "name": "J. Dupont", "email": "jean@..." } et un simple { "name": "Jean Dupont" }, lie donc cette seule fiche, dans les deux ordres.
Que se passe-t-il sans e-mail ?
L'e-mail n'est pas toujours là. Au comptoir, c'est « Dupont est passé, il a récupéré le vélo ». Pas d'adresse, parfois même pas de prénom. C'est là que les CRM indexés sur les noms bifurquent en silence, alors ici Historis refuse de créer à l'aveugle : avant de créer automatiquement un contact au nom seul, le serveur compare le nom en similarité floue aux contacts que vous pouvez voir.
-- garder les candidats dont le score >= 0.7
GREATEST(
similarity(display_name, ref_name), -- variantes d'orthographe
word_similarity(ref_name, display_name) -- 'Dupont' dans 'Jean Dupont'
)
Les deux opérateurs couvrent les deux bifurcations du monde réel. La similarité par trigrammes attrape la dérive d'orthographe, car une coquille d'un caractère sur un nom complet obtient encore un score d'environ 0,73, assez pour être signalée. La similarité par mots attrape le nom partiel : « Dupont » s'aligne sur des mots entiers dans « Jean Dupont » et obtient un score proche de 1,0, alors qu'un nom court simplement enchâssé au milieu d'un mot ne le fait pas (« Ann » dans « Joanne » reste autour de 0,25, donc une « Joanne » existante ne bloque jamais la création d'« Ann »). « Jean Boulanger » à côté de « Marie Boulanger » obtient environ 0,63, sous le plancher, si bien qu'une famille d'habitués ne transforme pas chaque capture en interrogatoire.
En cas de correspondance, rien n'est créé et rien n'est lié automatiquement. À la place, la référence est retenue et renvoyée dans ambiguous_persons avec les candidats, et votre assistant choisit, ou vous demande. Les imports en masse ont le même garde-fou et sautent les doublons probables au lieu d'en fabriquer. Et si la vérification de similarité est elle-même en panne, le garde-fou laisse passer : la capture aboutit et la réponse signale que le contrôle anti-doublon était indisponible, parce que bloquer vos écritures en silence serait le pire échec.
L'ordre de résolution complet, en un tableau :
| Référence entrante | Contact existant | Résultat |
|---|---|---|
| Porte un e-mail | Même adresse sur une fiche visible | Se lie à cette fiche, quoi que dise le nom |
| Porte un e-mail | Même nom, fiche sans adresse | Reporte l'adresse et se lie |
| Porte un e-mail | Même nom, autre adresse | Crée une nouvelle fiche (homonyme présumé) |
| Nom seul | Correspondance proche (score >= 0,7) | Retenue dans ambiguous_persons ; votre assistant demande |
| Nom seul | Aucune correspondance plausible | Crée une nouvelle fiche |
Ce qui peut encore bifurquer
Soyons honnêtes sur les limites. Une capture au nom seul dont l'orthographe dérive au-delà du seuil bifurque encore ; le plancher est calibré pour interroger sur les doublons probables, pas pour vous questionner sur chaque voisin de chaque nom. Deux clients réellement différents qui partagent un nom complet font deux fiches par conception ; donnez une adresse à chacun et ils ne pourront plus jamais être confondus. Et une bifurcation qui passe quand même se répare à la main aujourd'hui : relier les événements égarés, supprimer le doublon vide. Chaque écriture d'agent étant attribuée, on voit immédiatement quelles captures déplacer.
Pourquoi ça compte
Un historique client ne vaut d'être tenu que s'il s'accumule sur le bon client. Le tableur échoue en silence. Une ligne de plus ne coûte rien, donc chaque vendeur et chaque agent en ajoute une. La plupart des CRM échouent poliment, avec un écran de fusion visité une fois par trimestre. Historis ancre l'identité sur l'e-mail et verrouille le chemin du nom ; la base tranche les courses. La timeline que votre équipe et son IA construisent ensemble atterrit au bon endroit : un client, une fiche.
À lire aussi : pourquoi laisser une IA écrire dans votre CRM est sûr, les garanties d'écriture sur lesquelles ce travail d'identité repose.
Questions fréquentes
- Pourquoi mon assistant IA crée-t-il des contacts en double ?
- Parce qu'il désigne les gens par leur nom, et que les noms varient d'une capture à l'autre : « Jean Dupont », « J. Dupont » et « Dupont » sont trois personnes pour un CRM indexé sur les noms. Historis résout les références par e-mail d'abord, donc les variantes portant la même adresse atterrissent sur la même fiche, et une capture sous un nom seul qui ressemble à un contact existant revient sous forme de question, pas de nouvelle fiche.
- Que se passe-t-il quand j'enregistre un client par son nom seul, sans e-mail ?
- Le serveur compare le nom en similarité floue aux contacts que vous pouvez voir avant de créer quoi que ce soit. Une correspondance proche (un nom partiel, une coquille d'un caractère) retient la référence et renvoie les candidats à votre assistant pour trancher ; rien n'est lié automatiquement. Un nom sans correspondance plausible crée une nouvelle fiche comme avant.
- Deux agents qui écrivent en même temps peuvent-ils créer deux fois le même contact ?
- Non. L'e-mail est unique par organisation via un index de base de données, donc une fiche en double est impossible. Quand deux captures se croisent, une insertion gagne et l'autre récupère la fiche gagnante et s'y lie ; si cette fiche n'est pas visible pour l'agent perdant, aucun lien n'est posé. La convergence vient de la base, pas de la coordination des agents.
- Comment Historis distingue-t-il deux clients qui portent le même nom ?
- Par l'e-mail. Un contact homonyme qui possède déjà une autre adresse est traité comme une personne différente, et une référence portant une adresse ne se lie jamais à un homonyme qui en détient une autre. Deux homonymes font deux fiches par conception ; seule une référence par nom seul très proche d'un contact existant déclenche une question de désambiguïsation.