Linha do Tempo

Este documento consolida as decisões arquiteturais (ADRs) tomadas durante o desenvolvimento do Opportunity Recommendation System (ORS), organizadas cronologicamente.

Arquitetura Orientada a Eventos (EDA)

Usar Arquitetura Orientada a Eventos (EDA) com RabbitMQ como broker de mensagens. Desacoplar a coleta (Scrapers) do processamento (Worker) e do consumo (API). RabbitMQ atua como buffer para absorver picos de carga.

Veja Mais →

Adoção da Ontologia ESCO

Usar a Ontologia ESCO (European Skills, Competences, Qualifications and Occupations) como espinha dorsal do Grafo de Conhecimento. Utilizar a taxonomia de Habilidades (Skills) da ESCO com suporte multilíngue, incluindo português brasileiro.

Veja Mais →

Uso do Padrão Template Method para Scrapers

Implementar o Padrão Template Method com uma classe base BaseScraper para garantir fluxo consistente de coleta com publicação incremental de oportunidades. Centraliza infraestrutura (publisher, logging, tratamento de erros) em classe base.

Deduplicação de Oportunidades via Hash

Calcular um hash SHA-256 no Publisher baseado em todos os campos da oportunidade exceto scraped_at. Adicionar content_hash como campo UNIQUE no banco de dados para evitar reprocessamento de duplicatas.

Selenium Standalone Container para Scrapers

Usar o container selenium/standalone-chrome como serviço dedicado no Docker Compose. Scrapers conectam via Remote WebDriver através da variável de ambiente SELENIUM_REMOTE_URL, eliminando a necessidade de instalar Chrome em cada container de scraper.

Adotar Stack PLG para Observabilidade Centralizada

Adotar a stack PLG (Promtail, Loki, Grafana) para centralização de logs, combinada com Loguru para padronização de logs estruturados em JSON nas aplicações Python. Promtail coleta de stdout/stderr, Loki armazena, Grafana visualiza.

Abordagem para Extração de Skills via NLP

Usar Ollama com modelo local (Gemma 3 1B) com JSON Mode para extração estruturada de skills de textos em português. Oferece capacidade zero-shot learning sem necessidade de fine-tuning ou dataset anotado, com performance moderada (~5-15s por documento com GPU).

Veja Mais →

Configuração Centralizada no Módulo Shared

Centralizar todas as configurações do projeto em src/shared/config.py usando pydantic_settings.BaseSettings. Garante single source of truth para configurações e facilita mudanças de ambiente (dev, prod).

Strategy Pattern para Mapeamento de Prompts por Source

Implementar Strategy Pattern com um Registry para mapeamento de prompts por source de dados. Cada source tem sua própria estratégia de mapeamento registrada, permitindo extensibilidade sem violar Open/Closed Principle.

Veja Mais →

Modular Docker Compose com Include

Separar docker-compose.yml em módulos usando a diretiva include (v2.20.3+). Cada domínio mantém seu compose isolado e autocontido. Comando docker compose up funciona normalmente com múltiplos arquivos.

Integração ESCO-LLM em 4 Estágios

Dividir o processo de integração ESCO-LLM em 4 estágios bem definidos: (1) Injeção de taxonomia ESCO no prompt, (2) Extração de skills candidatos pelo LLM, (3) Vinculação a URIs ESCO, (4) Validação contra ontologia.

Veja Mais →

RabbitMQ DLQ e Padrão de Retry

Implementar Manual ACK com limite de 3 retries e Dead Letter Queue (DLQ). ACK manual garante que mensagem só é reconhecida após sucesso. Limite de 3 tentativas evita loops infinitos. DLQ preserva mensagens com erro para investigação.

Veja Mais →

Histórico de Processamento para Fine-Tuning

Criar coleção separada processed_history para armazenar contexto completo de cada processamento (prompt text, modelo usado, timestamp). Separação de responsabilidades entre dados operacionais e histórico para análise.

Reestruturação de Diretórios do Projeto

Reestruturar projeto em 3 camadas claramente separadas: domain/ (lógica de negócio), infra/ (conectores de infraestrutura), workers/ (entrypoints e Dockerfiles). Elimina imports circulares e dependências confusas.

Skill Post-Processing Pipeline

Implementar SkillProcessor como Stage 2.5 do pipeline (entre extração LLM e linking). Responsabilidades: reorganizar por tipo correto, filtrar não-skills, extrair position/company, validar contra ESCO, injetar reference_labels para linking.

Veja Mais →

Arquitetura de Microserviços Orientada a Eventos

Implementar Event-Driven Architecture (EDA) com microserviços Docker independentes. Usar apenas Events ao invés de Commands. Event Bus (RabbitMQ) como backbone, Padrão Publisher-Subscriber para desacoplamento total.

Veja Mais →

Filas Únicas por Domínio com Dispatch por Event Type

Usar uma fila por domínio (opportunities e resumes), onde cada serviço registra handlers para tipos de evento que processa. EventDispatcher roteia mensagens para handler correto. Reduz complexidade de infraestrutura de N eventos para 2 filas principais.

Veja Mais →

EventRouter com Routing Key via Decorator

Usar o padrão Decorator @EventRouter.route() para registrar routing keys em registry centralizado. Validações: (1) chave qualificada module.classname, (2) erro se houver colisão de nome ou routing key, (3) formato domain.entity.action.

Veja Mais →

Retry com Republish e Incremento de Contador

Implementar retry via ACK + Republish com header incrementado. A mensagem é reconhecida (ACK) e uma nova é publicada com x-retry-count incrementado. Permite incrementar contador sem requeue infinito.

Veja Mais →

Bindings Específicos por Serviço

Usar bindings específicos por serviço consumidor ao invés de wildcard único. Cada serviço recebe apenas eventos que declarou interesse. Evita "Competing Consumers" indesejados.

Veja Mais →

Pipeline de Processamento de Currículos

Estender processors existentes com handlers para currículos ao invés de criar serviços duplicados. Mesmo skill-extractor, entity-linker e graph-populator processam ambos os domínios.

Veja Mais →

Estratégia de Testes com Testcontainers

Implementar suite de testes com Testcontainers para provisionar containers de dependências (MongoDB, RabbitMQ, Neo4j) em tempo de execução. Garante rede de segurança real contra regressões.

MVP do Sistema de Recomendação via Cypher

Criar serviço recommender-cypher que consome dados do Neo4j e fornece recomendações via RabbitMQ. Usa Índice de Jaccard como métrica de matching. Single Cypher Query sem plugins complexos.

Veja Mais →

Implementação da API para Front-end

Implementar API REST com HTTPS/TLS, JWT para autenticação, validação rigorosa de entrada, proteção contra injeções, rate limiting e logging centralizado.

Arquitetura de Connectors e Persistência Centralizada

Migrar para connectors centralizados (connector-graph e connector-mongodb) via RabbitMQ. Padrão RPC over Message Queue para operações síncronas. Connectors "donos" dos drivers e da integridade dos dados.

Veja Mais →

Collective Entity Linking — Modelo Duplo Diamante

Substituir o entity linking estático por um Collective Entity Linking (CEL) em dois estágios. Diamante de Expansão: KNN semântico maximiza o recall de candidatos via embeddings. Diamante de Convergência: Personalized PageRank no Neo4j valida coerência topológica e maximiza a precisão.

Veja Mais →

Logger Singleton com Filtragem Transparente de Módulos

Refatorar o pacote de logging para uma classe Logger Singleton que garante configuração única do Loguru em todo o serviço. InterceptHandler preserva o record.name original de cada biblioteca, permitindo silenciamento granular via variável de ambiente LOG_LEVELS.

Refatoração do Loader para Múltiplos Domínios

Renomear loader-resumes-kaggle para loader-kaggle com escopo ampliado. Passa a publicar tanto eventos ResumeRawLoaded (currículos) quanto OpportunityScraped (oportunidades) a partir de fontes CSV do Kaggle.

Veja Mais →

Prompt Engineering Estruturado para SLMs

Adotar técnicas de prompt engineering baseadas em assistentes estado-da-arte: Role/Persona Setting, estruturação por XML Tags, Negative Constraints e enforcement de JSON nativo. Prompts e lógica de mapeamento ficam exclusivamente no processor-prompt-generator.

Veja Mais →

Deduplicação no Neo4j via Versionamento de Nós

Implementar merge_node aprimorado com identidade estável por profile_id e versão volátil por id. Ao detectar mudança de versão, relações HAS_SKILL são removidas atomicamente antes da atualização, garantindo grafo sempre consistente.

Veja Mais →

Ingestão da ESCO e Índice Vetorial no Neo4j

Usar o Neo4j Vector Index como solução unificada para grafo e vetores. Embeddings dos nós (:Skill) gerados via embeddinggemma no Ollama e indexados com HNSW (cosine similarity, 768 dimensões). Novo serviço loader-esco orquestra a ingestão.

Veja Mais →

Métrica de Avaliação — Jaccard Médio Global

Adotar a média dos percentuais médios de Jaccard por usuário como métrica de avaliação do sistema de recomendação. Calcula a similaridade entre skills recomendadas e skills do perfil para todos os currículos e extrai a média global.

Veja Mais →

Estratégia de Prefixo Tipado para Embeddings

Adotar o formato Raw merged newlines como template canônico: preferred_label\n description\n alt_labels. Elimina prefixos textuais em inglês ou português que introduzem ruído lexical, permitindo que o modelo opere sobre conteúdo semântico puro.

Veja Mais →

Otimização de Performance do Ollama

Avaliar configurações do Ollama com benchmark dedicado (scripts/ollama_benchmark.py) testando Gemma 3 1B em múltiplas configurações. Configuração 2 alcançou redução de ~50% no p95 de latência em relação à baseline.

Veja Mais →

Prompt Multi-Role e Few-Shot via Chat History

Reestruturar prompts com mensagens multi-role (system, user, assistant) e 2 exemplos few-shot por tipo de entidade no histórico de chat. Prompts migram para arquivos físicos em resources/ (Markdown para instrução, JSON para resposta esperada). Contexto ampliado para 8.192 tokens.

Veja Mais →

Especialização do Entity Linking por Tipo de Skill

Implementar Fuzzy Match com fallback para Duplo Diamante diretamente no processor-entity-linker. Skills "hard" passam primeiro por fuzzy match em memória via RapidFuzz (threshold 0.9). Se o score for insuficiente, a pipeline KNN+PPR é executada. Skills "soft" e "knowledge" vão direto ao Duplo Diamante.

Veja Mais →

Refinamento do Personalized PageRank no Entity Linking

Adotar novo fluxo: KNN → threshold de similaridade → pesos uniformes no Top-K → PPR → normalização PPR/PR → multiplicação por cosine similarity → ranking final. Equilibra proximidade semântica (KNN) com validação estrutural na ontologia ESCO (PPR).

Veja Mais →