Estudo de caso · No ar
USP Aberta
Uma plataforma de dados abertos que torna a produção de pesquisa da Universidade de São Paulo pesquisável: docentes, unidades, publicações, redes de colaboração, inovação e eventos, construída a partir de currículos Lattes e fontes abertas.
Engenharia de Jonathas Castilho / Xnths como único desenvolvedor, em parceria com o IME-USP e o LabXP, com orientação do Prof. Alfredo Goldman. Uma iniciativa da PRPI-USP.
Em resumo
- Papel
- Engenheiro único: arquitetura, engenharia de dados, busca, backend, frontend, DevOps e segurança
- Período
- julho de 2025 – presente
- Parceiros e créditos
- Links
- Tecnologias
- Next.js 15 · TypeScript · Tailwind v4 · Django 5.2 · DRF · Go · PostgreSQL · Elasticsearch 8.12 · Neo4j · Apache Airflow · Docker Compose · nginx · Prometheus · Grafana · Terraform · Ansible · GitLab CI · pytest-bdd · Playwright · Cypress
- 5.697
- docentes indexados
- com currículo Lattes e unidade
- 1,34 mi
- produções únicas
- ligadas a partir de 1,51 mi de autorias
- ≈4.800×
- travessia de grafo de 1 salto mais rápida
- Neo4j vs Elasticsearch, mediana
- 6.200+
- URLs no sitemap
- eram 3
- 53
- unidades em 8 campi
- 43 unidades de ensino, 4 museus, 6 institutos
- 41
- provedores de dados abertos
- somente fontes livres e abertas
- 1.087
- commits em 15 meses
- ≈125 mil linhas de Python, TypeScript, Go e SQL
1Contexto e problema
Os currículos de pesquisadores brasileiros vivem na Plataforma Lattes[1], uma fonte rica, mas com formato fechado: cada currículo é um grande documento XML e os dados dos docentes da USP estavam dentro de uma view de banco de 9 GB. Quem fazia perguntas simples (quem trabalha neste tema, quais unidades colaboram, como a produção se distribui) precisava contornar a plataforma em vez de passar por ela.
Problema. Tornar a produção de pesquisa da universidade pesquisável e explorável pelo público, sem APIs de busca pagas, sem dados pessoais e em uma infraestrutura pequena o bastante para uma única pessoa operar. As primeiras versões caíam por falta de memória sob carga de crawlers e analytics.
Decisão. Tratar como produto de dados: um pipeline reproduzível da view de origem até armazenamentos dedicados (Elasticsearch para busca e estatísticas, Neo4j para consultas de grafo), uma API Django enxuta e um front-end Next.js que renderiza tudo no servidor e usa cache de forma agressiva.
2Arquitetura
Uma única porta de entrada pública e um único caminho do servidor até os dados. O navegador nunca fala direto com o Django: o Next.js chama a API no servidor, então a superfície da API pode ficar privada e a política de cache fica em um só lugar.
Borda
- TLS na borda da USP
- nginx (roteamento, limites de taxa)
Web
- Next.js 15 (SSR + ISR)
- TypeScript
- Tailwind v4
API
- Django 5.2 + DRF
- Gunicorn
- pytest-bdd
Serviços Go
- auth-server (JWT, OAuth)
- Ágora (eventos)
- Hermes (e-mail)
- Argos (status)
Pipelines
- Airflow 3: bots Mnemosine
- DAGs da Ágora
- Jobs de ETL e reindexação
Dados
- PostgreSQL (schemas isolados)
- Elasticsearch 8.12
- Neo4j 5
- Apache Jena Fuseki (RDF)
Operações
- Prometheus
- Grafana
- Alertmanager
- sondas blackbox
- Aplicação
- Next.js 15 · React 19 · TypeScript 5 · Tailwind v4 · Django 5.2 · DRF · Go
- Dados
- PostgreSQL · Elasticsearch 8.12 · Neo4j 5 · Apache Airflow · Apache Jena Fuseki
- Entrega
- Docker Compose · GitLab CI · Terraform · Ansible · systemd
- Qualidade
- pytest-bdd · Vitest · Playwright + axe · Cypress · k6 · OWASP ZAP
3Engenharia de dados
Problema. A origem é uma view de banco com currículos brutos, que inclui dados pessoais, duplicatas e qualidade irregular; o produto precisa de documentos limpos, deduplicados e seguros do ponto de vista de privacidade, atualizados repetidamente sem indisponibilidade.
Decisão. Um papel dedicado de somente leitura acessa a view; o ETL remove 13 tags privadas (CPF, endereço, telefones, e-mail, dados de nascimento e outras) antes de qualquer armazenamento, e vale a versão mais recente do currículo. Uma única cadeia de comandos atualiza tudo o que vem depois.
- 01ETL Lattessanitizar · upsert
- 02Reindexação da buscaíndice de docentes
- 03Índice de produçõesdeduplicar
- 04Grafo de coautoriaconstruir
- 05Sincronização Neo4jlotes
- 06Relatório de qualidade7 dimensões
- Execução do ETL de 05/10/2026: 436.480 linhas de origem lidas, 347.554 gravadas, 168 ilegíveis, 0 erros; o restante não tinha ID Lattes e foi ignorado.
- Ligação de registros: as produções são casadas por DOI normalizado ou, na falta dele, por título, ano e categoria. Isso transforma 1.514.901 autorias em 1.339.162 produções únicas (88,4%); 132.098 delas têm mais de um autor da USP. A mesma ideia, aplicada a eventos com uma regra de blocagem no estilo Fellegi–Sunter[2], alimenta o calendário Ágora.
- Qualidade de dados: medida nas sete dimensões da ISO/IEC 25012[3], armazenada em snapshots, exposta por uma API e exportada ao Prometheus como razão.
- Inferência de gênero: primeiro uma tabela de primeiros nomes do IBGE, depois um SVM que se abstém quando não tem certeza: 98,0% de acurácia com 94% de cobertura em 3.000 nomes, com 4,7% dos docentes informados como “indeterminado” em vez de chutados.
Bots Mnemosine. Fontes externas (notícias da universidade, hub de inovação, patentes, catálogos de cursos, provedores de dados abertos) são coletadas por oito bots baseados em Airflow. Cada bot tem seu próprio schema; as respostas brutas ficam comprimidas em uma camada bronze, deduplicadas por SHA-256 e versionadas somente por acréscimo, então a correção de um parser reprocessa o histórico sem refazer requisições. Um exemplo: o arquivo de notícias tem 52.606 matérias e 6.552 episódios de podcast.
- Educado por construção. user agent assinado com cabeçalho de contato, robots.txt respeitado[4], no mínimo 3 s entre requisições a hosts da universidade com jitter, um único slot de pool do Airflow, backoff que respeita Retry-After, um circuit breaker cujo tempo de espera dobra até 24 h e trabalho pesado somente fora do horário de pico.
- Somente livre e aberto. uma regra permanente: toda fonte precisa ter licença aberta, ser gratuita e dispensar aprovação, e aparece no site com um selo de citação. 41 provedores alimentam o enriquecimento do calendário de eventos.
4Busca e recuperação de informação
Problema. Nomes, áreas de pesquisa e biografias se comportam de formas diferentes, e uma única função de ranqueamento trata todos do mesmo jeito.
Decisão. Similaridade por campo: modelos de linguagem com suavização de Dirichlet (μ = 2000)[5] em nomes e biografias, divergence-from-independence nas áreas de atuação, BM25 nos títulos de produção e um analisador de edge n-grams para o autocompletar. Deliberadamente não há stemmer, lista de stop words nem tabela de sinônimos: o corpus é feito de nomes e termos técnicos.
Avaliação. Um harness offline executa 1.200 consultas (12 classes × 100) e calcula nDCG@k[6] com k = 3. A regra de aceite é que nenhuma classe de consulta pode piorar. A comparação entre BM25 e Dirichlet é o tema de uma submissão a congresso; não cito valores de nDCG aqui até esse artigo ser público.
- Telemetria para modelos de clique. consultas e interações (impressões, cliques ranqueados, tempo de permanência, retornos) são registradas contra um id de sessão pseudônimo de 30 minutos, exportáveis para learning-to-rank não enviesado e resumidas em um painel do Grafana que acompanha o mean reciprocal rank.
- Limites de entrada. consultas de até 1.000 caracteres, validadas no navegador com um contador inline, de modo que o usuário nunca provoca um erro no backend; consultas com mais de oito palavras dispensam correspondência aproximada.
5Benchmark de grafos
Redes de coautoria são a única carga em que um índice invertido é a ferramenta errada. Em vez de discutir, medi.
Travessia de coautores, 1 salto: Elasticsearch
Travessia de coautores, 1 salto: Neo4j
Busca por nome: Elasticsearch
Busca por nome: Neo4j
log scale · ms
Resultado. O Neo4j atende apenas o grafo de colaboração, por meio de oito estratégias Cypher. Somente a profundidade da travessia é interpolada na consulta, a partir de uma lista permitida com 1, 2 e 3; todo o resto é parâmetro vinculado. As construções do grafo passaram a trabalhar em streaming em vez de materializar resultados, depois de um incidente de falta de memória.
6Cache e desempenho
Problema. As páginas de estatísticas agregam mais de um milhão de produções e o tráfego público é em rajadas. Requisições a frio eram lentas e, sob carga, o servidor ficava sem memória.
Decisão. Três camadas de cache, cada uma com sua própria história de invalidação:
- Elasticsearch: o cache de requisições dos shards subiu do padrão de 1% para 10% do heap.
- Django: respostas em cache por 24 h (agregações puras), 1 h (dados editáveis por administradores) e 30 min (tendências). Um contador de geração armazenado no banco invalida as estatísticas, de modo que todos os workers as descartam em cerca de um minuto.
- Next.js: regeneração estática incremental com vida de uma hora em cache compartilhado, revalidação por tags a partir de um endpoint interno e um aquecimento na inicialização que espera a API responder antes de purgar.
- Memória. o heap da busca passou de 256 MB para 768 MB depois de erros de OutOfMemory; a construção do índice de produções abortava após 1.040 de 5.653 docentes e agora usa spool em disco e termina 1,34 mi de produções em cerca de três minutos; as construções de coautoria trabalham em streaming com um circuit breaker de memória residente em 1 GiB.
- Reindexação. os lotes rodam no horário mais calmo previsto a partir de 14 dias de taxas de requisição do Prometheus, em vez de um botão manual.
| Antes | Depois | |
|---|---|---|
| JavaScript no primeiro carregamento, página inicialentre 64% e 76% menor nas páginas medidas | 975 kB | 249 kB |
| Peso das imagens nos assets públicos | 80 MB | 11 MB |
| Desempenho no Lighthouse, perfil de docente | 45 | 77 |
| Largest Contentful Paint, perfil de docente | 9,2 s | 4,0 s |
| Time to first byte, perfil de docente | 232 ms | 10 ms |
| Total Blocking Time, página da unidade IME | 1.225 ms | 426 ms |
| Nota de SEO no Lighthouseem três páginas auditadas | 92 | 100 |
7DevOps
Decisão. A produção é um único projeto Docker Compose. Os deploys são simples e reversíveis de propósito: cada release é uma tag de imagem imutável, a anterior é mantida como tag de rollback e as imagens são enviadas com uma transferência retomável e com checksum, de modo que um upload interrompido continua em vez de recomeçar.
- Pipeline. o GitLab CI define estágios de build, teste (SAST, orçamentos de SEO, carga com k6, OWASP ZAP, caos de rede com netem), deploy e métricas. Os pushes passam por um hook de pre-push que roda as suítes.
- Infraestrutura como código. Terraform e nove papéis Ansible (segredos no Vault) descrevem a configuração anterior na nuvem. Um caminho de migração para k3s e ArgoCD foi construído, avaliado e deliberadamente adiado em agosto de 2026, registrado como decisão e não deixado como deriva.
- Autorrecuperação. timers e watchdogs do systemd reiniciam o que importa e protegem os daemons de acesso remoto do OOM killer; as tarefas agendadas são timers porque o servidor não tem cron.
8Observabilidade e SRE
Prometheus (retenção de 15 dias), Grafana, Alertmanager e sondas blackbox cobrem a pilha, organizados em torno de oito medidas no estilo DORA: MTTR, lead time, frequência de deploy, taxa de falha de mudanças, disponibilidade contra um SLO de 99,5%, duração do deploy, tempo de detecção e histogramas de tempo de resposta[7].
- Página de status Argos. um serviço em Go sonda 11 componentes a cada 60 segundos, só muda um estado depois de três verificações consecutivas (proteção contra oscilação), desenha barras de disponibilidade de 90 dias, abre e resolve incidentes automaticamente e envia e-mail nos dois eventos. É pública em /status.
- Runbooks. quatro runbooks escritos (5xx no backend, Elasticsearch degradado, latência, incidente de busca); falhas de busca devolvem um corpo problem+json localizado com um id de requisição.
9Segurança
Decisão. A identidade é um pequeno serviço em Go (auth-server): JSON Web Tokens RS256 com vida de 15 minutos, um endpoint JWKS com identificadores de chave conforme a RFC 7638[8], Google OAuth como único login, estado CSRF assinado e claims de papel negadas por padrão (usuário, administrador, super administrador). Ele também é o provedor de login único do Grafana e do Airflow.
- O login por senha foi removido depois que bots tentaram força bruta nele. O proxy a montante não repassa o endereço IP do cliente, então a limitação por IP é ineficaz por construção; as defesas são baseadas em identidade e em rota: zonas de limite de taxa no nginx (alertas, grafo, autenticação, bots conhecidos) e throttles do DRF nas consultas de grafo.
- Trilha de auditoria à prova de adulteração. um papel de banco somente de inserção, uma cadeia de hashes calculada por um trigger do banco sob um advisory lock, um comando que verifica a cadeia, partições mensais e retenção de dois anos.
- Injeção. parâmetros vinculados em todo lugar; o único valor interpolado em Cypher vem de uma lista permitida.
- Varreduras. bandit, pip-audit, pnpm audit, gitleaks, gosec, golangci-lint e uma baseline do OWASP ZAP rodam no CI.
10Privacidade e conformidade
A plataforma publica dados acadêmicos de pessoas identificadas, então privacidade é uma entrada de projeto e não um checklist. Um registro das operações de tratamento conforme a LGPD[9] informa a finalidade e a base legal de cada atividade (dados de docentes, auditoria, listas de e-mail, notícias, logs de busca).
- Minimização. os dados pessoais são removidos duas vezes, na hora do ETL e de novo na hora da indexação; o campo de número USP interno foi apagado de todas as camadas em junho de 2026.
- Retenção. logs de busca por 13 meses, logs de aplicação por 30 dias, registros de auditoria por dois anos.
- Telemetria. respeita o Global Privacy Control e o Do Not Track, remove e-mails, CPFs e telefones das consultas e nunca guarda endereço IP nem user agent.
- Listas de e-mail. double opt-in, endereços cifrados com AES-256-GCM mais um blind index HMAC para busca, descadastro em um clique[10], sem pixels de rastreamento, inscrições não confirmadas apagadas após 48 horas.
- Licenciamento aberto e atribuição. o conteúdo do site é CC BY-NC-SA 4.0[11] e toda fonte externa carrega um selo de citação visível.
- Acessibilidade. varreduras automáticas com axe-core contra a WCAG 2.1 A e AA quebram o build diante de problemas graves ou críticos.
11SEO e descoberta por LLMs
Problema. Um site de interesse público que buscadores e assistentes de IA não encontram não serve ao público. O sitemap tinha três URLs.
- Sitemap. agora um índice de sitemaps filhos (até 10.000 URLs de docentes cada) com datas de última modificação reais, tiradas dos currículos: mais de 6.200 URLs, incluindo 5.552 perfis de docentes.
- Higiene de canônicas. duplicatas redirecionam com 308 para a URL canônica e soft 404 viraram 404 de verdade.
- Dados estruturados. JSON-LD para WebSite com SearchAction, Organization, Person, CollegeOrUniversity, Museum, ResearchOrganization e BreadcrumbList.
- Para modelos de linguagem. /llms.txt e /llms-full.txt, uma versão compacta em Markdown de cada perfil de docente (alguns quilobytes contra 1,1–1,6 MB de HTML), um diretório de A a Z e um grupo explícito no robots.txt para 16 crawlers de IA.
- Gates de regressão. orçamentos de renderização, bundle e Lighthouse mais uma spec de SEO no Playwright; na primeira execução contra o site antigo, 15 asserções do Lighthouse CI falharam.
12Testes e qualidade
Testes orientados a comportamento descrevem o produto no vocabulário de quem o usa, e um hook de pre-push roda as suítes antes de o código sair da máquina.
- 36
- arquivos de feature do pytest-bdd
- 208
- arquivos de teste do Vitest
- 110
- arquivos de teste em Go
- 15
- specs do Playwright
- SEO, acessibilidade, visual
O gate de cobertura do backend está em 90%; isso é um limite, não uma medição. As verificações não funcionais incluem testes de carga com k6, OWASP ZAP, caos de rede e profiling de memória. Uma análise comportamental de código no estilo de Tornhill[12] sobre 656 commits orienta onde pagar primeiro a dívida técnica.
13Superfície do produto
O que a plataforma oferece hoje, tudo gerado a partir de dados vivos e não de texto escrito à mão:
- Perfis de docentes, unidades, departamentos e museus, com edição no local em estilo planilha para curadores (desfazer, refazer e um log de mudanças reversível).
- Estatísticas: produção ao longo do tempo, áreas do conhecimento, países de publicação, distribuição de gênero.
- Uma rede de colaboração com alternância entre grafo e tabela.
- Ágora: um calendário de eventos de toda a USP com exportação .ics e uma API REST, um subprojeto com página própria.
- Inovação: patentes, dados de transferência de tecnologia e um verificador de elegibilidade que roda inteiramente no navegador.
- Cursos e currículos, e um verificador de formatura (/formatura) que lê o PDF do histórico do aluno no próprio navegador.
- Um arquivo de imprensa, newsletters por e-mail e a página pública de status.
14Próximos passos
O roadmap é tanto pesquisa quanto engenharia. Os embeddings de docentes e produções já estão calculados com um modelo aberto; a orquestração com LangChain e a camada de geração de um pipeline de geração aumentada por recuperação vêm a seguir, condicionadas à qualidade dos dados, e por isso o relatório de qualidade vem primeiro. Mais adiante: verificação formal dos pipelines de coleta com redes de Petri, uma comparação entre busca lexical e semântica sobre logs de uso reais e a ampliação do corpus para eventos artísticos e música brasileira.
Este estudo de caso é a base de um plano de pesquisa de mestrado; veja a página de Pesquisa.
Referências
- [1]CNPq. Plataforma Lattes.
- [2]Fellegi, I. P., & Sunter, A. B.. A theory for record linkage. Journal of the American Statistical Association 64(328) (1969).
- [3]ISO/IEC. ISO/IEC 25012:2008 — Modelo de qualidade de dados (2008).
- [4]Koster, M., et al.. Robots Exclusion Protocol (RFC 9309) (2022).
- [5]Zhai, C., & Lafferty, J.. A study of smoothing methods for language models applied to information retrieval. ACM Transactions on Information Systems 22(2) (2004).
- [6]Järvelin, K., & Kekäläinen, J.. Cumulated gain-based evaluation of IR techniques. ACM Transactions on Information Systems 20(4) (2002).
- [7]Forsgren, N., Humble, J., & Kim, G.. Accelerate: The Science of Lean Software and DevOps. IT Revolution (2018).
- [8]Jones, M., & Sakimura, N.. JSON Web Key (JWK) Thumbprint (RFC 7638) (2015).
- [9]República Federativa do Brasil. Lei Geral de Proteção de Dados Pessoais (Lei nº 13.709/2018) (2018).
- [10]Levine, J., & Herkula, T.. Signaling One-Click Functionality for List Email Headers (RFC 8058) (2017).
- [11]Creative Commons. Creative Commons Atribuição-NãoComercial-CompartilhaIgual 4.0.
- [12]Tornhill, A.. Your Code as a Crime Scene. Pragmatic Bookshelf (2015).
Projetos relacionados
Subprojeto · No ar
Ágora: o calendário de eventos de toda a USP
Um calendário de palestras, simpósios, concertos e exposições da Universidade de São Paulo, montado a partir de divulgações e enriquecido com fontes abertas.
Go · Apache Airflow · PostgreSQL · REST · iCalendar
Subprojeto · No ar
Verificador de formatura
Uma ferramenta para estudantes de BCC do IME-USP: envie seu histórico e veja se você está no caminho para se formar, com o histórico lido inteiramente no seu navegador.
TypeScript · Next.js · PDF parsing