Vai al contenuto
Torna a Blog

CRM data quality con AI: pulizia nomi contatti e classificazione automatica in HubSpot

di Federico 6 min di lettura
  • hubspot
  • ai
  • crm

Un CRM con 14.000 contatti non vale molto se il 25% dei record ha nomi sbagliati, vuoti o duplicati. E non vale molto nemmeno se ogni contatto è classificato come “Other” perché nessuno ha tempo di compilare a mano il campo “tipo contatto”. Il problema non è il CRM in sé: HubSpot fa il suo lavoro. Il problema è il flusso a monte. Lead che arrivano da forms diversi, importati da fiere, sincronizzati da WhatsApp, raccolti durante eventi. Ogni canale ha il suo modo di scrivere “info@aziendaxyz.it” nel campo nome, oppure di mettere tutto in maiuscolo, oppure di lasciare il cognome vuoto.

Questo post racconta come abbiamo affrontato il tema su un CRM HubSpot reale, in un settore education/travel B2C, combinando regole deterministiche, fallback chain e AI generativa. Quattro layer di pulizia, una skill per la classificazione in 16 categorie, e un approccio eval-driven che permette di misurare la qualità prima di mandare in produzione.

Il problema reale, non quello dei case study

Il primo audit del CRM ha mostrato tre famiglie di dirty data:

  1. Nomi popolati male: tutto in maiuscolo, accenti sbagliati, “info@” come first name, nome completo duplicato in firstname e lastname.
  2. Nomi vuoti: 2.759 contatti su 14.000 avevano firstname e lastname blank, ma email valida.
  3. Contatti senza tipo: 60% del database con campo “function” (tipo contatto) vuoto o impostato genericamente.

Una pulizia manuale non era proponibile. Una pulizia “ChatGPT-style” senza guardrails era pericolosa: avremmo riscritto migliaia di record senza poter dimostrare cosa era stato cambiato e perché.

Layer 1: pattern matching deterministico sull’email

Prima di chiamare qualsiasi AI, abbiamo provato a estrarre nomi dalla parte locale dell’email. Su 2.759 contatti vuoti, abbiamo identificato 8 pattern ricorrenti:

  • firstname.lastname@dominio.it
  • firstname-lastname@
  • firstname_lastname@
  • lastname.firstname@ (forma “reversed dot”)
  • f.lastname@ (initial + cognome)
  • lastname.f@ (cognome + iniziale)
  • token singolo (marco@) — match contro dizionario di nomi italiani
  • form concatenata (marcorossi@) — segmentation con dizionario

Ogni pattern ha una priorità: i più affidabili (puntato classico) vincono sui dubbi (token singolo). Risultato: 1.011 nomi recuperati su 2.759, zero AI in questo step, output CSV import-ready.

Il punto qui non è la magia. È che questo lavoro è molto più veloce di una correzione manuale e più sicuro di un prompt LLM open-ended, perché ogni decisione è ispezionabile.

Layer 2: ORG_CONTACT detection

Molti record con “nome” tipo Hotel Belvedere srl o Trasporti Lombardi non sono persone: sono organizzazioni infilate nel modulo persona. Trattarli come contatti significa storpiare reportistica, segmentazione, email marketing.

Abbiamo costruito una detection ibrida basata su:

  • match contro un dizionario di circa 15 ORG_WORDS (hotel, srl, transport, agency, school, tour, ecc.)
  • regola LONG_FN: firstname con più di 3 token è quasi sempre un’organizzazione
  • domain match: se il “nome” corrisponde al dominio aziendale (bellavista per bellavista.it), è organizzazione

Su 10.772 contatti named, abbiamo identificato 150 ORG masquerading come persona. Sono passati in un branch separato: tag aggiornato, nome originale preservato come company.

Layer 3: fallback chain con dati HubSpot

Quando email pattern e ORG detection falliscono, scatta il chain di arricchimento:

  1. LinkedIn URL slug: se il contatto ha un LinkedIn populated, estraiamo il segmento finale dello URL e parsiamo first/last name.
  2. Associated company: se il record è linkato a una company, prendiamo il salutation/owner.
  3. HubSpot name fields: titolo, salutation, eventuali property custom.
  4. Email threads: ultimo fallback, parsing di greeting (Ciao Marco) e signature (-- Marco Rossi) sui thread email associati.

Ogni step ha una confidence; ogni decisione viene loggata. Se niente raggiunge la soglia, il contatto resta a “manual review”.

Layer 4: la skill weekly automatizzata

I primi 3 layer girano in batch storici. Per il flusso continuo, abbiamo packageato la logica in una skill che ogni lunedì alle 9:00 scansiona i contatti creati negli ultimi 7 giorni e applica 4 azioni: CLEAR_BOTH (nome impossibile da pulire), CLEAR_FN_ONLY, TRIM_FN, SKIP. Le scritture HubSpot sono non-distruttive: il nome originale viene salvato in una property original_name_backup prima di toccare il record.

Su uno spot check di 180 fix su 10.772 contatti, zero errori in produzione.

Classificazione: 16 categorie in tempo reale

Il secondo problema — contatti senza tipo — l’abbiamo affrontato con un classifier Make + Gemini. Il pattern:

  • Trigger: Make scenario on new contact HubSpot.
  • Fetch: lo scenario tira note + email engagements degli ultimi 90 giorni.
  • Prompt: Gemini 2.5 Flash Lite riceve email, nome, company, contenuto engagement, e deve scegliere 1 di 16 categorie con decision logic + tie-breaker.
  • Write: la categoria scelta finisce nel campo custom function.

Le 16 categorie sono specifiche del dominio (education/travel): Parent, Program Participant, Hotel, Transportation, Activity Provider, School Partner, Press, Vendor, ecc. La decision logic gestisce ambiguità (“è un parent che chiede info per il figlio program participant”): tie-breaker basati su signal gerarchici (email signature > engagement type > inferred from domain).

Tempo medio per classificazione: circa 8 secondi dal momento del create.

In parallelo gira un re-classifier notturno che ri-processa con finestra rolling 7 giorni tutti i contatti con function blank o “Other”. I contatti che inizialmente non avevano abbastanza signal — magari il first engagement è arrivato dopo — vengono recuperati senza intervento.

Eval-driven: come abbiamo evitato di shippare junk

L’errore più grosso che si può fare con AI nel CRM è mandare in produzione un prompt che “sembra funzionare”. Per ognuna delle skill (cleanup e classifier) abbiamo costruito un dataset di evaluation: 50-100 esempi etichettati a mano, run “with-skill” vs “baseline” (manuale), benchmark e viewer per ispezionare i diff.

Su una skill che ha decisioni binarie chiare (CLEAR_BOTH sì/no), il benchmark ha rivelato un 12% di falsi positivi nella prima iterazione del prompt. Abbiamo capito che le organizzazioni con nomi tipo “Marco Polo Tours” venivano classificate come persone. Tre iterazioni di prompt e un nuovo guardrail dopo, siamo passati al 2% di errore. Tutto questo prima di toccare un solo record reale.

Pattern replicabile

Se hai un CRM HubSpot con name dirty e tipo contatto vuoto, il pattern funziona così:

  1. Audit veloce dei dati esistenti — quante righe, che tipo di dirt.
  2. Pattern matching deterministico prima dell’AI — pesca i casi facili senza spendere token.
  3. Fallback chain ordinata — ogni step ha priorità e confidence.
  4. Guardrails non-distruttivi — backup property prima di scrivere.
  5. Eval suite — 50 esempi etichettati, prompt iteration, benchmark.
  6. Scheduled task settimanale — la pulizia è continua, non una tantum.

Per una PMI ecommerce con 5-20k contatti, l’investimento è di 3-5 giornate di setup. Il ROI non sta nel numero di contatti puliti, ma nel fatto che da quel momento in poi marketing, customer care e sales lavorano su dati di cui ci si può fidare.

Hai un processo simile da automatizzare?

Raccontaci il tuo flusso: ti diciamo con onestà se e dove l'automazione ha senso.