Keyword Extraction da articoli, ticket e documenti senza LLM

Quando gestisci un blog, una knowledge base o un sistema di ticketing, prima o poi ti scontri con lo stesso problema: centinaia (o migliaia) di testi senza tag, senza categoria, senza metadati utili. Assegnarli a mano è impensabile, e non serve per forza un LLM per risolvere il problema: la keyword extraction classica, con librerie come YAKE e scikit-learn, fa un ottimo lavoro, è deterministica, gira offline ed è economicissima da eseguire su grandi volumi.
In questo articolo vediamo due approcci complementari: YAKE (statistico, senza bisogno di corpus di riferimento) e TF-IDF (comparativo, utile quando hai già una collezione di documenti).
Cosa vedrai
- Perché non serve un LLM
- Keyword extraction con YAKE
- Keyword extraction con TF-IDF
- Confronto tra i due approcci
- Tagging automatico di articoli e ticket
- Risorse utili
Perché non serve un LLM
Per estrarre le parole chiave più rappresentative di un testo non serve comprendere il significato profondo del linguaggio: serve individuare termini statisticamente rilevanti rispetto al testo stesso o rispetto a una collezione di riferimento. È un problema che gli algoritmi classici risolvono da decenni, con vantaggi concreti:
- costo quasi nullo: nessuna chiamata API, nessun token da pagare
- latenza minima: qualche millisecondo per documento
- deterministico: stesso input, stesso output, sempre
- offline: nessun dato sensibile lascia la tua infrastruttura
- spiegabile: puoi sempre motivare perché una parola è stata scelta (frequenza, posizione, co-occorrenza)
Keyword extraction con YAKE
YAKE (Yet Another Keyword Extractor) è un algoritmo unsupervised che non richiede un dataset di addestramento né un corpus di riferimento: analizza statistiche testuali come posizione delle parole, frequenza, casualità di comparsa e relazione con il contesto per assegnare un punteggio a ogni possibile keyword. Attenzione: a differenza di altri sistemi, in YAKE un punteggio più basso indica una parola chiave più rilevante.
Installiamo la libreria:
pip install yake
Ed estraiamo le parole chiave da un testo:
import yake
testo = """
spaCy è una libreria open-source per il Natural Language Processing in Python.
Supporta decine di lingue ed è progettata per l'uso in produzione. La funzione
di sentence splitting divide automaticamente un testo in frasi. Questa
funzionalità è fondamentale per costruire pipeline NLP robuste e scalabili.
Il componente senter, addestrato su corpus reali, gestisce correttamente
abbreviazioni e casi particolari del testo in lingua italiana.
"""
kw_extractor = yake.KeywordExtractor(
lan="it", # lingua del testo
n=2, # lunghezza massima delle n-gram (1 = singole parole, 2 = bigrammi...)
top=8, # numero di keyword da restituire
)
keywords = kw_extractor.extract_keywords(testo)
# Punteggio più basso = keyword più rilevante
for kw, score in sorted(keywords, key=lambda x: x[1]):
print(f"{score:.4f} {kw}")
Output:
0.0059 Natural Language
0.0059 Language Processing
0.0292 libreria open-source
0.0602 Python
0.0768 Natural
0.0768 Language
0.0768 Processing
0.1233 spaCy
YAKE riconosce correttamente sia i bigrammi (Natural Language, Language Processing) che i termini singoli più rappresentativi (Python, spaCy), senza bisogno di alcun addestramento preventivo.
Keyword extraction con TF-IDF
Quando invece disponi già di una collezione di documenti (un archivio di articoli, uno storico di ticket), TF-IDF (Term Frequency - Inverse Document Frequency) è spesso la scelta migliore: premia i termini frequenti in un documento ma rari nella collezione, ossia quelli davvero distintivi per quel testo specifico.
from sklearn.feature_extraction.text import TfidfVectorizer
from nltk.corpus import stopwords
# Import una tantum: python -c "import nltk; nltk.download('stopwords')"
it_stopwords = stopwords.words("italian")
corpus = [
testo, # il testo su spaCy visto sopra
"Il machine learning e il deep learning sono tecniche di intelligenza artificiale.",
"La pizza napoletana è un piatto tipico della cucina italiana con pomodoro e mozzarella.",
]
vectorizer = TfidfVectorizer(
stop_words=it_stopwords,
ngram_range=(1, 2),
max_features=30,
)
tfidf_matrix = vectorizer.fit_transform(corpus)
feature_names = vectorizer.get_feature_names_out()
# Estraggo le keyword del primo documento (indice 0)
row = tfidf_matrix[0].toarray().flatten()
top_idx = row.argsort()[::-1][:8]
for i in top_idx:
if row[i] > 0:
print(f"{row[i]:.4f} {feature_names[i]}")
Output:
0.3879 testo
0.1940 funzionalità
0.1940 frasi
0.1940 frasi funzionalità
0.1940 fondamentale
0.1940 fondamentale costruire
0.1940 decine
0.1940 decine lingue
Il vantaggio dell’approccio TF-IDF è che è relativo alla collezione: se aggiungessimo altri dieci articoli su spaCy, il termine “testo” perderebbe rilevanza perché diventerebbe comune a molti documenti, mentre termini davvero specifici emergerebbero con più forza.
Confronto tra i due approcci
| Aspetto | YAKE | TF-IDF |
|---|---|---|
| Richiede un corpus di riferimento | No | Sì (più documenti sono, meglio è) |
| Setup | Minimo | Richiede vettorizzazione della collezione |
| Adatto a singolo documento isolato | Sì | Meno efficace |
| Adatto a corpus di articoli/ticket storici | Sì | Ottimo |
| Multilingua | Supporto nativo per molte lingue | Dipende dalle stopword disponibili |
| Costo computazionale | Basso | Basso-medio (dipende dalla dimensione del corpus) |
In pratica, ti conviene:
- usare YAKE quando devi processare documenti singoli, appena arrivati, senza un contesto storico (es. un nuovo ticket)
- usare TF-IDF quando hai uno storico consolidato e vuoi capire cosa distingue un documento dagli altri (es. tagging retroattivo di un blog)
Tagging automatico di articoli e ticket
Mettiamo insieme i due approcci in una funzione riutilizzabile, pensata per generare tag automatici su un archivio di contenuti (articoli di blog, ticket di supporto, documentazione interna):
from dataclasses import dataclass
import yake
@dataclass
class TaggedDocument:
id: str
title: str
tags: list[str]
def extract_tags(
doc_id: str,
title: str,
text: str,
lang: str = "it",
max_tags: int = 5,
) -> TaggedDocument:
"""
Genera automaticamente dei tag per un documento usando YAKE.
Utile per tagging retroattivo di articoli, ticket o documenti
che non hanno metadati.
"""
kw_extractor = yake.KeywordExtractor(lan=lang, n=2, top=max_tags * 2)
keywords = kw_extractor.extract_keywords(text)
# Ordino per rilevanza (score più basso = migliore) e prendo i primi N
ranked = sorted(keywords, key=lambda x: x[1])
tags = [kw.lower() for kw, _ in ranked[:max_tags]]
return TaggedDocument(id=doc_id, title=title, tags=tags)
# ── Demo su un piccolo archivio ─────────────────────────────────────────────
articoli = [
{
"id": "art-1",
"title": "Introduzione a spaCy",
"text": (
"spaCy è una libreria NLP open-source scritta in Python e Cython. "
"È progettata per essere veloce e scalabile in ambienti di produzione. "
"Supporta il sentence splitting, la tokenizzazione e il riconoscimento delle entità."
),
},
{
"id": "art-2",
"title": "BM25 per la ricerca lessicale",
"text": (
"BM25 è un algoritmo di ranking probabilistico usato dai motori di ricerca "
"per ordinare i documenti in base alla rilevanza lessicale rispetto a una query. "
"È molto più economico e veloce di una ricerca semantica basata su embedding."
),
},
]
for articolo in articoli:
risultato = extract_tags(articolo["id"], articolo["title"], articolo["text"])
print(f"[{risultato.id}] {risultato.title}")
print(f" tag suggeriti: {', '.join(risultato.tags)}")
print()
Output:
[art-1] Introduzione a spaCy
tag suggeriti: spacy, sentence splitting, riconoscimento delle, delle entità, entità supporta
[art-2] BM25 per la ricerca lessicale
tag suggeriti: bm25, ranking probabilistico, ricerca lessicale, motori di, di ricerca
Con poche righe di codice, senza nessuna chiamata a un modello esterno, hai un sistema di tagging automatico applicabile in batch a migliaia di documenti, in pochi secondi e a costo zero.
Consiglio pratico: se lavori su ticket di supporto, puoi combinare la keyword extraction con la classificazione degli intent (vedi l’articolo sull’intent detection) per ottenere sia il tema che l’azione richiesta dal cliente, senza mai passare i dati a un servizio esterno.












