Índice de Jaccard
Índice
O que é
O Índice de Jaccard mede a similaridade entre dois conjuntos como a razão entre sua interseção e sua união:
- J = 0 → nenhuma habilidade em comum
- J = 1 → conjuntos idênticos (compatibilidade perfeita)
No Kairos, A representa o conjunto de habilidades de um currículo e B o conjunto de habilidades requeridas por uma oportunidade. O resultado é expresso como match_percentage (0–100) na interface do sistema.
O Jaccard é simétrico (J(A, B) = J(B, A)) e naturalmente normalizado. Não exige treino de modelo, é interpretável em linguagem natural ("você possui 70% das habilidades desta vaga") e é penalizado tanto por habilidades faltantes no currículo quanto por excesso de requisitos na vaga.
Como Funciona
Por que Jaccard para Recomendação de Vagas
A escolha do Índice de Jaccard como métrica de compatibilidade para o MVP do sistema de recomendação se baseia em quatro fatores:
1. Simplicidade,Interpretabilidade O Jaccard é uma métrica intuitiva, se adequanto ao objetivo de alinhamento de competências entre currículos e vagas, além de ser facilmente interpretável. Ela servirá de baseline para o desenvolvimento de uma abordagem baseada em GNN, permitindo comparações diretas de performance e interpretabilidade entre o modelo simples e o modelo avançado.
2. Natureza Conjuntual das Skills
Habilidades são conjuntos por definição: não têm ordenação relevante, não se repetem e a pertinência (Python ∈ skills_do_currículo) é o que importa. O Jaccard modela exatamente essa semântica.
3. Grafo Já Estruturado para Isso
A pipeline NLP do Kairos popula o Neo4j com os relacionamentos [:HAS_SKILL] (currículo possui habilidade) e [:REQUIRES_SKILL] (vaga requer habilidade), ambos apontando para nós (:Skill) da ESCO. A interseção A ∩ B é simplesmente o conjunto de nós (:Skill) apontados por ambos — calculável com uma única query Cypher.
4. Implementação Sem Dependências Externas O Jaccard é computável via Cypher puro usando o princípio da inclusão-exclusão para calcular a união sem precisar de plugins como APOC:
Implementação no recommender-cypher
O Subgrafo Utilizado (G0)
O recommender MVP opera exclusivamente sobre relacionamentos diretos entre currículos, habilidades e oportunidades — sem traversal hierárquico da ESCO (sem [:BROADER]/[:NARROWER]):
A Query Cypher
A query usa a fórmula de inclusão-exclusão em uma única passagem sobre o grafo:
MATCH (r:Resume)
WHERE r.raw_data_hash = $hash
MATCH (o:Opportunity)
OPTIONAL MATCH (r)-[:HAS_SKILL]->(s:Skill)<-[:REQUIRES_SKILL]-(o)
WITH r, o, count(s) AS intersection
WITH r, o, intersection,
size([(r)-[:HAS_SKILL]->() | 1]) AS resume_skills,
size([(o)-[:REQUIRES_SKILL]->() | 1]) AS opp_skills
WITH r, o, intersection,
(resume_skills + opp_skills - intersection) AS union_size
WHERE union_size > 0
RETURN
o.entity_id AS opportunity_id,
o.position AS position,
o.company AS company,
round(toFloat(intersection) / union_size * 100, 2) AS match_percentage,
intersection AS matched_skills,
union_size AS total_skills
ORDER BY match_percentage DESC
Todo o cálculo de interseção, união e percentual acontece dentro de uma única query Cypher — sem loops N+1, sem APOC e sem processamento em memória na aplicação host.
Estrutura do Resultado
Cada recomendação retornada pelo recommender-cypher contém:
| Campo | Tipo | Descrição |
|---|---|---|
opportunity_id | string | Identificador da vaga no sistema |
match_percentage | float | Compatibilidade Jaccard em percentual (0–100) |
reasoning.message | string | Mensagem legível para exibição direta na UI |
reasoning.matched_skills | int | Número de habilidades em comum |
reasoning.total_skills | int | Tamanho da união (total de habilidades únicas consideradas) |
A estrutura reasoning foi projetada para ser agnóstica ao modelo matemático: quando o algoritmo de recomendação evoluir para abordagens mais sofisticadas (ex: ReMR baseado em RL), o front-end não precisará de adaptações.
Escopo por raw_data_hash
Cada execução da pipeline de processamento de currículo gera um identificador imutável chamado raw_data_hash — um hash SHA-256 calculado sobre os campos estáveis do currículo (excluindo timestamps).
Todas as queries de recomendação Jaccard são escopadas por esse hash, garantindo que o cálculo opere sempre sobre o snapshot mais recente e consistente do currículo:
WHERE r.raw_data_hash = $hash
Se um usuário resubmeter o currículo (atualização de habilidades), o hash muda e um novo snapshot é criado. Isso evita que habilidades de processamentos anteriores contaminem as recomendações atuais — cada cálculo Jaccard reflete o estado exato das habilidades vinculadas naquele processamento.
Demonstração / Exemplos
Cálculo Passo a Passo
Contexto: currículo de um desenvolvedor sendo comparado a duas vagas distintas.
Habilidades do currículo (conjunto A):
{Python, Docker, SQL, Git, React}
Vaga 1 — Alta Compatibilidade
Requisitos da vaga (conjunto B₁):
{Python, SQL, JavaScript, Docker, Node.js}
A ∩ B₁ = {Python, SQL, Docker} → |∩| = 3
A ∪ B₁ = {Python, Docker, SQL, Git, React, JavaScript, Node.js} → |∪| = 7
J(A, B₁) = 3 / 7 ≈ 0.4286 → match_percentage = 42.86%
{
"opportunity_id": "vaga-001",
"match_percentage": 42.86,
"reasoning": {
"message": "Você possui 3 das 7 habilidades únicas desta vaga.",
"matched_skills": 3,
"total_skills": 7
}
}
Vaga 2 — Baixa Compatibilidade
Requisitos da vaga (conjunto B₂):
{Java, Spring Boot, Kubernetes, Terraform, AWS}
A ∩ B₂ = {} → |∩| = 0
A ∪ B₂ = {Python, Docker, SQL, Git, React, Java, Spring Boot, Kubernetes, Terraform, AWS} → |∪| = 10
J(A, B₂) = 0 / 10 = 0.0 → match_percentage = 0.00%
O currículo não possui nenhuma skill em comum com a vaga 2, resultando em match_percentage = 0% — a vaga não aparecerá no topo das recomendações.
Efeito da Simetria
O Jaccard é simétrico: adicionar requisitos irrelevantes à vaga penaliza o score mesmo que o currículo seja forte nas skills relevantes.
Currículo: {Python, SQL}
Vaga A: {Python, SQL} → J = 2/2 = 1.00 (100%)
Vaga B: {Python, SQL, Java, Kotlin, Go, Rust} → J = 2/6 = 0.33 (33%)
Ambos os currículos têm todas as skills que importam para a Vaga B, mas o Jaccard penaliza porque a union cresce com os requisitos não cobertos — comportamento intencional que desfavorece vagas com requisitos genéricos.
ADR's Relacionadas
| ADR | Data | Decisão |
|---|---|---|
| ADR-030 | Mar 2026 | Adoção do Índice de Jaccard como métrica de compatibilidade do MVP; algoritmo anterior baseado em soma de pesos descartado por ineficiência em larga escala |
| ADR-040 | 2026 | Estabelecimento do Jaccard Médio Global como indicador de estabilidade da pipeline de processamento |