Pipeline de extração, estruturação e visualização de editais de fomento. Acessa três fontes reais (FAPDF, FUNCAP, CAPES), extrai os PDFs dos editais, usa um LLM para estruturar as informações em campos padronizados e expõe os resultados via API REST + frontend React.
scrape → extrai PDF → LLM estrutura → valida Pydantic → LLM judge → JSON → API → React
| Camada | Tecnologia | Função |
|---|---|---|
| Scrapers | httpx + BS4 / Playwright / Firecrawl | Navegação e coleta de links |
| Extração | pdfplumber | Texto bruto dos PDFs |
| Estruturação | GPT-4o (fallback: Claude Sonnet) | Preenchimento dos campos padronizados |
| Avaliação | GPT-4o (LLM-as-a-judge) | Score de qualidade por campo |
| API | FastAPI + uvicorn | Endpoints REST |
| Frontend | React 18 + Vite + Tailwind | Visualização e dashboard |
| Fonte | Estratégia | Motivo |
|---|---|---|
| FAPDF | httpx + BeautifulSoup | WordPress estático com accordion JS |
| FUNCAP | httpx + BeautifulSoup | WordPress estático |
| CAPES | Firecrawl | Portal gov.br renderizado via JS |
- Python 3.13+
- Node.js 18+ (apenas para o frontend)
- Chaves de API: OpenAI, Anthropic (fallback), Firecrawl (scraper CAPES)
git clone <repo>
cd operation-public-notice
python -m venv .venv
# Windows:
.venv\Scripts\activate
# Linux/Mac:
source .venv/bin/activate
pip install -e ".[dev]"playwright install chromiumNecessário para o scraper da FAPDF (accordion JS).
Crie um arquivo .env na raiz do projeto:
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
FIRECRAWL_API_KEY=fc-...cd frontend
npm install
cd ..python main.pyO pipeline irá:
- Buscar editais nas três fontes configuradas
- Baixar e extrair o texto dos PDFs
- Estruturar cada edital via LLM (GPT-4o com fallback para Claude)
- Avaliar a qualidade da extração com um LLM judge
- Tentar correção automática se o score for inferior a 0.6
- Salvar os resultados em
output/
Os resultados parciais são escritos a cada edital processado — não é necessário aguardar o fim do pipeline para consultar os dados.
# Terminal 1 — API
uvicorn api.main:app --reload
# Terminal 2 — Frontend (desenvolvimento)
cd frontend && npm run devAcesse http://localhost:5173.
Para disparar o pipeline pela interface:
curl -X POST http://localhost:8000/pipeline/runOu clique em "Executar pipeline" no frontend.
Os resultados são salvos em output/:
Array de editais com os seguintes campos:
{
"id": "sha256(link_edital)[:12]",
"titulo": "string",
"orgao": "string",
"objetivo": "string | null",
"publico_alvo": ["string"],
"areas_tematica": ["string"],
"elegibilidade": "string | null",
"prazo_submissao": "string | null",
"valor_financiamento": "string | null",
"modalidade_fomento": "string | null",
"documentos_exigidos": ["string"],
"criterios_avaliacao": "string | null",
"cronograma": [{"evento": "string", "data": "string"}],
"link_edital": "string",
"link_pdf_principal": "string | null",
"links_anexos": ["string"],
"observacoes": "string | null",
"fonte": "fapdf | funcap | capes",
"extraido_em": "ISO datetime"
}Campos não encontrados retornam null — nunca string vazia.
Array de avaliações por edital, com score por campo (fidelidade + completude), score geral, flags determinísticas e rastreamento do modelo LLM utilizado.
GET /editais lista editais (filtros: ?fonte=, ?min_score=)
GET /editais/{id} edital + avaliação
GET /evaluation todas as avaliações
GET /evaluation/summary KPIs: score médio, uso de modelo, correções
GET /pipeline/status {"running": bool}
POST /pipeline/run dispara pipeline em background
pytest # testes unitários Python
cd frontend && npm test # testes do frontendOs testes de integração (scrapers reais) exigem acesso à internet e podem ser executados com:
pytest -m integrationCada fonte é um arquivo isolado que implementa BaseScraper. Adicionar uma nova fonte exige apenas criar o scraper e registrá-lo em config/sources.py — o main.py não precisa ser alterado.
O texto bruto do PDF é enviado ao GPT-4o com um schema fixo e instrução para retornar JSON puro. A resposta é validada com Pydantic. Campos ausentes retornam null.
Após a extração, um segundo LLM avalia campo a campo a fidelidade (o valor está no texto?) e a completude (alguma informação foi omitida?). Se o score geral for inferior a 0.6, o pipeline tenta uma correção automática com feedback dos campos problemáticos — uma única tentativa, sem loop.
Se o GPT-4o falhar ou atingir limite de taxa, o sistema faz fallback transparente para o Claude Sonnet sem interromper o pipeline.
O pipeline rastreia chamadas por minuto para cada provider e aguarda proativamente antes de estourar o limite, reduzindo erros 429. Configurável via LLMConfig.rpm_openai e LLMConfig.rpm_claude.
Textos acima de ~80k tokens (~320k caracteres) são truncados para as 15 primeiras páginas, com log obrigatório. O campo text_truncated na avaliação registra quando isso ocorreu.
Os JSONs em output/ são a fonte de verdade. A API lê os arquivos a cada request. Adequado para POC; substituível por banco relacional sem alterar scrapers ou extratores.
