xnths.

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
  • Uma iniciativa da: PRPI-USP
  • Parceiro: IME-USP
  • Laboratório: LabXP
  • Orientação de: Prof. Alfredo Goldman
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
Fig. 1Arquitetura do sistema USP Aberta. Camadas da borda até os armazenamentos de dados. Os serviços em Go são processos pequenos e de propósito único; cada schema do Postgres tem seu próprio papel.Fonte: Documentação de arquitetura do repositório, outubro de 2026
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.

  1. 01ETL Lattessanitizar · upsert
  2. 02Reindexação da buscaíndice de docentes
  3. 03Índice de produçõesdeduplicar
  4. 04Grafo de coautoriaconstruir
  5. 05Sincronização Neo4jlotes
  6. 06Relatório de qualidade7 dimensões
Fig. 2Pipeline de atualização. Cada etapa é idempotente e pode ser reexecutada isoladamente.Fonte: Documentação do motor de busca e da reindexação
  • 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

7.691,8 ms

Travessia de coautores, 1 salto: Neo4j

1,6 ms

Busca por nome: Elasticsearch

5,09 ms

Busca por nome: Neo4j

4,44 ms

log scale · ms

Fig. 3Latência mediana, Elasticsearch versus Neo4j. A travessia de coautores com um salto é cerca de 4.800× mais rápida no Neo4j (mediana de 1,6 ms contra 7.691,8 ms). A busca simples por nome fica praticamente empatada.Fonte: Tempos brutos do benchmark, 12/07/2026

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.
Core Web Vitals e peso, antes e depois da rodada de desempenho de 09/10/2026
AntesDepois
JavaScript no primeiro carregamento, página inicialentre 64% e 76% menor nas páginas medidas975 kB249 kB
Peso das imagens nos assets públicos80 MB11 MB
Desempenho no Lighthouse, perfil de docente4577
Largest Contentful Paint, perfil de docente9,2 s4,0 s
Time to first byte, perfil de docente232 ms10 ms
Total Blocking Time, página da unidade IME1.225 ms426 ms
Nota de SEO no Lighthouseem três páginas auditadas92100
Fig. 4Core Web Vitals e peso, antes e depois da rodada de desempenho de 09/10/2026. Medições de laboratório (Lighthouse) em páginas públicas.Fonte: Relatório de base de SEO e desempenho

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. [1]CNPq. Plataforma Lattes.
  2. [2]Fellegi, I. P., & Sunter, A. B.. A theory for record linkage. Journal of the American Statistical Association 64(328) (1969).
  3. [3]ISO/IEC. ISO/IEC 25012:2008 — Modelo de qualidade de dados (2008).
  4. [4]Koster, M., et al.. Robots Exclusion Protocol (RFC 9309) (2022).
  5. [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. [6]Järvelin, K., & Kekäläinen, J.. Cumulated gain-based evaluation of IR techniques. ACM Transactions on Information Systems 20(4) (2002).
  7. [7]Forsgren, N., Humble, J., & Kim, G.. Accelerate: The Science of Lean Software and DevOps. IT Revolution (2018).
  8. [8]Jones, M., & Sakimura, N.. JSON Web Key (JWK) Thumbprint (RFC 7638) (2015).
  9. [9]República Federativa do Brasil. Lei Geral de Proteção de Dados Pessoais (Lei nº 13.709/2018) (2018).
  10. [10]Levine, J., & Herkula, T.. Signaling One-Click Functionality for List Email Headers (RFC 8058) (2017).
  11. [11]Creative Commons. Creative Commons Atribuição-NãoComercial-CompartilhaIgual 4.0.
  12. [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

USP Aberta — estudo de caso — Jonathas Castilho