Deduplicación: Jaccard, clustering y circuit breaker de contenido
Con fuentes heterogéneas ingiriendo las mismas noticias desde ángulos distintos, la deduplicación es crítica para no saturar la base de datos ni generar alertas duplicadas. El pipeline usa dos mecanismos complementarios.
Deduplicación por título (Jaccard)
Antes de guardar un documento, se compara su título con los títulos ya procesados en la misma ejecución del pipeline usando similitud de Jaccard sobre tokens de 4+ caracteres, excluyendo palabras vacías:
def _is_near_duplicate(title: str, seen: list[frozenset], threshold=0.6) -> bool:
tokens = _tokenize_for_jaccard(title)
return any(_jaccard_sim(tokens, s) > threshold for s in seen)
Un threshold de 0.6 permite que titulares con ligeras variaciones (añadido de fecha, nombre de medio) se identifiquen como duplicados sin eliminar noticias genuinamente distintas sobre el mismo tema.
Clustering (WM-8)
Además de la deduplicación, el pipeline asigna un cluster_id a documentos relacionados. Documentos del mismo cluster no son duplicados exactos sino cobertura del mismo evento desde fuentes distintas:
def _cluster_documents(docs, threshold=0.5):
# Jaccard incremental: cada documento se compara contra
# el centroide de cada cluster existente.
# Si la similitud supera el threshold, entra en ese cluster.
# Si no, crea un cluster nuevo.
...
El campo cluster_size indica cuántos documentos comparten cluster, lo que sirve como proxy de la relevancia de un evento: cuanto mayor el cluster, más fuentes lo están cubriendo.