15 KiB
PRD — Seleção determinística da biblioteca de extração de conteúdo
Versão: 1.0
Data: 20/08/2026
Status: Pronto para implementação
1. Contexto
Cada artigo é processado pelas bibliotecas Trafilatura, Newspaper4k e Readability a partir do mesmo HTML. O resultado é consolidado em um arquivo JSON que contém, para cada artigo, as saídas das três bibliotecas.
É necessário escolher deterministicamente uma única biblioteca por artigo. A escolha deve ocorrer mesmo quando todos os resultados forem ruins. O processamento não pode retornar estado ambíguo nem deixar um artigo sem seleção.
2. Objetivo
Receber um arquivo JSON no formato do arquivo de referência, selecionar a melhor saída de extração disponível para cada artigo e gerar um novo arquivo JSON com os mesmos dados, acrescentando somente a chave selected_extractor em cada item de articles.
3. Escopo
3.1 Incluído
- Ler um arquivo JSON com uma coleção
articles. - Comparar as saídas de Trafilatura, Newspaper4k e Readability de cada artigo.
- Escolher obrigatoriamente uma das três bibliotecas.
- Adicionar
selected_extractorem cada artigo. - Gerar um novo arquivo JSON.
- Preservar os dados e a ordem dos artigos recebidos.
3.2 Fora do escopo
- Baixar ou renderizar páginas.
- Verificar se as bibliotecas receberam o mesmo HTML.
- Executar novamente as bibliotecas de extração.
- Limpar, recortar, combinar ou reescrever o conteúdo extraído.
- Gerar Markdown.
- Usar LLM, embeddings ou regras específicas por domínio.
- Alterar qualquer campo existente no JSON.
- Adicionar métricas, justificativas ou outras chaves ao arquivo de saída.
4. Premissas
- As três bibliotecas processaram exatamente o mesmo HTML.
- A entrada segue a estrutura do JSON de referência.
- A seleção é executada individualmente para cada artigo.
- O algoritmo deve sempre produzir uma escolha, inclusive em situações sem concordância entre as bibliotecas.
5. Entrada
5.1 Arquivo
- Formato: JSON válido.
- A raiz deve conter
articlescomo uma lista. - Cada item de
articlesrepresenta um artigo.
5.2 Conteúdos comparados
| Biblioteca | Campo usado na comparação |
|---|---|
| Trafilatura | trafilatura.text |
| Newspaper4k | newspaper4k.text |
| Readability | readability.cleaned_text |
Os demais campos, incluindo título, descrição, resumo, palavras-chave, HTML estruturado e metadados, não participam da seleção.
5.3 Estado de um candidato
Para cada biblioteca, o candidato é classificado em um dos seguintes estados:
| Estado | Condição |
|---|---|
| Utilizável | Campo de conteúdo é uma string não vazia após normalização e error é nulo |
| Degradado | Campo de conteúdo é uma string não vazia após normalização, mas error não é nulo |
| Indisponível | Campo ausente, nulo, de tipo diferente de string ou vazio após normalização |
O campo de erro considerado é trafilatura.error, newspaper4k.error ou readability.error, conforme a biblioteca.
6. Saída
6.1 Arquivo
O arquivo de entrada nunca deve ser alterado. Deve ser criado um novo arquivo com o nome:
<nome_original_sem_extensão>_selected.json
Exemplo: river_plate_extracted(2).json gera river_plate_extracted(2)_selected.json.
6.2 Alteração permitida
Cada item de articles deve receber exatamente uma nova chave no mesmo nível de trafilatura, newspaper4k e readability:
selected_extractor
Valores permitidos:
trafilaturanewspaper4kreadability
Não são permitidos null, string vazia, ambiguous ou qualquer outro valor.
6.3 Preservação da entrada
- Todas as chaves e valores existentes devem permanecer semanticamente idênticos.
- A ordem dos itens de
articlesdeve ser preservada. - Nenhum artigo pode ser adicionado ou removido.
- Espaçamento, indentação e ordem textual das chaves do JSON não fazem parte do contrato, pois o arquivo pode ser serializado novamente.
- Caso
selected_extractorjá exista, seu valor deve ser recalculado e substituído.
7. Algoritmo determinístico de seleção
7.1 Formar o conjunto ativo
Para cada artigo:
- Identificar os candidatos utilizáveis.
- Se existir pelo menos um utilizável, considerar somente os utilizáveis.
- Se não existir utilizável, considerar os candidatos degradados.
- Se não existir candidato utilizável nem degradado, selecionar
newspaper4kpelo desempate final obrigatório. - Se o conjunto ativo possuir somente um candidato, selecioná-lo imediatamente.
7.2 Normalizar os conteúdos
A normalização serve apenas para comparação e não modifica o JSON de saída.
Para cada candidato ativo:
- Decodificar entidades HTML.
- Remover marcação HTML e Markdown, preservando o texto visível.
- Em links, preservar o texto e remover o endereço.
- Aplicar normalização Unicode NFKC.
- Converter o texto para minúsculas.
- Substituir toda sequência de espaços, tabulações ou quebras de linha por um único espaço.
- Tokenizar mantendo letras e números Unicode.
- Desconsiderar pontuação.
Nenhuma palavra ou trecho pode ser removido por interpretação semântica.
7.3 Gerar shingles
- Gerar a sequência ordenada de tokens de cada candidato.
- Formar o conjunto de todas as janelas consecutivas de cinco tokens.
- Quando o candidato possuir entre um e quatro tokens, usar a sequência completa como um único shingle.
- Candidato sem token é indisponível e não participa do conjunto ativo.
7.4 Construir o consenso
O consenso é o conjunto de shingles presentes em pelo menos dois candidatos ativos.
Para cada candidato ativo, calcular:
Cobertura:
coverage = quantidade de shingles do consenso presentes no candidato / quantidade total de shingles do consenso
Suporte:
support = quantidade de shingles do candidato presentes no consenso / quantidade total de shingles do candidato
Pontuação:
score = 2 × coverage × support / (coverage + support)
Quando coverage + support for zero, a pontuação será zero.
7.5 Selecionar quando existe consenso
- Ordenar os candidatos por
score, do maior para o menor. - Identificar o maior
score. - Considerar empate técnico todo candidato cuja diferença para o maior
scoreseja menor ou igual a0,03. - Se houver apenas um candidato no empate técnico, selecioná-lo.
- Se houver empate técnico, selecionar o candidato com a menor quantidade de shingles.
- Se a quantidade de shingles também empatar, aplicar a prioridade final:
newspaper4kreadabilitytrafilatura
A preferência pelo menor candidato ocorre somente no empate técnico. Nesse cenário, os candidatos possuem qualidade de concordância equivalente, e a decisão favorece menor conteúdo excedente.
7.6 Selecionar quando não existe consenso
Quando nenhum shingle aparece em pelo menos dois candidatos ativos:
- Com três candidatos ativos: selecionar o candidato com a quantidade mediana de shingles.
- Com dois candidatos ativos: selecionar o candidato com a maior quantidade de shingles.
- Com um candidato ativo: selecionar o único candidato.
- Em empate de quantidade: aplicar a prioridade
newspaper4k,readability,trafilatura. - Sem candidato ativo: selecionar
newspaper4k.
Essas regras garantem uma escolha mesmo quando não existe concordância textual.
7.7 Gravar a escolha
Adicionar ou substituir selected_extractor no artigo com o identificador da biblioteca vencedora. Repetir o processo até que todos os itens de articles tenham sido processados.
8. Requisitos funcionais
| ID | Requisito |
|---|---|
| FR-001 | O sistema deve aceitar um arquivo JSON como entrada. |
| FR-002 | O sistema deve validar que a raiz é um objeto e que articles é uma lista. |
| FR-003 | O sistema deve processar todos os artigos, preservando sua ordem. |
| FR-004 | O sistema deve usar exclusivamente os três campos de conteúdo definidos no PRD para calcular a escolha. |
| FR-005 | O sistema deve executar a normalização e a comparação conforme o algoritmo deste PRD. |
| FR-006 | O sistema deve selecionar exatamente uma biblioteca por artigo. |
| FR-007 | O sistema nunca deve produzir resultado ambíguo. |
| FR-008 | O sistema deve adicionar somente selected_extractor em cada artigo. |
| FR-009 | O valor de selected_extractor deve pertencer ao catálogo fechado de valores permitidos. |
| FR-010 | O sistema deve preservar todas as chaves e valores recebidos. |
| FR-011 | O sistema deve gerar um novo arquivo e manter o arquivo original inalterado. |
| FR-012 | O sistema deve produzir a mesma seleção sempre que receber exatamente a mesma entrada. |
| FR-013 | O sistema deve recalcular selected_extractor quando a chave já existir. |
9. Tratamento de erros
| Situação | Comportamento obrigatório |
|---|---|
| JSON inválido | Encerrar o processamento e não gerar arquivo de saída |
| Raiz diferente de objeto | Encerrar o processamento e não gerar arquivo de saída |
articles ausente ou diferente de lista |
Encerrar o processamento e não gerar arquivo de saída |
articles vazio |
Gerar arquivo com lista vazia e sem outras alterações |
| Estrutura de uma biblioteca ausente | Tratar seu candidato como indisponível |
| Campo de conteúdo com tipo inválido | Tratar seu candidato como indisponível |
| Todas as bibliotecas indisponíveis | Selecionar newspaper4k |
| Falha ao gravar o arquivo | Não deixar arquivo de saída parcialmente gravado |
Um erro em um artigo não pode impedir a seleção dos demais artigos, desde que o JSON e a lista articles sejam válidos.
10. Requisitos não funcionais
| ID | Requisito |
|---|---|
| NFR-001 | O processamento deve ser totalmente determinístico. |
| NFR-002 | O processamento não deve realizar chamadas de rede. |
| NFR-003 | O processamento não deve depender de LLM, embeddings ou serviços externos. |
| NFR-004 | O processamento deve operar somente sobre os dados do arquivo recebido. |
| NFR-005 | A gravação do arquivo deve ser atômica: sucesso completo ou ausência do arquivo de saída. |
11. Critérios de aceite
- Dado o JSON de referência com 20 artigos, o arquivo de saída contém os mesmos 20 artigos na mesma ordem.
- Cada artigo contém exatamente um
selected_extractorválido. - Nenhum artigo contém
selected_extractornulo, vazio ou ambíguo. - Todas as chaves e valores anteriores permanecem semanticamente idênticos.
- Nenhuma chave adicional, além de
selected_extractor, é criada. - O arquivo original permanece inalterado.
- Duas execuções sobre o mesmo arquivo produzem os mesmos valores de
selected_extractor. - A seleção usa somente
trafilatura.text,newspaper4k.textereadability.cleaned_text. - Quando todas as saídas estiverem vazias ou indisponíveis,
selected_extractorrecebenewspaper4k. - Quando não houver consenso, o desempate segue exatamente as regras da seção 7.6.
- Quando houver empate técnico, o desempate segue exatamente as regras da seção 7.5.
- Um JSON inválido ou sem
articlesválido não produz arquivo parcial.
12. Casos obrigatórios de teste
| Caso | Condição | Resultado esperado |
|---|---|---|
| CT-001 | Três candidatos com consenso e um vencedor claro | Selecionar o maior score |
| CT-002 | Dois ou mais candidatos dentro de 0,03 do maior score |
Selecionar o de menor quantidade de shingles |
| CT-003 | Empate técnico e mesma quantidade de shingles | Aplicar prioridade final |
| CT-004 | Três candidatos sem consenso | Selecionar a quantidade mediana de shingles |
| CT-005 | Dois candidatos sem consenso | Selecionar a maior quantidade de shingles |
| CT-006 | Somente um candidato utilizável | Selecionar esse candidato |
| CT-007 | Nenhum utilizável, mas existe candidato degradado | Executar o algoritmo somente com os degradados |
| CT-008 | Todos os candidatos indisponíveis | Selecionar newspaper4k |
| CT-009 | Readability retorna apenas um fragmento pequeno enquanto os outros concordam | O fragmento perde por baixa cobertura do consenso |
| CT-010 | Um candidato contém o conteúdo comum e muito conteúdo excedente | O candidato perde suporte e reduz sua pontuação |
| CT-011 | Um candidato contém somente parte do conteúdo comum | O candidato perde cobertura e reduz sua pontuação |
| CT-012 | A entrada já contém selected_extractor |
Recalcular e substituir somente essa chave |
| CT-013 | articles está vazio |
Gerar saída válida com articles vazio |
| CT-014 | JSON inválido | Não gerar saída |
13. Definition of Done
- Todos os requisitos funcionais foram implementados.
- Todos os casos obrigatórios de teste foram automatizados e aprovados.
- O JSON de referência com 20 artigos é processado integralmente.
- A saída contém somente a inclusão de
selected_extractorem cada artigo. - O arquivo original permanece inalterado.
- Execuções repetidas sobre a mesma entrada produzem as mesmas escolhas.
- Não existe caminho de execução que produza
ambiguous,nullou artigo sem seleção.