Como criei uma ferramenta para adaptar currículo usando o método oficial de Harvard

Hero
Tempo estimado de leitura: 5 min
Olha pra tela inicial do ResumeTailor: de cara você vê dois cards bem diretos no topo, um pra colocar o seu currículo (ou clicar em exportar o teu LinkedIn como PDF e subir o arquivo) e outro pra colar a descrição da vaga de trabalho. Logo abaixo, o painel de resultados monta o documento final em tempo real, exibindo os badges com o modelo utilizado e a duração exata de cada etapa.
E muita atenção aqui: esta não é uma ferramenta feita pra criar currículo genérico pra você sair disparando por aí. Não mesmo. O ResumeTailor foi desenhado usando o manual oficial da Harvard para reescrever o seu histórico com foco total em uma vaga específica. É esse o objetivo.
O problema
Mandar o mesmo PDF para cinquenta vagas diferentes é a receita perfeita para ser ignorado em dez segundos. Filtros ATS e recrutadores leem o documento procurando o vocabulário exato da vaga, e se você aplicou para analista de dados com um texto de backend, seu perfil vira ruído na triagem.
E tem outra: muita gente cai na ilusão de que currículo bom é currículo quilométrico. Não é. O mercado lá fora quer ver longevidade e foco no que realmente interessa para a posição, não uma lista infinita de tudo o que você tocou nos últimos dez anos. Só que sentar na frente do computador e reescrever cada linha do histórico na mão a cada candidatura consome horas de um trabalho chato pra caramba.
Aí a galera apela: joga tudo num prompt genérico do ChatGPT. O resultado é péssimo, com a IA inventando números, criando ferramentas que você nunca usou e cuspindo um texto vazio de marketing que qualquer recrutador fareja de longe.
A solução
Para resolver isso, criei o ResumeTailor: uma aplicação focada em pegar o seu histórico e alinhar cada linha às diretrizes do manual oficial de carreiras de Harvard.
O fluxo é direto. Você vai pegar o seu currículo ou clicar em exportar o teu LinkedIn como PDF, postar na ferramenta, colar a descrição da vaga com os dados da empresa e aí sim gerar o documento adaptado junto com a cover letter.
E muita atenção aqui: ela não inventa fatos. Existe um contrato rígido de grounding no prompt que proíbe o modelo de inventar empresas, métricas ou títulos que você não tem. É a verdade da sua carreira, mas no vocabulário exato da vaga.
Você já pode rodar seus testes direto na aba de ferramentas do meu site. É esse o objetivo.
Funcionalidades
Pra fazer a mágica acontecer na prática, estruturei o app com recursos bem diretos ao ponto:
- Entrada flexível de dados: você pode colar o texto na mão ou simplesmente arrastar um PDF. Se você for no seu perfil do LinkedIn e clicar em exportar como PDF, a ferramenta extrai tudo no próprio navegador via PDF.js, sem mandar teu arquivo pra servidor nenhum.
- Pipeline multi-modelo em paralelo: ao clicar em "Gerar todas", o app dispara três chamadas simultâneas via OpenRouter para Sumário, Experiência e Habilidades. Se uma seção der erro, ela não trava o resto, e você ainda pode definir um modelo diferente pra cada bloco.
- Modo de chamada única: se preferir uma voz idêntica no documento todo, esse botão gera o currículo de ponta a ponta e traz seções extras como Formação, Certificações e Idiomas quando seu histórico original sustenta isso.
- Carta de apresentação no padrão Harvard: tela dedicada com cinco campos opcionais. E muita atenção aqui: se você deixar a empresa ou o contato em branco, o prompt degrada com elegância em vez de inventar dados inexistentes.
- Contato editável e PDF limpo: o painel recupera seus links e telefone de forma transparente, permitindo ajustes antes de gerar um PDF A4 no servidor, sem cabeçalhos feios do navegador. É esse o objetivo.
Stack técnica
Pra colocar essa máquina de pé sem backend pesado, fechei a arquitetura com peças bem diretas.
No front-end, a base roda com React 19, TypeScript 6.0 e Vite 8. A casca visual usa Tailwind CSS 4 com componentes do shadcn/ui, somada a animações fluidas do Motion. Quando você sobe teu PDF do LinkedIn, a extração de texto acontece direto na sua máquina usando PDF.js. Zero tráfego desnecessário.
A chamada de IA vai direto do navegador pra API do OpenRouter via streaming. O modelo padrão é o openai/gpt-5.6-sol-pro em esforço máximo de reasoning (max), sem passar por servidor intermediário.
O único pedaço que roda em Node é a rota POST /api/pdf. Criei um plugin customizado pro Vite que converte o markdown com markdown-it e aciona o Puppeteer com Chromium headless pra carimbar o PDF A4 na régua exata. Pra amarrar tudo com segurança, escrevi mais de 840 testes no Vitest checando desde os contratos de prompt XML até os invariantes de cabeçalho.
O desafio mais interessante
O maior pesadelo ao colocar IA pra mexer em currículo é a alucinação descarada. Se você só pede pro modelo "adaptar meu histórico pro manual oficial da Harvard", ele se empolga: troca seus termos por verbos de ação incríveis e inventa do nada que você liderou cinquenta engenheiros ou dobrou faturamento. Mentira pura.
Pra travar esse comportamento, montei um template XML estruturado no padrão goal-first. O arquivo define um contrato de grounding severo e divide a execução do raciocínio em etapas internas obrigatórias antes da emissão da resposta final:
<!-- src/assets/prompt.xml -->
<grounding_contract>
<rule id="resume" apply="true" />
</grounding_contract>
Exportando PDF perfeito no servidor
Tentar imprimir currículo direto pelo navegador usando window.print() é pedir pra passar raiva. O Chrome insiste em estampar a data no topo, tacha a URL com about:blank no rodapé e ainda consegue enfiar uma folha em branco na primeira página por causa de wrapper CSS da aplicação. Não dá pra mandar um documento assim pra vaga dos seus sonhos.
A saída foi chutar o diálogo do navegador e montar uma rota dedicada. O cliente manda o markdown via POST /api/pdf, um plugin do Vite compila isso num HTML limpo e isolado, e o Puppeteer roda o Chromium em segundo plano pra cuspir um A4 impecável.
// server/vite-plugin-resume-pdf.ts
import { createPdfMiddleware } from './pdfMiddleware';
import puppeteer from 'puppeteer';
export async function renderA4Pdf(htmlContent: string): Promise<Buffer> {
const browser = await puppeteer.launch({ headless: 'shell' });
const page = await browser.newPage();
await page.setContent(htmlContent, { waitUntil: 'networkidle0' });
const pdfBuffer = await page.pdf({
format: 'A4',
printBackground: true,
displayHeaderFooter: false,
margin: { top: '0px', bottom: '0px', left: '0px', right: '0px' }
});
await browser.close();
return Buffer.from(pdfBuffer);
}
A flag displayHeaderFooter: false desliga qualquer sujeira de cabeçalho, enquanto o modo headless: 'shell' reduz o consumo de memória sem exigir a instalação pesada do Chrome inteiro. O contraponto aqui é claro: hospedar só arquivos estáticos num bucket sem servidor quebra a geração do PDF. Pra funcionar em produção, você vai precisar de um runtime Node rodando esse middleware junto com o app. É esse o trade-off.
O que aprendi
Construir essa ferramenta me ensinou quatro coisas bem práticas sobre desenvolvimento com modelos de raciocínio.
Primeiro: o catálogo de verbos de ação de Harvard faz uma diferença brutal. Quando você força o modelo a usar verbos de impacto em vez de enrolar em gerúndio corporativo, o currículo ganha outra postura na hora.
Segundo: prender o app em um único provedor é tiro no pé. Deixar o usuário escolher qualquer modelo pelo catálogo da OpenRouter garante liberdade de custo e velocidade sem engessar o produto.
Terceiro: pedir com educação no prompt não resolve alucinação. Você precisa de contratos explícitos de validação e portões de checagem. Sem regra decidível, o modelo inventa dados.
E por último, raciocínio em esforço máximo exige respeitar o tempo da máquina. Aprendi na marra que requisições comuns morrem pelo caminho se a gente não segurar a conexão via streaming SSE com comentários de keep-alive. Paciência programada.
Experimente!
A ferramenta tá no ar e pronta pra você rodar. Entra lá em https://frederico-kluser.com/tools e procura pelo ResumeTailor.
O teste mais rápido que você pode fazer agora é simples: vai no teu perfil do LinkedIn, clica em exportar como PDF e sobe o arquivo direto na aplicação. Se preferir não mexer nos teus dados logo de cara, sem problema, dá pra carregar os exemplos fictícios prontos na interface. Pega a descrição daquela vaga específica que você tanto quer, joga lá e gera o material.
Testa na prática, me diz o que achou e abre uma issue com sugestões de melhoria no repositório. Se tiver algum colega na busca por recolocação, passa esse post pra ele.
Candidatura genérica vai direto pro lixo; aplicar com foco cirúrgico muda o jogo. É esse o objetivo.