feat(runtime): implement single-article consolidation runtime and modularize codebase

This commit is contained in:
2026-08-24 00:14:07 -03:00
parent e1e0be1353
commit 23de7d8fe7
176 changed files with 266754 additions and 10179 deletions
+208 -46
View File
@@ -42,6 +42,12 @@
- [Sanitização Editorial e Deduplicação](#sanitização-editorial-e-deduplicação)
- [Argumentos e Flags CLI](#argumentos-e-flags-cli-2)
- [Exemplos Práticos de Uso](#exemplos-práticos-de-uso-2)
- [6. Runtime de Consolidação e Higienização de Artigos (006-article-consolidation-runtime)](#6--runtime-de-consolidação-e-higienização-de-artigos-006-article-consolidation-runtime)
- [Visão Geral e Arquitetura](#visão-geral-e-arquitetura-do-runtime)
- [Configuração de Ambiente (.env)](#configuração-de-ambiente-env)
- [Comandos e Utilitários CLI](#comandos-e-utilitários-cli)
- [O que Esperar do Resultado (Artefatos Gerados)](#o-que-esperar-do-resultado-artefatos-gerados)
- [Contrato de Códigos de Saída (Exit Codes)](#contrato-de-códigos-de-saída-exit-codes)
- [Estrutura do Projeto](#-estrutura-do-projeto)
- [Testes e Qualidade de Código](#-testes-e-qualidade-de-código)
- [Licença](#-licença)
@@ -472,37 +478,205 @@ print(f"Markdown gerado em: {out_file}")
---
## 6. 🚀 Runtime de Consolidação e Higienização de Artigos (006-article-consolidation-runtime)
### Visão Geral e Arquitetura do Runtime
O **Runtime de Consolidação e Higienização Editorial de Artigos** é um pipeline de produção industrial de alta confiabilidade projetado para transformar a saída de extração tríplice de artigos em documentos editoriais finais em **Markdown limpo e estruturado com Manifesto JSON de auditoria completa**, operando estritamente sob **modelos baratos de IA (Groq / DeepSeek / OpenAI / Proxies)** e com **proibição total de expressões regulares (`zero-regex`)** em suas operações de texto.
```mermaid
flowchart TD
In[Artigo JSON + ECP Snapshot] --> Val[1. Validação Estrita de Schemas e Preflight]
Val --> FP[2. Cálculo de Fingerprint Determinístico SHA-256]
FP --> SQLClaim[3. Claim Atômico no SQLite WAL - BEGIN IMMEDIATE]
SQLClaim --> Shingles[4. Parser de Candidatos e Mapeamento de Equivalências sem Regex]
Shingles --> HygLLM[5. Higienização Extrativa por LLM - Grounding por IDs de Blocos]
HygLLM --> RepVal[6. Validador de Reparos Textuais Restritos - Mojibake/Typos/Espaçamento]
RepVal --> ECPGate{7. Gate Obrigatório de ECP}
ECPGate -- Não Inerente / Tangencial --> RejManifest[Grava Manifesto rejected_ecp - Zero Markdown]
ECPGate -- Inerente DIRECT/CONTEXTUAL --> EnrichLLM[8. Enriquecimento LLM - Sentimento e Tags]
EnrichLLM --> AtomPersist[9. Persistência Atômica temp + os.replace]
AtomPersist --> OutMD[10. Markdown com YAML Front-matter + Manifesto .result.json]
OutMD --> SQLiteDone[11. Atualização de Estado completed_text no SQLite]
```
### Configuração de Ambiente (`.env`)
Crie ou edite o arquivo `.env` na raiz do projeto com suas credenciais:
```bash
# Provedor Padrão (OpenAI / Omniroute / Proxy Customizado)
OPENAI_API_KEY="sk-..."
OPENAI_BASE_URL="https://omniroute.app.andreferraro.com/v1"
OPENAI_MODEL="cgpt-web/gpt-5.5"
# Ou Provedores Nativos Específicos
GROQ_API_KEY="gsk_..."
DEEPSEEK_API_KEY="sk-..."
# Observabilidade (Opcional - Degrada para fila SQLite local se offline)
LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_HOST="https://cloud.langfuse.com"
```
---
### Comandos e Utilitários CLI
O runtime expõe **5 utilitários CLI normativos**:
#### 1. Consolidação de Artigo Único (`consolidate.py`)
Executa a consolidação de ponta a ponta de um artigo contra um perfil de entidade (ECP):
```bash
python src/runtime/cli/consolidate.py \
--config runtime_config.local.json \
--article examples/sample_article_valid.json \
--ecp examples/sample_ecp_snapshot.json
```
#### 2. Certificação de Pré-Voo (`preflight.py`)
Valida permissões de disco, banco de dados SQLite, prompts, hashes e integridade do ambiente antes do início das operações:
```bash
python src/runtime/cli/preflight.py --config runtime_config.local.json
```
#### 3. Teste de Fumaça (`smoke.py`)
Valida rapidamente o funcionamento do pipeline completo com dados de exemplo locais:
```bash
python src/runtime/cli/smoke.py --config runtime_config.local.json
```
#### 4. Reconciliação e Recuperação de Quedas (`reconcile.py`)
Detecta artefatos gravados em disco e reconcilia o estado do banco SQLite após reinicializações ou crashes do processo:
```bash
# Auditoria e sincronização de estados divergentes
python src/runtime/cli/reconcile.py --config runtime_config.local.json
# Reconciliação com limpeza de arquivos temporários órfãos (.tmp_*)
python src/runtime/cli/reconcile.py --config runtime_config.local.json --cleanup-orphans
```
#### 5. Despejo de Telemetria Operacional (`telemetry_flush.py`)
Efetua o flush em lote de eventos e métricas enfileirados localmente no SQLite quando a conexão com o Langfuse foi restabelecida:
```bash
python src/runtime/cli/telemetry_flush.py --config runtime_config.local.json --batch-size 50
```
---
### O que Esperar do Resultado (Artefatos Gerados)
Para cada artigo processado, o runtime grava seus artefatos no diretório configurado (ex: `out/articles/`):
#### 1. Documento Markdown Higienizado (`<fingerprint>.md`)
Quando o artigo é classificado como inerente (`DIRECT_INHERENT` ou `CONTEXTUAL_INHERENT`), um arquivo Markdown padronizado é gerado com **YAML Front-Matter** estrito e corpo textual higienizado:
```markdown
---
title: "Los puntajes de River vs. Independiente Santa Fe"
fingerprint: "c987f362b35e76239e3fda0841c9e5f89c3d4193760738e6a2e9424ce45fde88"
source_url: "https://www.tycsports.com/river-plate/los-puntajes-de-river-vs-independiente-santa-fe.html"
sentiment: "positive"
---
# Los puntajes de River vs. Independiente Santa Fe
River Plate empató sin goles ante Independiente Santa Fe en el estadio El Campín de Bogotá por la ida de los octavos de final de la Copa Sudamericana.
El equipo de Marcelo Gallardo resistió la presión del conjunto colombiano y definirá la serie la próxima semana en el estadio Monumental de Buenos Aires.
```
#### 2. Manifesto de Auditoria e Resultado (`<fingerprint>.result.json`)
Contém a auditoria completa de hashes, tokens, latência, custos, status ECP e metadados:
```json
{
"schema_version": "1.0.0",
"fingerprint": "c987f362b35e76239e3fda0841c9e5f89c3d4193760738e6a2e9424ce45fde88",
"source_url": "https://www.tycsports.com/river-plate/los-puntajes-de-river-vs-independiente-santa-fe.html",
"selected_extractor": "trafilatura",
"final_status": "completed_text",
"generate_markdown": true,
"markdown_path": "out/articles/c987f362b35e76239e3fda0841c9e5f89c3d4193760738e6a2e9424ce45fde88.md",
"markdown_hash": "b6b21b19f10feab12525030016a3eeb4ed702cdec6d39c91fc42289b65091e0e",
"ecp_classification": {
"category": "DIRECT_INHERENT",
"is_inherent": true,
"confidence": 0.98,
"rationale": "Direct match of target entity 'Club Atlético River Plate' with strong contextual anchor density (4 anchor(s) matched)."
},
"enrichment": {
"sentiment": "positive",
"tags": ["river plate", "copa sudamericana", "futebol"]
},
"config_version": "1.0.0",
"error_codes": []
}
```
> **Nota sobre Artigos Não Inerentes**: Caso o artigo seja rejeitado pelo ECP (`TANGENTIAL` ou `NOT_RELATED`), o arquivo `.md` **não é gerado** (zero bytes de lixo editorial) e o manifesto `.result.json` é gravado com `final_status: "rejected_ecp"` e `generate_markdown: false`.
#### 3. Rastreamento e Estado no Banco SQLite (`out/runtime.db`)
O banco SQLite opera em modo **WAL** com transações imediatas para prevenir concorrência e deadlocks, registrando o histórico de transições de status (`received` → `claimed` → `hygiene_running` → `ecp_evaluated` → `enrichment_running` → `completed_text` / `rejected_ecp`).
---
### Contrato de Códigos de Saída (Exit Codes)
O CLI segue estritamente os códigos de saída normativos:
| Código | Significado | Descrição |
| :---: | :--- | :--- |
| `0` | **Sucesso** | Processamento completado com sucesso (artigo consolidado ou rejeitado com manifesto válido). |
| `1` | **Erro de Contrato / Input** | JSON de entrada inválido, schema corrompido ou argumentos ausentes. |
| `2` | **Erro de Preflight / Config** | Arquivo de configuração ausente, chave de API inexistente ou modelo não certificado. |
| `3` | **Erro de Gateway / Fallback** | Provedor primário e fallback falharam simultaneamente sem recuperação determinística. |
| `4` | **Erro Fatal de I/O** | Disco inacessível, falha de integridade SHA-256 ou corrupção de persistência atômica. |
---
## 📁 Estrutura do Projeto
```text
TextNLPClassifierApp/
├── classify.py # CLI principal do Classificador de Inerência
├── runtime_config.local.json # Configuração canônica do Runtime (roles, modelos, pricing)
├── .env # Chaves de API e URLs locais (gitignored)
├── classify.py # CLI legado do Classificador de Inerência
├── scripts/
│ ├── __init__.py # Pacote utilitário de scripts
│ ├── ci_check.py # Pipeline unificado de validação estática e CI
│ ├── extract_google_news.py # CLI de Extração de Manchetes do Google News
│ ├── extract_article_contents.py # CLI de Extração e Parsing Multimotor de Artigos
│ ├── select_article_extractor.py # CLI de Seleção Determinística de Extrator
│ └── convert_article_to_markdown.py # CLI de Conversão de Artigo JSON para Markdown
├── src/ # Módulos centrais do classificador
│ ├── classifier.py # Orquestrador de classificação (Tier 1, 2, 3)
│ ├── models.py # Modelos de dados e esquemas (ECPSnapshot, Decision)
│ ├── preprocessor.py # Normalização de texto e detecção de idioma
│ └── adapters/ # Adaptadores opcionais de Embeddings e LLM
├── specs/ # Especificações e planos arquiteturais (Speckit)
│ ├── 001-multilingual-entity-classifier/
│ ├── 002-google-news-extractor/
│ ├── 003-article-content-extractor/
│ ├── 004-deterministic-content-selection/
│ └── 005-convert-json-markdown/ # Specs da conversão JSON para Markdown
├── tests/ # Suíte de testes automatizados
│ ├── test_classifier.py
│ ├── test_extract_google_news.py
│ ├── test_extract_article_contents.py
│ ├── test_select_article_extractor.py
│ ├── test_convert_article_to_markdown.py # Testes da conversão para Markdown
│ ├── test_llm_fallback.py # Testes do Tier 3 LLM Fallback
│ ├── test_e2e_text_analysis_pipeline.py # Suíte E2E do Funil de Análise e Fallback
│ └── test_classify_exhaustive_suite.py # Suíte Exaustiva de Casos Felizes/Infelizes (QA Sênior)
├── src/
│ ├── runtime/ # MÓDULOS CENTRAIS DO RUNTIME DE CONSOLIDAÇÃO
│ │ ├── candidate/ # Parser de candidatos, shingles e normalização zero-regex
│ │ ├── cli/ # CLIs: consolidate, preflight, smoke, reconcile, flush
│ │ ├── core/ # Configs, contratos, fingerprint SHA-256 e limites
│ │ ├── ecp/ # Adapter de decisão de inerência e schema ECP
│ │ ├── enrichment/ # Harness de enriquecimento (sentimento e tags)
│ │ ├── gateway/ # Gateway agnóstico (Groq, DeepSeek, OpenAI) com failover
│ │ ├── hygiene/ # Harness de higienização LLM e grounding por IDs
│ │ ├── observability/ # Logging estruturado e tracer Langfuse com fila offline
│ │ ├── quality/ # Validador de reparos textuais restritos (mojibake/typos)
│ │ └── storage/ # Persistência atômica (file_store) e SQLite WAL
│ └── tools/ # Módulos e extratores legados (classifier, language, parser)
├── specs/ # Especificações Speckit (001 a 006)
│ └── 006-article-consolidation-runtime/ # Especificação técnica completa do runtime
├── docs/structured_extraction/ # Documentação arquitetural (PRD, ADRs, Test Plan, Runbook)
├── tests/
│ ├── runtime/ # SUÍTE DO RUNTIME (72 testes)
│ │ ├── contract/ # Validação de schemas e contratos
│ │ ├── fault_injection/ # Falhas HTTP 429, 500, JSON corrompido
│ │ ├── integration/ # Concorrência (8 workers), Reconciliação, CLI subprocess, E2E Real
│ │ ├── load/ # Benchmark de sustentação de carga (100 art/h)
│ │ ├── quality/ # Orçamento de custo, Golden Set 20, Zero Regex scanner
│ │ ├── security/ # Injeção de prompt e redação de secrets
│ │ └── unit/ # Testes unitários dos módulos internos
│ ├── tools/ # SUÍTE DE TOOLS LEGADAS (247 testes)
│ └── scripts/check_zero_regex.py # Auditor estático de AST garantindo Zero Regex
├── requirements.txt # Dependências do projeto
├── pyproject.toml # Configurações de ferramentas (pytest, ruff, mypy)
└── README.md # Documentação principal
@@ -512,38 +686,26 @@ TextNLPClassifierApp/
## 🧪 Testes e Qualidade de Código
O repositório possui **247 testes automatizados** com 100% de aprovação cobrindo testes unitários, de regressão, de integração, Golden Fixtures exatas, testes de sensibilidade de mutação, testes de fallback para LLM (Tier 3), validações de degradação graciosa, matriz multilíngue e testes End-to-End (E2E) via CLI subprocess:
O repositório possui **319 testes automatizados** com **100% de aprovação**:
```bash
# Executar toda a suíte de testes do projeto (247 testes)
pytest -v
# 1. Executar a Verificação Completa do CI (Validação Estática Zero-Regex + 319 Testes)
python scripts/ci_check.py
# Executar a Suíte Exaustiva de Classificação e Fallback (38 testes)
pytest tests/test_classify_exhaustive_suite.py -v
# 2. Executar apenas a Suíte do Runtime (72 testes)
pytest tests/runtime -v
# Executar a Suíte E2E do Funil de Análise de Texto e Fallback para LLM
pytest tests/test_e2e_text_analysis_pipeline.py -v
# 3. Executar o Teste E2E Real contra API ao vivo
pytest tests/runtime/integration/test_live_e2e_real_api.py -v -s
# Executar os testes do Fallback para LLM (Tier 3)
pytest tests/test_llm_fallback.py -v
# 4. Executar o Teste de Concorrência de Alta Contenção (8 workers paralelos simultâneos)
pytest tests/runtime/integration/test_concurrency_claims.py -v
# Executar os testes de Conversão de Artigo para Markdown (67 testes)
pytest tests/test_convert_article_to_markdown.py -v
# 5. Executar apenas a Suíte de Ferramentas Legadas (247 testes)
pytest tests/tools -v
# Executar os testes do Seletor Determinístico
pytest tests/test_select_article_extractor.py -v
# Executar os testes do Extrator de Conteúdo Multimotor
pytest tests/test_extract_article_contents.py -v
# Executar os testes do Extrator do Google News
pytest tests/test_extract_google_news.py -v
# Validação com Ruff
ruff check .
# Verificação estática de tipos com Mypy
mypy src/ scripts/ tests/
# 6. Auditoria Estática de Proibição Absoluta de Regex
python tests/scripts/check_zero_regex.py
```
---