5.5 KiB
5.5 KiB
Data Model & State Transitions: Classificação e Roteamento de Notícias de Mídia
Feature: 007-media-article-routing
Date: 2026-08-24
Status: Completed
1. Diagrama Entidade-Relacionamento e Fluxo de Dados
flowchart TD
Input[InputArticle JSON] --> Crawler[Foxcape Crawler]
Crawler --> LoadedPage[DOM Carregada: html, title, status]
LoadedPage --> Gate[DOM Media Gate: detecção estrutural]
Gate -- "Sem mídia candidata relevante" --> TextPipeline[Multimotor Textual: Trafilatura + Newspaper4k + Readability]
Gate -- "Com mídia candidata relevante" --> CompactBuilder[Montagem de Payload Compacto]
CompactBuilder --> Classifier[Cadeia Sequencial LLM: Ollama -> Groq -> OmniRoute]
Classifier -- "content_type = text" --> TextPipeline
TextPipeline --> ExtractedRecord[ExtractedArticle: Sucesso Textual]
ExtractedRecord --> MainReport[ExtractionBatchReport -> *_extracted.json]
Classifier -- "content_type = media" --> MediaRecord[MediaArticle: Registro de Mídia]
MediaRecord --> MediaEnvelope[MediaOutputFile -> *_media.json]
Classifier -- "Falha total dos 3 provedores" --> FailureRecord[Registro de Falha de Classificação]
FailureRecord --> MainReport
2. Modelos de Dados (Dataclasses e Estruturas de Runtime)
2.1 InputArticle
Representa a notícia original carregada do JSON de entrada.
titulo: str(Obrigatório)url: str(Obrigatório)subtitulo: str | None(Opcional, preservado conforme entrada)quando_publicado: str | None(Opcional, preservado conforme entrada)pagina: int | None(Opcional, preservado conforme entrada)- Campos adicionais da entrada são preservados no dicionário
input_meta.
2.2 MediaCandidateInfo (Estrutura Interna do Gate)
Resultado da análise estrutural da DOM:
has_candidate_media: boolhas_video: boolimage_count: inthas_embed: bool
2.3 MediaClassification (Estrutura Interna de Saída do LLM)
Resultado estrito retornado pelo classificador LLM:
content_type: Literal["text", "media"]media_type: Literal["video", "image", "images", "embed", "mixed"] | None- Regra semântica:
content_type == "text"\iffmedia_type is None.
2.4 MediaArticle (Persistência em *_media.json)
Registro consolidado de publicação classificada como mídia:
input_meta: dict[str, Any](Preserva todos os metadados recebidos da entrada)crawled_url: strpage_title: str | Nonehttp_status: int | Nonecontent_type: Literal["media"]media_type: Literal["video", "image", "images", "embed", "mixed"]
2.5 ExtractedArticle (Persistência Textual em *_extracted.json)
Representa exclusivamente artigos textuais que efetivamente passaram pelo multimotor (extract_all_engines):
input_meta: InputArticleextraction_status: Literal["success", "failed"]error_message: str | Nonecrawled_url: strpage_title: str | Nonehttp_status: int | Nonetrafilatura: TrafilaturaData | Nonenewspaper4k: NewspaperData | Nonereadability: ReadabilityData | None
2.6 Registro de Falha de Classificação (Persistência Inline em *_extracted.json)
Para artigos que sofram falha operacional dos três provedores LLM, o registro é persistido inline no array articles do JSON principal sem ter executado os extratores textuais:
input_meta: dict[str, Any]crawled_url: strpage_title: str | Nonehttp_status: int | Noneclassification_status: Literal["failed"]error_message: strtrafilatura: Nonenewspaper4k: Nonereadability: None
2.7 ExtractionBatchReport
Envelope consolidado de saída gravado no arquivo principal (*_extracted.json):
source_file: strprocessed_at: str(ISO 8601 UTC)total_articles: int(Total de registros presentes no arrayarticlesdo JSON principal)successful_articles: int(Total de artigos textuais processados com sucesso)failed_articles: int(Total de registros de falha presentes, incluindo erros de crawl e de classificação)articles: list[dict[str, Any] | ExtractedArticle]
2.8 MediaOutputFile
Envelope mínimo gravado no arquivo de mídia (*_media.json):
articles: list[MediaArticle]
3. Máquina de Estados do Processamento de Artigo
stateDiagram-v2
[*] --> Crawling
Crawling --> CrawlFailed: Erro HTTP / Timeout Foxcape
CrawlFailed --> MainJSONRecord: Registra falha de crawl
Crawling --> StructuralInspection: DOM carregada com sucesso
StructuralInspection --> TextExtraction: Sem mídia candidata relevante
StructuralInspection --> LLMClassification: Mídia candidata relevante detectada
state LLMClassification {
[*] --> TryOllama
TryOllama --> ValidOutput: Resposta com schema válido
TryOllama --> TryGroq: Falha operacional Ollama
TryGroq --> ValidOutput: Resposta com schema válido
TryGroq --> TryOmniRoute: Falha operacional Groq
TryOmniRoute --> ValidOutput: Resposta com schema válido
TryOmniRoute --> AllProvidersFailed: Falha operacional OmniRoute
}
ValidOutput --> TextExtraction: content_type == text
ValidOutput --> MediaRouting: content_type == media
AllProvidersFailed --> MainJSONRecord: Registra classification_status = failed
TextExtraction --> MainJSONRecord: Executa Trafilatura + Newspaper + Readability
MediaRouting --> MediaJSONRecord: Grava em *_media.json
MainJSONRecord --> [*]
MediaJSONRecord --> [*]