judex-mini is a Python scraper and parser for Brazilian Supreme Court (STF) process data. It is built HTTP-first. The project is organized as a Typer-based CLI (judex) with three pipeline stages — varrer-processos (case JSON scrape), baixar-pecas (PDF byte download), and extrair-pecas (text extraction with pluggable OCR providers) — feeding a DuckDB warehouse rebuilt by atualizar-warehouse. It supports highly sharded runs with proxy rotation and a content-addressed cache separates raw bytes from extracted text so re-OCR is decoupled from re-download.
Extração automatizada de dados de processos do STF (Supremo Tribunal Federal). Você passa uma classe (HC, ADI, RE, AI, …) e um intervalo de números de processo; o programa acessa o portal do STF, extrai metadados de cada processo (partes, andamentos, relator, decisão, URLs dos PDFs anexados) e grava um arquivo .json por processo. Se quiser o texto dos PDFs também, há dois comandos dedicados (baixar-pecas + extrair-pecas) — descritos na seção 5.
Glossário rápido. Processo é cada caso julgado pelo STF — o
judex-minigrava um JSON por processo com partes, andamentos, relator, decisão e URLs dos PDFs anexados. Peça é cada um desses PDFs (decisão monocrática, voto, manifestação da PGR, acórdão, etc.); o cache mantém bytes (.pdf.gz) e texto extraído (.txt.gz) lado a lado, identificados porsha1(url).
Este README é o guia prático para rodar a ferramenta. Detalhes de arquitetura, testes e convenções para quem contribui com o código estão em CLAUDE.md.
Subcomandos principais disponíveis via uv run judex <comando> (ajuda detalhada com --help):
| Comando | O que faz |
|---|---|
varrer-processos |
Raspa metadados de processos do STF (partes, andamentos, PDFs, etc.). |
baixar-pecas |
Baixa os bytes dos PDFs anexados para o cache local. |
extrair-pecas |
Extrai texto dos PDFs em cache (pypdf / mistral / chandra / unstructured). |
atualizar-warehouse |
Reconstrói o DuckDB analítico a partir dos JSONs + cache (zero HTTP). |
fazer-backup |
Empacota data/source/processos + data/raw/pecas + data/derived/pecas-texto em um único .zip (Windows-friendly). |
relatorio-diario |
Relatório diário de novas distribuições (watchlist opcional para diffs). |
Do zero a um warehouse consultável em SQL, cinco passos em ordem. Os dois primeiros falam com o STF; os três seguintes são locais (zero HTTP).
1. varrer-processos ← HTTP (portal.stf.jus.br)
2. baixar-pecas ← HTTP (sistemas.stf.jus.br)
3. extrair-pecas ← local (lê bytes do cache, escreve texto)
4. aggregate_unallocated_pids ← local (compila sweep.state.json → registro)
5. atualizar-warehouse ← local (JSONs + cache → DuckDB)
| # | Comando | Pule se… |
|---|---|---|
| 1 | judex varrer-processos |
nunca — é a base de tudo |
| 2 | judex baixar-pecas |
só quer metadados |
| 3 | judex extrair-pecas |
baixou bytes mas o texto não importa |
| 4 | uv run python scripts/aggregate_unallocated_pids.py --classe HC |
sweep pequeno; em sweeps de milhares, rode entre 1 e 5 — no próximo sweep passe --excluir-nao-alocados data/derived/nao-alocados/HC.txt para pular IDs que o portal nunca atribuiu a um processo |
| 5 | judex atualizar-warehouse |
consulta os JSONs direto (raro) |
O passo 4 agrega observações status="unallocated" (o sinal canônico do STF de "este processo_id nunca foi alocado a um incidente") de todos os sweeps já feitos e grava em data/derived/nao-alocados/<classe>.txt os IDs confirmados (≥ 2 observações independentes). A tabela <classe>.candidates.tsv ao lado guarda a auditoria completa. Veja ADR-0002.
Exemplo concreto para 2026 (HC, ids 267138..271138):
# 1. Metadados — sharded com rotação de proxy (~60 min em 8 shards)
uv run judex varrer-processos -c HC -i 267138 -f 271138 \
--rotulo hc_2026 --saida runs/active/hc-2026 \
--diretorio-itens data/source/processos/HC \
--shards 8 --proxy-pool config/proxies
# 2. Bytes dos PDFs — sharded (~30 min)
uv run judex baixar-pecas -c HC -i 267138 -f 271138 \
--saida runs/active/hc-2026-bytes --nao-perguntar \
--shards 8 --proxy-pool config/proxies
# 3. Texto via pypdf, zero HTTP (~10 min)
uv run judex extrair-pecas -c HC -i 267138 -f 271138 \
--saida runs/active/hc-2026-text --nao-perguntar
# 4. Agrega IDs não-alocados de todos os sweeps já feitos (~5 s, local)
uv run python scripts/aggregate_unallocated_pids.py --classe HC
# → data/derived/nao-alocados/HC.txt + HC.candidates.tsv
# 5. Rebuild do warehouse (só 2026, swap atômico, ~5 s)
uv run judex atualizar-warehouse --ano 2026 --classe HC \
--saida data/derived/warehouse/judex-2026.duckdb- Retry automático em 403 — backoff exponencial via tenacity; o WAF do STF usa 403 (não 429) como sinal de throttle, e a janela abre em minutos.
- Rotação proativa de proxy — troca de IP antes do WAF endurecer; cada proxy tem janela ativa e cooldown alinhados à memória do WAF.
- Sweeps shardeados — particiona um CSV em N processos paralelos, um pool de proxy por shard, arquivo de PIDs para monitor/stop.
- Disjuntor (circuit breaker) — janela rolante das últimas raspagens; quando a fração de falhas ultrapassa o limiar, o sweep para limpo (escreve estado e sai).
- Classificação de regime WAF —
CliffDetectoracompanha em tempo realhealthy→approaching_collapse→engaged→collapse, e rotaciona preventivamente. - Retomada atômica —
sweep.state.json+ arquivos por processo com renomeação atômica;--retomarpula o que já deuok. - Retentar só as falhas —
--retentar-de sweep.errors.jsonlpara re-atacar apenas os processos que falharam. - Cache de PDF versionado por provedor — sidecar
.extractorpermite trocar OCR (pypdf → mistral → chandra) sem rebaixar bytes. - Prévia de custo —
--dry-runemextrair-pecasestima páginas, USD e tempo antes de queimar chave de API. - Validação contra gabarito — fixtures hand-verified em
tests/ground_truth/para evitar regressões em extractors. - Watchlist no relatório diário — lista de processos monitorados com diff estruturado por rodada.
O projeto só roda em Linux / macOS / WSL. No Windows você precisa do WSL (Windows Subsystem for Linux); Linux e macOS rodam direto no terminal.
Você vai precisar de três coisas:
- Um terminal Linux (Ubuntu via WSL no Windows, Terminal no macOS, seu emulador preferido no Linux).
- O gerenciador Python
uv— instalado abaixo, não precisa instalar Python antes. git(geralmente já vem instalado; se não,sudo apt install git).
Atenção Windows: toda a execução acontece dentro do WSL, nunca no PowerShell nem no CMD. Depois de instalar o WSL, abra o app "Ubuntu" no menu iniciar e rode os comandos lá.
Alternativa rápida —
pip(requer Python 3.10+ já instalado): se você já tem Python no sistema e quer só rodar ojudexCLI sem clonar o repositório,pip install git+https://github.com/noah-art3mis/judex-mini— depois pule direto para a § 3. Para análise (notebooks Marimo, plots), usepip install "git+https://github.com/noah-art3mis/judex-mini#egg=judex-mini[analysis]". O resto deste guia supõe instalação viauv(receita abaixo), que é o caminho recomendado para desenvolvimento e sweeps grandes.
No PowerShell como administrador:
wsl --installReinicie o computador quando pedir. Depois abra o app Ubuntu que apareceu no menu iniciar e crie um usuário/senha quando ele pedir. A partir daí, todos os comandos deste guia são digitados dentro do Ubuntu.
No terminal do Ubuntu (ou macOS/Linux):
curl -LsSf https://astral.sh/uv/install.sh | shDepois de instalar, feche e reabra o terminal — senão o comando uv não vai ser encontrado. Para confirmar que funcionou:
uv --versionSe aparecer algo como uv 0.x.y, está tudo certo. Se der command not found, veja Problemas comuns.
git clone https://github.com/noah-art3mis/judex-mini
cd judex-miniAinda dentro da pasta judex-mini:
uv syncIsso baixa a versão certa do Python e todas as bibliotecas. Só precisa ser feito uma vez (e a cada git pull que mexa nas dependências).
Para conferir que ficou tudo ok:
uv run judex --helpDeve aparecer o menu de subcomandos (varrer-processos, baixar-pecas, extrair-pecas, atualizar-warehouse, exportar, fazer-backup, relatorio-diario, probe, analisar-regimes, validar-gabarito). Para ver as opções de cada um: uv run judex <comando> --help. (O comando longo uv run python main.py … também funciona — é apenas um atalho para o mesmo hub.)
Comando mínimo para baixar um único processo (ex.: HC 135041):
uv run judex varrer-processos -c HC -i 135041 -f 135041O que cada pedaço significa:
| Flag | Significa | Exemplo |
|---|---|---|
-c |
classe processual | HC, ADI, RE, AI |
-i |
número inicial | 135041 |
-f |
número final | 135041 |
Se tudo deu certo, o arquivo do processo fica em runs/coletas/<timestamp>-<rótulo>/items/judex-mini_HC_135041-135041.json. O timestamp e o rótulo são inferidos quando você passa só -c/-i/-f; para escolher, use --saida caminho/ e --rotulo meu-nome.
Baixar um intervalo de processos (ex.: HC 135041 a 135050):
uv run judex varrer-processos -c HC -i 135041 -f 135050Salvar em uma pasta específica:
uv run judex varrer-processos -c HC -i 135041 -f 135041 \
--saida runs/coletas/meu_testeRefazer uma extração: cada execução grava em runs/coletas/<timestamp>-<rótulo>/, então reusos não se atropelam — é só rodar de novo. Para retomar uma varredura interrompida, aponte a mesma --saida e passe --retomar (só os processos que ainda não deram ok são rerodados).
Salvar direto no Desktop do Windows (só WSL):
uv run judex varrer-processos -c HC -i 135041 -f 135041 \
--saida /mnt/c/Users/SeuUsuario/Desktop/judex-miniSubstitua SeuUsuario pelo seu nome de usuário do Windows. Os arquivos aparecem no Desktop como se tivessem sido gerados pelo próprio Windows.
Ver todas as opções:
uv run judex --helpPara sweeps de produção, judex executar substitui a antiga cadeia de três comandos por um único processo: três asyncio.Pools concorrentes (portal, sistemas, ocr) que se alimentam mutuamente, com estado retomável e relatório consolidado. Mesma cobertura, uma invocação só, métricas de pipelining no report.md.
Um intervalo de HCs em IP direto, OCR via pypdf (extrai a camada de texto, sem custo). Cabe num único processo; o WAF do STF tolera o ritmo serial bem.
uv run judex executar -c HC -i 250920 -f 267137 \
--saida runs/active/hc2025-fillin-$(date +%Y%m%d)/ \
--nao-perguntar- Custo: $0,00 (sem proxy, sem OCR pago).
- Wall esperado: ~30–60 min se o cache estiver frio (ano inteiro de HCs); ~2 min se já tiver passado uma vez (cache hit em todas as três etapas).
- Quando usar: ranges pequenos (algumas dezenas a poucos milhares), passes de verificação sobre dados já capturados, ambientes de dev sem proxy contratado.
- Monitor:
uv run judex acompanhar runs/active/hc2025-fillin-<data>/(auto-detecta layout monolítico vs. shardeado, faz tail-with-headers).
Para sweeps em escala (uma classe inteira, ano-ladders), o trio é: shards (paralelismo via processos) + proxies (rotação de IP para escapar do WAF) + --provedor auto (roteia ACÓRDÃOs para tesseract_fly e o resto para pypdf local — pago só onde precisa).
# Pré-requisitos uma vez:
# - Pool de proxies em config/proxies (uma URL por linha)
# - App Fly de OCR rodando (ver docs/setup-fly.md)
# - Variáveis de ambiente apontando para o app Fly
export PATH="$HOME/.fly/bin:$PATH"
export FLY_TESSERACT_URL=https://judex-ocr-tesseract-arcos.fly.dev/extract
export JUDEX_AUTO_TESSERACT_PROVIDER=tesseract_fly
# Pré-aquece o cluster Fly (elimina storm de 502 no início).
# ~$0.002 por pulse; auto-stop devolve as Machines a $0 após ~5 min.
fly machine start --select -a judex-ocr-tesseract-arcos || true
sleep 15
# Sweep shardeado: 16 processos paralelos, um pool de proxy por shard,
# OCR auto-roteado (~91% pypdf / ~9% tesseract_fly em corpora de HC).
uv run judex executar -c HC -i 250920 -f 267137 \
--saida runs/active/hc2025-completo-$(date +%Y%m%d)/ \
--rotulo hc2025_completo \
--shards 16 --proxy-pool config/proxies \
--provedor auto \
--ocr-concurrencia 10 \
--nao-perguntar- Custo estimado (ano inteiro de HCs ≈ 14 k cases / 19 k peças):
- Proxies (varrer): ~660 MB × $3,65/GB ≈ $2,35
- Proxies (baixar): ~3,15 GB × $3,65/GB ≈ $11,49
- OCR Fly (auto): ~9% das peças OCR'd, ≈ 1,7 k páginas × $0,005/1k ≈ $0,01
- Total ≈ $14 por ano de HC (re-anchore com
--preverantes de cada lançamento)
- Wall esperado: ~2,5 h (varrer ~38 min + baixar ~1,3 h + extrair ~32 min, pipelinados).
- Por que
--ocr-concurrencia 10e não 60: o número 60 era pra o cluster sempre-quente (Modal). Com Fly emmin_machines_running=0(Machines ligam sob demanda), 10 chamadas paralelas casam com o ritmo de wake-on-request — acima disso, o proxy Fly devolve 502 enquanto Machines ainda aquecem. Detalhes emdocs/setup-fly.md. - Monitor:
uv run judex acompanhar runs/active/hc2025-completo-<data>/interleva os logs dos 16 shards.pgrep -af hc2025_completo_shard_confirma quantos seguem vivos. Para parar limpo:xargs -a runs/active/hc2025-completo-<data>/shards.pids kill -TERM. - Antes de lançar: sempre
--preverpara ver o painel de custo + tempo (uv run judex executar … --prever). Re-anchore as constantes emjudex/utils/cost.pyse o corpus dobrou desde a última calibragem.
Para o roteador auto em si (regras de classificação ACÓRDÃO vs. demais, fold-over para pypdf quando o Fly cai), ver o docstring de judex/sweeps/peca_classification.py; empíricos do trade-off de custo/qualidade entre provedores em docs/reports/2026-04-30-ocr-bakeoff.md.
O varrer-processos já coleta os metadados de cada processo, incluindo as URLs dos PDFs anexados (decisões, acórdãos, manifestações da PGR). Quando você quer o texto desses PDFs também, rode dois comandos em sequência — eles são independentes de propósito:
baixar-pecas— baixa os bytes das peças paradata/raw/pecas/<hash>.<ext>.gz(.pdf.gzpara PDFs,.rtf.gzpara RTFs etc. — a chave ésha1(url), agnóstica ao formato). É a única parte que volta a falar com o STF, mas vai para um domínio diferente (sistemas.stf.jus.br, nãoportal.stf.jus.br), com seu próprio orçamento de taxa — na prática, 403 aqui é bem mais raro que novarrer-processos. Aceita--proxy-poole tem modo shardeado (--shards) para sweeps grandes.extrair-pecas --provedor <X>— lê os bytes do disco e extrai o texto via o provedor escolhido. Nenhuma chamada ao STF. Provedores suportados:pypdf(local, grátis, camada de texto; padrão),mistral,chandra,unstructured(OCR pagos; exigem chave de API).
Exemplo típico para os HCs que você acabou de varrer:
# 1) Baixa os bytes (uma vez por URL — respeita o --forcar)
uv run judex baixar-pecas -c HC -i 135041 -f 135041 --nao-perguntar
# 2) Extrai o texto com pypdf (rápido, grátis, zero HTTP)
uv run judex extrair-pecas -c HC -i 135041 -f 135041 --nao-perguntar
# Depois, re-extrair com OCR melhor (sem baixar de novo):
export MISTRAL_API_KEY="..."
uv run judex extrair-pecas -c HC -i 135041 -f 135041 \
--provedor mistral --forcar --nao-perguntarO texto extraído fica em data/derived/pecas-texto/<hash>.txt.gz, com um pequeno arquivo .extractor ao lado marcando qual provedor produziu aquele texto. Na próxima execução com o mesmo --provedor, a ferramenta pula os PDFs que já têm texto desse provedor; passe --forcar para reextrair.
Sempre use --dry-run antes de uma extração paga (Mistral / Chandra / Unstructured). A prévia mostra quantas páginas serão processadas, custo estimado em dólares e tempo estimado. Detalhes técnicos em docs/peca-sweep-conventions.md.
A árvore data/ é organizada por custo de deleção: três pastas no topo, cada uma respondendo "o que acontece se eu apagar isso?".
data/
├── source/ ← PRECIOSO. O produto científico. Não apagar.
│ └── processos/<CLASSE>/ um JSON por processo, fonte de verdade
│ └── judex-mini_HC_135041-135041.json
│
├── raw/ ← BYTES IMUTÁVEIS do STF. Re-baixar custa horas + proxy.
│ ├── html/<CLASSE>_<N>.tar.gz fragmentos HTML por processo (1 tar por caso)
│ └── pecas/<sha1>.<ext>.gz bytes crus das peças (sha1(url)-keyed,
│ agnóstico ao formato: .pdf.gz, .rtf.gz, …)
│
└── derived/ ← REGENERÁVEL local em minutos. Apague à vontade.
├── warehouse/judex.duckdb warehouse DuckDB (rebuild ~3.7 min)
├── exports/ tabelas feather/parquet por estudo
├── reports/<slug>/ saídas dos notebooks de relatório
├── nao-alocados/<CLASSE>.txt processo_ids confirmados não-alocados no STF
└── pecas-texto/ texto extraído + sidecar + elementos OCR
├── <sha1>.txt.gz extraído por extrair-pecas
├── <sha1>.extractor qual provedor produziu (pypdf/mistral/…)
└── <sha1>.elements.json.gz (opcional) elementos estruturados
runs/ ← OPERACIONAL. Estado de cada varredura.
├── coletas/<timestamp>-<rótulo>/ saída de varrer-processos
│ ├── items/judex-mini_HC_<N>.json (um por processo, atômico)
│ ├── sweep.state.json estado retomável
│ ├── sweep.log.jsonl log append-only
│ ├── sweep.errors.jsonl só as falhas (use com --retentar-de)
│ └── report.md resumo humano
├── active/ saída de baixar/extrair-pecas (em curso)
└── archive/ varreduras concluídas
state/ ← OPERACIONAL persistente.
├── daily_report.json marca d'água do relatório diário
├── watchlist/ snapshots da watchlist
├── logs/scraper_*.log logs de sessão
└── requests-archive.duckdb arquivo de auditoria HTTP
data/source/processos/é o que importa — um JSON por processo com partes, andamentos, relator, decisão, URLs das peças. Faça backup. Não apague.data/raw/custa caro de regenerar (proxy + WAF) — recuperável via re-scrape, mas evite.data/derived/é descartável:rm -rf data/derived/é seguro;atualizar-warehousereconstrói em ~4 min, eextrair-pecasrepopulapecas-texto/lendo deraw/pecas/.runs/estate/logs/são operacionais — apague depois que a varredura terminou.
Detalhes completos em docs/data-layout.md. Para mudar a pasta de saída, passe --saida /caminho/da/pasta.
Backup em um único arquivo (.zip). Para empacotar processos + peças num único .zip que abre direto no Windows Explorer (e que você pode subir manualmente para Drive / OneDrive / disco externo):
# Tudo: data/source/processos + data/raw/pecas + data/derived/pecas-texto
uv run judex fazer-backup
# Só metadados (sem peças)
uv run judex fazer-backup --sem-pecas
# Só HC, com warehouse junto
uv run judex fazer-backup --classe HC --incluir-warehouseSaída padrão: runs/active/backups/judex-backup-<UTC>.zip. A escrita é atômica — se cair no meio, o arquivo final nunca aparece corrompido. JSONs deflacionam; .pdf.gz / .txt.gz entram como ZIP_STORED (já estão comprimidos por dentro). Confira o --help para todas as flags.
Quando o -c/-i/-f não chega: sweeps de milhares de processos, OCR pago, rotação de proxy, retomada de erros e disjuntor. Cada sub-seção é independente — leia só o que precisar.
Para raspar listas não-contíguas ou na casa dos milhares, passe um CSV em vez de -c/-i/-f. Formato (cabeçalho obrigatório):
classe,processo
HC,138706
HC,138707
ADI,4321Invocação (no modo CSV --saida e --rotulo viram obrigatórios):
uv run judex varrer-processos \
--csv runs/alvos.csv \
--saida runs/coletas/meu_sweep \
--rotulo hc-2025-backfillCada processo ok é escrito atomicamente em runs/coletas/meu_sweep/items/ — não há ponto de falha que corrompe o diretório. Para retomar depois de uma queda, aponte para o mesmo --saida e adicione --retomar; ele pula todo processo já marcado como status=ok em sweep.state.json.
Ao final de um sweep, os erros ficam em sweep.errors.jsonl dentro do diretório de saída. Para rerodar só esses:
uv run judex varrer-processos \
--retentar-de runs/coletas/meu_sweep/sweep.errors.jsonl \
--saida runs/coletas/meu_sweep \
--rotulo hc-2025-backfillÚtil quando o WAF teve um mau momento no meio do sweep — a retomada completa a cobertura sem revisitar os já-ok.
Sem proxy, um IP residencial típico aguenta ~600–1000 requisições ao portal do STF antes do WAF começar a devolver 403s em sequência (ver docs/rate-limits.md). Para sweeps de milhares, rotação de IP é praticamente mandatória.
Setup. Crie um arquivo com uma URL de proxy por linha:
http://usuario:senha@proxy1.example.com:8080
http://usuario:senha@proxy2.example.com:8080
http://usuario:senha@proxy3.example.com:8080
E passe como --proxy-pool:
uv run judex varrer-processos --csv alvos.csv \
--saida runs/coletas/sweep --rotulo sweep-1 \
--proxy-pool runs/proxies.txtO scraper rotaciona proativamente: cada IP é usado por ~4,5 min e fica ~4 min em cooldown, alinhado com o tempo que o WAF do STF leva para "esquecer" um IP.
Custo (referência prática). Eu uso ProxyScrape residencial — R$ 100 por 5 GB de tráfego. Cada processo do STF custa ~30–50 KB de HTML, então 5 GB dão ordem de 100 k raspagens. Para rodar um processo isolado não compensa; para o backfill de uma classe inteira (HC ~216 k), compensa.
baixar-pecas também aceita --proxy-pool. Vive em sistemas.stf.jus.br — domínio separado, contador de reputação próprio, bem mais tolerante que portal.stf.jus.br, então em volumes pequenos roda direto. Em sweeps grandes (milhares de PDFs), o modo shardeado (--shards N --proxy-pool FILE) é o caminho: particiona o CSV e distribui o pool de proxies entre os shards.
Modo shardeado: um arquivo só de proxies. Os dois sweepers consomem --proxy-pool FILE em todos os modos. Em modo monolítico o arquivo vai direto para o driver; em modo shardeado o launcher divide round-robin (linha i → shard i % N) e materializa as fatias em <saida>/proxies/proxies.<letra>.txt na hora de subir os filhos. Sem precisar manter pasta de pools nem renomear arquivos:
config/proxies ← uma URL por linha; cole o batch novo do provedor aqui
(linhas em branco e comentários `#` são ignoradas)
# scrapegw, IPRoyal, ProxyScrape... copia, cola, salva. Pronto.
uv run judex varrer-processos --csv X.csv --saida out/ --rotulo r \
--shards 16 --proxy-pool config/proxies --retomarO launcher falha limpo (ValueError) se o arquivo tiver menos URLs úteis do que o número de shards pedido — uma URL por shard é o mínimo.
Para evitar queimar proxy e IP quando o WAF entra em regime de bloqueio total, varrer-processos embute um disjuntor ligado por padrão: mantém uma janela rolante das últimas raspagens e, se a fração de falhas ultrapassa o limiar, o sweep para limpo (escreve estado, sai com código 2). Em paralelo, o CliffDetector classifica o regime WAF em tempo real (healthy / approaching_collapse / engaged / collapse) e dispara rotação preventiva de proxy antes da janela expirar. Detalhes em docs/rate-limits.md.
extrair-pecas aceita vários provedores. O padrão (pypdf) é local, grátis e tira proveito só da camada de texto — em PDFs escaneados devolve texto vazio ou sujo. Para volume em escala, os caminhos de produção são tesseract_fly (Fly.io self-hosted, mais barato) e tesseract_modal (Modal, contingência) — ver docs/setup-fly.md e docs/setup-modal.md. Os provedores comerciais abaixo exigem chave de API no ambiente:
| Provedor | Variável de ambiente | Onde obter |
|---|---|---|
mistral |
MISTRAL_API_KEY |
https://console.mistral.ai/ |
unstructured |
UNSTRUCTURED_API_KEY |
https://platform.unstructured.io/ |
chandra |
DATALAB_API_KEY |
https://www.datalab.to/ |
gemini |
GEMINI_API_KEY |
https://aistudio.google.com/apikey |
chandra_runpod |
RUNPOD_API_KEY + RUNPOD_CHANDRA_ENDPOINT_ID |
ver docs/setup-runpod.md |
Custo por 1 000 páginas (fonte: módulo SPEC de cada provedor em judex/scraping/ocr/):
| Provedor | Tier | USD / 1k páginas |
|---|---|---|
pypdf |
local | $0.00 |
tesseract_fly |
Fly.io self-host | ~$0.005 |
tesseract_modal |
Modal CPU | ~$0.14 |
chandra_runpod |
RunPod 4090 | ~$0.31 |
gemini |
batch (~24h SLA) | $0.66 |
mistral |
batch | $1.00 |
unstructured |
fast | $1.00 |
gemini |
sync (padrão) | $1.32 |
mistral |
sync (padrão) | $2.00 |
chandra |
hosted (Datalab) | ~$3.00 |
unstructured |
hi_res | $10.00 |
Configure a chave no shell de trabalho (nunca commite) e rode primeiro com --dry-run para ver páginas, custo estimado em USD e tempo estimado:
export MISTRAL_API_KEY="sk-..."
uv run judex extrair-pecas -c HC -i 135041 -f 135041 \
--provedor mistral --dry-runSempre --dry-run antes. Bote na ponta do lápis com o câmbio do dia antes de aprovar uma extração paga.
Menos usados, mas úteis em contextos específicos:
validar-gabarito— diff da saída do raspador contra os gabaritos conferidos à mão emtests/ground_truth/. Rodar depois de qualquer mudança em extractor.exportar— exporta os notebooks Marimo de HC como HTML autônomo para compartilhar resultados.probe— monitora uma varredura sharded ao vivo. Lêshard-*/sweep.state.jsonsob--out-roote mostra done/target, throughput e regime de WAF por shard, com ETA agregada. Use--watch Npara redesenhar a cada N segundos.analisar-regimes— análise post-hoc da trajetória doCliffDetectora partir desweep.log.jsonl(varrer) oupdfs.log.jsonl(baixar): responde "quando o regime mudou?" e "onde a queda começou?". Complementa oprobe(que olha snapshot ao vivo) lendo o log append-only.relatorio-diario— relatório diário de novas distribuições por classe (HC por padrão). Mantém marca d'água emstate/daily_report.jsone emite Markdown; aceita watchlist para diffs entre execuções.
uv run judex <comando> --help em cada um para ver as flags.
Você instalou o uv mas não reabriu o terminal. Feche e abra de novo.
Você provavelmente rodou python main.py em vez de uv run python main.py. Sempre use uv run. Se ainda assim der erro, rode uv sync de novo de dentro da pasta judex-mini.
O portal do STF bloqueia IPs que fazem requisições demais em pouco tempo. O bloqueio dura alguns minutos e libera sozinho. O que fazer:
- Espere 5 a 10 minutos e tente de novo.
- Rode intervalos menores (ex.: 50 processos por vez, não 1000).
- Para varreduras de milhares de processos, use rotação de proxy + disjuntor — receita completa em § 7.3 e § 7.4. O
baixar-pecasvive em outro domínio (sistemas.stf.jus.br) com contador próprio e bem mais tolerante — em volumes pequenos roda direto; para milhares de PDFs, passe--proxy-poolou use o modo shardeado. - Para detalhes técnicos (retry, pacing, regime WAF), ver
docs/rate-limits.mdeCLAUDE.md.
Em sweeps longos com rotação de proxy, o CliffDetector pode decidir que o regime WAF entrou em colapso e parar o shard limpo. Acontece principalmente quando o provedor de proxy tem a reputação degradada no STF (o próprio host/ASN é que está hot, não o seu IP). Sintomas:
- Tempos por caso escalam: 2 s → 30 s → 60 s → 100 s antes da parada
- Driver log termina com
[regime] warming → collapseeStopping cleanly — cool down ≥60 min before --resume pgreppara o shard parado retorna vazio; osweep.state.jsondele congela em X/500
O que fazer, em ordem de invasividade:
- Aguardar ≥60 min e relançar com
--retomar. Suficiente para o WAF "esquecer" a reputação na maioria dos casos. - Trocar para IP direto. Relance a varredura sem
--proxy-pool— seu IP tem contador próprio, intocado pelo sweep que colapsou. Adequado para recuperar algumas centenas de IDs. - Desligar o detector:
--ignorar-collapsemantém o sweep rodando apesar dos picos. Só vale em IP direto — em pool de proxy, o detector existe para proteger a reputação do pool. - Trocar de provedor de proxy. Se o provedor atual já queimou a reputação no STF, nenhum cooldown razoável recupera — precisa de outra ASN.
Depois de qualquer relançamento, rode uv run python scripts/aggregate_unallocated_pids.py --classe HC para o registro absorver as observações status="unallocated" capturadas antes do colapso — isso acelera a próxima tentativa via --excluir-nao-alocados.
CLAUDE.md— guia técnico para quem contribui: módulos, testes, convenções, arquitetura, pegadinhas do portal do STF, ferramentas de sweep em larga escala.docs/data-layout.md— mapa detalhado de onde cada arquivo mora e como se referenciam.docs/peca-sweep-conventions.md— convenções dos comandosbaixar-pecas+extrair-pecas(modos de entrada, layout de saída, formato da prévia).docs/current_progress.md— estado atual do trabalho em andamento.docs/reports/— relatórios de varreduras em larga escala (centenas/milhares de processos).