Skill Extractor
Índice
O que é
O processor-skill-extractor é o terceiro serviço da pipeline de processamento. Ele recebe o histórico de chat estruturado produzido pelo Prompt Generator, envia para o modelo Gemma3 1B via Ollama com Structured Output habilitado e retorna uma lista de habilidades candidatas classificadas por tipo (hard, soft, knowledge).
messages: list[dict] → Ollama API (/api/chat) → JSON validado → SkillCandidateExtracted
A extração usa zero-shot learning combinado com few-shot no histórico de chat — o modelo não foi fine-tuned, mas aprende pelo contexto dos exemplos embutidos no prompt.
Como Funciona
Fluxo de Funcionamento
Eventos Consumidos e Produzidos
| Tipo | Evento | Conteúdo |
|---|---|---|
| Consumido | OpportunityPromptGenerated | messages: list[dict], metadados da vaga |
| Consumido | ResumePromptGenerated | messages: list[dict], metadados do currículo |
| Produzido | HardSkillCandidateExtracted | Candidatos com type == "hard" |
| Produzido | SoftKnowledgeCandidateExtracted | Candidatos com type in ("soft", "knowledge") |
A separação em dois eventos de saída (hard vs. soft/knowledge) é uma decisão de roteamento do ADR-045: cada tipo segue um algoritmo diferente no Entity Linker downstream, por isso já são separados aqui na origem.
Decisões Abordadas
Gemma3 1B como modelo: quatro modelos foram avaliados com os mesmos parâmetros e prompt. O Gemma3 1B foi o único que capturava termos compostos ("machine learning engineering") e preenchia todos os campos do schema (position, company, hard_skills, soft_skills, knowledge_skills) de forma consistente. Modelos acima de 1B foram descartados: ao tentar ser mais elaborados, gastam tokens em contexto e explicações em vez de retornar a lista de forma objetiva.
Temperatura 0 para determinismo: o mesmo currículo deve gerar sempre as mesmas skills — variabilidade estocástica tornaria o sistema não-reproduzível e dificultaria a comparação de resultados entre execuções do pipeline.
Separação dos eventos de saída em dois tipos (hard vs. soft/knowledge): o Entity Linker usa algoritmos diferentes por tipo (fuzzy-first para hard, Duplo Diamante para soft/knowledge). Separar na origem elimina lógica de roteamento dentro do Entity Linker e torna cada consumer mais coeso.
Schema de Saída (Structured Output)
O Ollama enforça o schema via JSON Schema antes de retornar a resposta — respostas malformadas são rejeitadas no nível do modelo:
{
"type": "object",
"required": ["position", "company", "hard_skills", "soft_skills", "knowledge_skills"],
"properties": {
"position": { "type": "string" },
"company": { "type": "string" },
"hard_skills": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"soft_skills": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"knowledge_skills": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
}
}
Após o retorno do Ollama, o serviço valida novamente via Pydantic para garantir que o payload do evento downstream esteja correto antes de publicar no RabbitMQ.
Parâmetros de Inferência
| Parâmetro | Valor | Racional |
|---|---|---|
temperature | 0.0 | Output determinístico — o mesmo texto deve gerar sempre as mesmas skills |
top_k | 40 | Limita o vocabulário aos 40 tokens mais prováveis, reduzindo criatividade desnecessária |
top_p | 0.95 | Inclui tokens que somam 95% da probabilidade acumulada |
min_p | 0.05 | Descarta tokens com probabilidade abaixo de 5%, removendo ruído |
repeat_penalty | 1.0 | Sem penalização de repetição — skills podem se repetir entre domínios |
num_ctx | 8192 | Janela de contexto expandida para suportar o histórico few-shot + texto completo |
O Ollama implementa Structured Output via guided decoding: o modelo só pode emitir tokens que mantêm o JSON parcial em estado válido segundo o schema fornecido. Isso é mais robusto do que apenas pedir JSON no prompt.
Mitigação de Alucinações
SLMs podem inventar habilidades que não estão no texto original. O serviço aplica três camadas de defesa:
1. No prompt (Negative Constraints):
"Extraia SOMENTE skills que estão EXPLICITAMENTE mencionadas no texto"
"NÃO invente, infira ou suponha skills que não estão escritas"
2. Na inferência (temperatura 0): Temperatura zero elimina a variabilidade estocástica do modelo, tornando o output reproduzível e mais conservador.
3. No schema (Structured Output + Pydantic):
Skills inventadas ainda podem aparecer, mas o formato estruturado garante que o payload sempre seja processável. O Entity Linker downstream trata skills sem match na ESCO como SkillCustom — elas não são descartadas, mas são sinalizadas para revisão.
Habilidades sem correspondência na ESCO (alucinações ou termos legítimos não catalogados) são marcadas como SkillCustom pelo Entity Linker. O histórico de processamento persiste ambas as categorias para análise futura e eventual fine-tuning.
ADR's Relacionadas
| ADR | Data | Decisão |
|---|---|---|
| ADR-018 | 2026 | Seleção do Gemma3 1B após comparação de 4 modelos |
| ADR-045 | Jun 2026 | Separação de eventos de saída em hard vs. soft/knowledge para roteamento no Entity Linker |