Prompt Generator

Índice


O que é

O processor-prompt-generator é o segundo serviço da pipeline de processamento do Kairos. Ele recebe os dados brutos de uma vaga ou currículo, seleciona o template de prompt adequado para a fonte de dados e injeta o conteúdo real no template, produzindo um histórico de chat estruturado pronto para ser consumido pelo Skill Extractor.

Evento recebido → Identificar source → Selecionar mapper → Injetar dados → Publicar prompt

A separação entre "geração de prompt" e "extração de skills" é intencional: permite que os templates evoluam independentemente do cliente Ollama, e que cada fonte de dados tenha seu próprio formato de prompt sem alterar código compartilhado.


Como Funciona

Fluxo de Funcionamento

Eventos Consumidos e Produzidos

TipoEventoConteúdo
ConsumidoOpportunityScrapedDados brutos da vaga (título, empresa, requisitos, atividades)
ConsumidoResumeUploadedTexto bruto do currículo submetido pelo usuário via API
ConsumidoResumeRawLoadedTexto bruto do currículo carregado de dataset (ex: Kaggle)
ProduzidoOpportunityPromptGeneratedLista de mensagens messages: list[dict]
ProduzidoResumePromptGeneratedLista de mensagens messages: list[dict]

O campo messages substitui o enriched_prompt: str que existia na versão anterior — é uma lista de mensagens no formato da API /api/chat do Ollama, com roles system, user e assistant.

Decisões Abordadas

Strategy Pattern com Mapper Registry (ADR-009): diferentes fontes de dados (Mackenzie, Kaggle, API) têm estruturas distintas. Em vez de um if/elif crescente por source, cada fonte tem sua própria classe mapper registrada centralmente. Adicionar suporte a uma nova fonte não exige modificar código existente (Open/Closed Principle) — basta criar um novo arquivo com a classe e registrá-la.

Estrutura de 6 mensagens com few-shot no histórico de chat (ADR-042): fornecer respostas prontas no role assistant ensina o modelo a imitar exatamente a estrutura do JSON de saída e a categorização hard/soft/knowledge. Isso reduz falhas de parsing e inconsistências de classificação em SLMs sem exigir fine-tuning. A alternativa de colocar os exemplos no system prompt foi rejeitada porque modelos menores tendem a ignorar exemplos quando misturados às instruções.

Técnicas do system.md (ADR-038): o conteúdo do prompt de sistema usa role setting, XML tags, negative constraints e template JSON nativo. Essas técnicas foram validadas como mais eficazes do que prompts em linguagem natural livre para manter consistência em SLMs de 1B de parâmetros.


Strategy Pattern + Mapper Registry

Diferentes fontes de dados (Mackenzie, Kaggle, API) têm estruturas distintas. Em vez de um if/elif crescente por source, o serviço implementa o Strategy Pattern com um Registry central (ADR-009):

Os mappers são registrados centralmente no __init__.py do pacote. Para adicionar suporte a uma nova fonte, basta criar um novo arquivo com a classe e adicioná-la ao registro — sem modificar código existente (Open/Closed Principle).

class MackenziePromptMapper(BasePromptMapper):
    FIELD_MAPPING = {
        "position": "titulo",
        "company": "empresa",
        "requirements": "requisitos",
        "activities": "atividades",
    }

    @property
    def source(self) -> str:
        return "mackenzie_portal_carreiras"

    def map_to_messages(self, data: dict) -> list[dict]:
        fields = {
            key: data.get(src) or self.DEFAULT_VALUE
            for key, src in self.FIELD_MAPPING.items()
        }
        user_content = OPPORTUNITY_USER_MACKENZIE_TEMPLATE.format(**fields)
        return [
            {"role": "system", "content": OPPORTUNITY_SYSTEM_PROMPT},
            *OPPORTUNITY_FEW_SHOT_EXAMPLES,
            {"role": "user", "content": user_content},
        ]

O registro de todos os mappers é feito em processor_prompt_generator/__init__.py:

PromptMapperRegistry.register(MackenziePromptMapper())
PromptMapperRegistry.register(KaggleOpportunityPromptMapper())
PromptMapperRegistry.register(ResumeKagglePromptMapper())
PromptMapperRegistry.register(ResumeApiPromptMapper())

Estrutura Multi-Role Chat (Few-Shot)

O histórico de chat enviado ao Ollama segue uma estrutura de 6 mensagens que combina instrução, exemplos e dados reais (ADR-042):

PosiçãoRoleConteúdoArquivo
1systemTaxonomia ESCO + regras de extração + restrições negativassystem.md
2userExemplo fictício de entrada (vaga/currículo de treino)few_shot_1_user.md
3assistantResposta JSON esperada para o exemplo 1few_shot_1_assistant.json
4userSegundo exemplo fictício de entradafew_shot_2_user.md
5assistantResposta JSON esperada para o exemplo 2few_shot_2_assistant.json
6userDados reais da vaga ou currículo atual<source>_user.md (template)
Por que Few-Shot?

O aprendizado por analogia (fornecer respostas prontas no role assistant) ensina o modelo a imitar exatamente a estrutura do JSON de saída e a categorização hard/soft/knowledge. Reduz drasticamente falhas de parsing e inconsistências de classificação em SLMs.

Organização de Arquivos de Recursos

Os templates e exemplos são armazenados fora do código Python para facilitar governança e atualização sem deploy:

services/processor-prompt-generator/
└── resources/
    ├── opportunity/
    │   ├── system.md
    │   ├── few_shot_1_user.md
    │   ├── few_shot_1_assistant.json
    │   ├── few_shot_2_user.md
    │   ├── few_shot_2_assistant.json
    │   ├── mackenzie_user.md
    │   └── kaggle_user.md
    └── resume/
        ├── system.md
        ├── few_shot_1_user.md
        ├── few_shot_1_assistant.json
        ├── few_shot_2_user.md
        ├── few_shot_2_assistant.json
        ├── kaggle_user.md
        └── api_user.md

Os arquivos são lidos em tempo de importação do serviço e mantidos em cache. Cada mapper usa .format() nativo do Python para injetar os dados reais no template <source>_user.md.


Técnicas de Prompt Engineering (ADR-038)

O conteúdo do system.md aplica as seguintes estratégias para maximizar a consistência de SLMs:

TécnicaImplementaçãoBenefício
Role/Persona Setting"Você é um Consultor de Carreira Especialista em Análise de Competências"Foca os pesos atencionais do modelo no escopo correto
XML Tags<instruction>, <rules>, <data>, <output_format>Separa estruturalmente instruções de conteúdo
Negative Constraints"NÃO alucine", "NÃO adicione texto fora do JSON"Reduz tendências indesejadas sem exigir fine-tuning
JSON nativoTemplate JSON + enforçamento via structured outputGarante schema consistente para consumo downstream
Manutenção de Prompts

Cada nova fonte de dados requer a criação de um novo mapper, um novo arquivo <source>_user.md e o registro do mapper em __init__.py. O system.md e os exemplos few-shot são compartilhados entre sources do mesmo domínio (opportunity ou resume).


Janela de Contexto

O histórico com 2 exemplos few-shot + dados reais pode ser extenso. O processor-skill-extractor usa num_ctx=8192 tokens para garantir que mesmo currículos longos não sejam truncados.

system (regras)          ~500 tokens
few_shot_1 (user)        ~200 tokens
few_shot_1 (assistant)   ~100 tokens
few_shot_2 (user)        ~200 tokens
few_shot_2 (assistant)   ~100 tokens
user (dados reais)       até ~6000 tokens
─────────────────────────────────────────
Total                    até ~7100 tokens  (< 8192)

ADR's Relacionadas

ADRDataDecisão
ADR-0092026Strategy Pattern com Mapper Registry para extensibilidade por fonte de dados
ADR-0382026Técnicas de prompt engineering para SLMs (role setting, XML tags, negative constraints)
ADR-0422026Estrutura de 6 mensagens com few-shot no histórico de chat Multi-Role