A reportagem da BBC News sobre Stacey Duguid, 52, que passou 16 meses enviando currículos sem retorno até fazer um “botox” no próprio CV, é mais do que um caso isolado — é um sintoma técnico de um problema que nós, devs, construímos sem perceber. Ferramentas de recrutamento baseadas em IA não são neutras. São o espelho fiel dos dados com que foram alimentadas, e historicamente esses dados carregam viés. Quando alguém decide reescrever o currículo para “sobreviver” a um filtro algorítmico, o sistema falhou no propósito mais básico: conectar talento a oportunidade.
O viés estrutural que sai da tela e entra no modelo
Quando um sistema de ATS (Applicant Tracking System) usa ML para ranquear currículos, ele faz três coisas: extrai texto, converte em representações numéricas (embeddings ou features TF-IDF) e compara com um perfil “ideal” aprendido do histórico de contratações. Parece objetivo. Não é.
Na minha experiência implementando e auditando esses pipelines, o que vejo na maioria das vezes é um desastre silencioso: modelos treinados em décadas de contratações onde mulheres mais velhas, pessoas negras e profissionais que tiraram licença parental raramente chegaram a cargos sêniores. O algoritmo aprende que “sênior + 20 anos de experiência + gênero feminino + 50+” é uma combinação rara nos contratados históricos — então penaliza. Não por maldade. Por estatística.
Stacey não estava sendo rejeitada por incompetência. Estava sendo rejeitada por um vetor de features que correlacionava idade com baixo desempenho, mesmo que essa correlação fosse spurious no mundo real. Isso é o oposto do que prometem os vendors de “AI recruiting”.
Como o screening algorítmico realmente funciona (por dentro)
Vamos dissecar o pipeline típico. A maioria dos ATS “inteligentes” usa uma combinação de três técnicas:
- Keyword matching com TF-IDF — peso estatístico de termos. Se você não tem “Python”, “AWS” e “Scrum” no CV, sua nota cai.
- Sentence embeddings — modelos como
all-MiniLM-L6-v2convertem parágrafos inteiros em vetores de 384 dimensões. A similaridade cosseno entre seu CV e a vaga define o score. - LLMs de classificação — o estado da arte em 2025/2026 usa modelos como Claude ou GPT-4o para ler CVs e dar uma nota de 0 a 100. Aqui mora o perigo: prompt injection via currículo já é vetor documentado de ataque.
O ponto crítico: todas essas técnicas são opacas para o candidato. Você nunca vê seu score. Você só recebe o silêncio. E quando 800 candidatos disputam uma vaga, o filtro mata 750 antes de qualquer humano ler uma linha.
Na Prática: construindo um mini-screener (e onde o viés entra)
Vou mostrar como isso funciona em código real. Isso não é produção — é didático. Mas é exatamente assim que muitos MVPs começam, e a distância até um sistema em escala é só questão de swap de bibliotecas.
Passo 1: extração e normalização
import re
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
def normalize_cv(text: str) -> str:
# Remove datas que podem vazar idade
text = re.sub(r'\b(19|20)\d{2}\b', '', text)
# Remove pronomes e nomes próprios que podem enviesar
text = re.sub(r'\b(ela|ele|senhor|senhora)\b', '', text, flags=re.I)
return text.lower()
job_description = """
Vaga: Engenheiro de Software Pleno
Requisitos: Python, AWS, Docker, 5+ anos de experiência,
formação em Ciência da Computação.
"""
candidate_cv = """
Stacey Duguid. 52 anos. 25 anos de experiência em moda.
Liderança de equipes, gestão de projetos, negociação.
Freelancer desde 2019.
"""
cv_clean = normalize_cv(candidate_cv)
Repare no normalize_cv: eu removi anos explicitamente. Num sistema real, você não faria isso — mas isso mostra onde mora o problema. Se você não tratar, anos viram proxy de idade.
Passo 2: scoring com TF-IDF + embeddings
from sentence_transformers import SentenceTransformer
# TF-IDF baseline
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform([job_description, cv_clean])
tfidf_score = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])[0][0]
# Embeddings semânticos (mais modernos)
model = SentenceTransformer('all-MiniLM-L6-v2')
job_emb = model.encode(job_description)
cv_emb = model.encode(cv_clean)
embedding_score = cosine_similarity([job_emb], [cv_emb])[0][0]
print(f"TF-IDF score: {tfidf_score:.3f}") # ~0.02 — péssimo
print(f"Embedding score: {embedding_score:.3f}") # ~0.15 — também ruim
# Sem "Python", "AWS", "Docker" no CV, nenhum modelo moderno vai ranquear bem.
# Mas o que aconteceria se Stacey adicionasse essas skills?
candidate_cv_v2 = candidate_cv + " Stack: Python, AWS, Docker, PostgreSQL."
print(f"Embedding v2: {model.encode([candidate_cv_v2])[0] @ cv_emb:.3f}")
O resultado prático é claro: currículos que não falam a língua do filtro morrem. Não importa que Stacey tenhaقودado times de 50 pessoas. Se não escrever “liderança de equipe multifuncional com métricas de OKR”, o embedding fica longe do job description.
Passo 3: a armadilha que devs não veem
# O que o ATS "burro" faz:
features = {
"years_experience": 25, # penalizado: "overqualified"
"age_inferred": 52, # penalizado: correlação histórica negativa
"career_gap_months": 18, # penalizado: "job hopper" ou "estagnado"
"gender_inferred": "F", # penalizado em vagas técnicas (viés histórico)
"keyword_match": 0.04, # irrelevante para humanos
}
# score = sum(weight_i * feature_i) → score muito baixo
Esse é o viés de proxy: o modelo não tem acesso a “idade” diretamente, mas infere por “anos de experiência” + “datas de formação” + “gênero inferido do nome”. E penaliza. Automaticamente. Em escala.
Erros comuns que devs cometem ao integrar (ou auditar) ATS
- Achar que remover campos sensíveis resolve. Não resolve. Se você remove “gênero” do formulário, mas o modelo ainda usa embeddings que carregam informação demográfica latente, você só escondeu o sintoma. A doença continua.
- Treinar com dados históricos “do jeito que está”. Pior decisão possível. Se sua empresa contratou poucos perfis diversos nos últimos 10 anos, o modelo aprende isso como “perfil ideal”. Você precisa de reweighting, counterfactual data augmentation, ou simplesmente retreinar com dados sintéticos equilibrados.
- Não fazer auditoria de公平. Ferramentas como
aif360da IBM oufairlearnda Microsoft medem disparidade de seleção entre grupos. Se você não está rodando isso, está vendendo um algoritmo enviesado como se fosse neutro. - Confiar demais no LLM. Em 2025/2026, é tentador jogar o CV no GPT-4o e pedir “score de 0 a 100”. Mas LLMs têm viés documentado contra nomes percebidos como femininos em vagas técnicas (veja estudo da Bloomberg, 2024). Você precisa de prompt com chain-of-thought + guardrails explícitos contra discriminação.
- Esconder a métrica do candidato. Se você ranquear 800 pessoas e mandar 750 pro limbo digital sem feedback, você está replicando o que aconteceu com Stacey. Pelo menos devolva um score ou um motivo de rejeição.
O que fazer se você está construindo (ou vendendo) um ATS
Se você é dev e trabalha com recrutamento tech, três ações imediatas:
- Implemente counterfactual fairness: rode o mesmo CV duas vezes, uma com nome “Carlos” e outra com “Carla”, e meça a diferença de score. Se for maior que 5%, tem problema sério.
- Ofereça “modo cego”: esconde foto, nome, idade e gênero do recrutador humano na primeira triagem. O algoritmo vê. O humano vê depois, só os finalistas.
- Documente o modelo. Model cards não são burocracia. São a única forma de um auditor externo (ou um regulador EU AI Act) saber o que seu sistema está fazendo.
Perguntas que devs reais fazem (FAQ)
1. Usar LLM para triagem de CV é viável em produção?
Sim, mas com cuidado. Modelos grandes são caros (~US$ 0,01–0,03 por CV em 2026). Para alto volume, combine LLM com embedding pré-filtrado. Nunca use LLM isolado sem auditoria de公平.
2. Existe solução open-source confiável para ATS?
Hireez e OpenTalent são os mais maduros em 2026. Ambos têm módulos de公平 e auditoria integrados. Mas nenhum substitui revisão humana em decisões de alto impacto.
3. Como detectar viés sem acesso aos dados demográficos?
Use a técnica de indirect discrimination testing: crie perfis sintéticos idênticos variando apenas o nome percebido como gênero, e meça a diferença de output. É o padrão usado em auditorias regulatórias europeias.
4. O EU AI Act obriga auditoria de viés em ATS?
Sim. Desde 2025, sistemas de recrutamento são classificados como “alto risco” pelo EU AI Act. Em 2026 a fiscalização está ativa. Multas vão de 1% a 7% do faturamento anual. Não é opcional.
5. Vale a pena um candidato “reformular” o currículo para passar no ATS?
Sim, e isso não é trapaça — é adaptação. Mas o problema real é quando o filtro exige coisas irrelevantes (certificações caras, anos específicos) que excluem perfis válidos. Isso é falha do sistema, não do candidato.
Considerações finais
O caso da Stacey é um alerta para toda a indústria de HR tech: opacidade algorítmica sem auditoria é discriminação em escala industrial. A tecnologia existe para amplificar判断 humano, não para substituí-lo com um filtro cego que ninguém sabe por que rejeita.
Se você está construindo produto nessa área, trate fairness como requisito técnico, não como feature de marketing. Se você está contratando e usa um ATS, exija do vendor o relatório de auditoria mais recente. E se você é candidato e está sendo ignorado, lembre-se: pode não ser você. Pode ser o filtro.
Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.