Cómo creé una herramienta para adaptar tu CV con el método oficial de Harvard

Hero
Tiempo estimado de lectura: 5 min
Mira la pantalla de inicio de ResumeTailor: de entrada ves dos tarjetas bien directas arriba, una para poner tu currículum (o hacer clic en exportar tu LinkedIn como PDF y subir el archivo) y otra para pegar la descripción del puesto. Justo abajo, el panel de resultados arma el documento final en tiempo real, mostrando las etiquetas con el modelo que usó y la duración exacta de cada etapa.
Y ojo acá: esta no es una herramienta para crear un currículum genérico y salir a dispararlo por todos lados. Para nada. ResumeTailor se diseñó siguiendo la guía oficial de Harvard para reescribir tu historial apuntando directo a una vacante específica. Ese es el objetivo.
El problema
Mandar el mismo PDF a cincuenta vacantes distintas es la receta perfecta para que te ignoren en diez segundos. Los filtros ATS y los reclutadores leen el documento buscando las palabras exactas del puesto. Si te postulaste para analista de datos con un texto lleno de backend, tu perfil se vuelve ruido en el filtro inicial.
Y hay otra cosa: mucha gente cae en la trampa de creer que un buen currículum tiene que ser larguísimo. No es así. Afuera, el mercado busca trayectoria y foco en lo que de verdad le sirve al puesto, no una lista interminable de todo lo que tocaste en los últimos diez años. Pero sentarte frente a la pantalla a reescribir a mano cada línea de tu experiencia en cada postulación te come horas de un trabajo fastidioso a morir.
Entonces la gente se desespera y mete todo en un prompt genérico de ChatGPT. El resultado es malísimo: la IA inventa métricas, mete herramientas que jamás tocaste y escupe un texto vacío de marketing que cualquier reclutador huele a kilómetros.
La solución
Para resolver esto armé ResumeTailor: una aplicación enfocada en agarrar tu historial y alinear cada línea con las pautas del manual oficial de carreras de Harvard.
El flujo es directo. Vas a agarrar tu currículum o exportar tu LinkedIn en PDF, cargarlo en la herramienta, pegar la descripción del puesto con los datos de la empresa y ahí sí vas a generar el documento adaptado junto con la cover letter.
Y mucha atención acá: no inventa datos. Hay un contrato estricto de grounding en el prompt que le prohíbe al modelo inventar empresas, métricas o puestos que no tienes. Es la verdad de tu carrera, pero con el vocabulario exacto de la vacante.
Ya puedes hacer pruebas directo en la pestaña de herramientas de mi sitio. Ese es el objetivo.
Funcionalidades
Para que todo funcione sin rodeos, armé la app con opciones bien directas:
- Entrada flexible de datos: puedes pegar el texto a mano o arrastrar un PDF directo. Si vas a tu perfil de LinkedIn y haces clic en exportar como PDF, la herramienta saca todo desde el propio navegador con PDF.js, sin mandar tu archivo a ningún servidor.
- Pipeline multimodelo en paralelo: al hacer clic en "Generar todas", la app lanza tres llamadas al mismo tiempo vía OpenRouter para Perfil, Experiencia y Habilidades. Si una sección falla, no te frena el resto, y encima puedes definir un modelo distinto para cada bloque.
- Modo de llamada única: si prefieres un tono parejo en todo el documento, este botón te genera el currículum de punta a punta y suma secciones extra como Educación, Certificaciones e Idiomas cuando tu historial original las respalda.
- Carta de presentación al estilo Harvard: pantalla dedicada con cinco campos opcionales. Y mucho ojo aquí: si dejas la empresa o el contacto en blanco, el prompt se adapta limpio en vez de inventar datos que no existen.
- Contacto editable y PDF limpio: el panel rescata tus enlaces y teléfono sin enredos, dejándote ajustar lo que quieras antes de generar un PDF A4 en el servidor, sin esos encabezados feos del navegador. Ese es el objetivo.
Stack técnico
Para levantar esta máquina sin meterle un backend pesado, armé una arquitectura bien directa.
En el front-end, la base corre con React 19, TypeScript 6.0 y Vite 8. La capa visual usa Tailwind CSS 4 con componentes de shadcn/ui y animaciones fluidas de Motion. Cuando subes tu PDF de LinkedIn, la extracción de texto pasa directo en tu máquina con PDF.js. Cero tráfico innecesario.
La llamada a la IA va directo desde el navegador a la API de OpenRouter vía streaming. El modelo por defecto es openai/gpt-5.6-sol-pro en esfuerzo máximo de reasoning (max), sin pasar por ningún servidor intermedio.
La única parte que corre en Node es la ruta POST /api/pdf. Hice un plugin a medida para Vite que convierte el markdown con markdown-it y levanta Puppeteer con Chromium headless para estampar el PDF A4 con la medida exacta. Para blindar todo esto en serio, escribí más de 840 tests en Vitest revisando desde los contratos de prompt XML hasta los invariantes del encabezado.
El desafío más interesante
El peor dolor de cabeza cuando pones a una IA a tocar tu currículum es la alucinación descarada. Si nada más le pides al modelo "adapta mi historial al manual oficial de Harvard", se emociona: te cambia los términos por verbos de acción llamativos y se inventa de la nada que lideraste cincuenta ingenieros o que duplicaste las ventas. Mentira total.
Para frenar esto en seco, armé una plantilla XML estructurada bajo el patrón goal-first. El archivo define un contrato de grounding estricto y divide la ejecución del razonamiento en etapas internas obligatorias antes de soltar la respuesta final:
<!-- src/assets/prompt.xml -->
<grounding_contract>
<rule id="resume" apply="true" />
</grounding_contract>
Exportar un PDF perfecto desde el servidor
Intentar imprimir un currículum directo desde el navegador con window.print() es una pesadilla. Chrome se empeña en estampar la fecha arriba, te clava la URL con about:blank al pie de página y hasta mete una hoja en blanco en la primera carátula por culpa del wrapper CSS de la app. Imposible mandar un documento así para el puesto de tus sueños.
La salida fue mandar a volar el diálogo del navegador y montar una ruta dedicada. El cliente manda el markdown por POST /api/pdf, un plugin de Vite compila todo en un HTML limpio y aislado, y Puppeteer corre Chromium en segundo plano para escupir un A4 impecable.
// 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);
}
La bandera displayHeaderFooter: false apaga cualquier basura del encabezado, mientras que el modo headless: 'shell' reduce el consumo de memoria sin obligarte a instalar todo Chrome completo. La contrapartida acá es clarísima: alojar solo archivos estáticos en un bucket sin servidor te rompe la generación del PDF. Para que ande en producción, vas a necesitar un entorno Node corriendo este middleware junto con la aplicación. Ese es el precio a pagar.
Lo que aprendí
Construir esta herramienta me dejó cuatro lecciones bien prácticas sobre desarrollo con modelos de razonamiento.
Primero: la lista de verbos de acción de Harvard marca una diferencia brutal. Cuando obligas al modelo a usar verbos contundentes en vez de dar vueltas con gerundios corporativos, el currículum cambia de nivel al instante.
Segundo: amarrar la app a un solo proveedor es pegarse un tiro en el pie. Dejar que el usuario elija cualquier modelo del catálogo de OpenRouter da libertad de costos y velocidad sin trabar el producto.
Tercero: pedir las cosas por favor en el prompt no frena las alucinaciones. Necesitas contratos explícitos de validación y filtros de control. Sin reglas comprobables, el modelo inventa datos.
Y por último, el razonamiento al máximo esfuerzo exige respetar los tiempos de la máquina. Aprendí a la mala que las peticiones normales se mueren a medio camino si uno no sostiene la conexión vía streaming SSE con comentarios de keep-alive. Paciencia programada.
¡Pruébalo!
La herramienta ya está arriba y lista para que la uses. Entra a https://frederico-kluser.com/tools y busca ResumeTailor.
La prueba más rápida que puedes hacer ahora es simple: entra a tu perfil de LinkedIn, dale clic a exportar como PDF y sube el archivo directo a la app. Si prefieres no meter tus datos reales de entrada, cero lío, puedes cargar los ejemplos de prueba listos en la interfaz. Agarras la descripción de esa vacante específica a la que tanto le traes ganas, la pegas ahí y generas tu material.
Pruébalo en la práctica, cuéntame qué te pareció y abre un issue con sugerencias en el repositorio. Y si tienes algún colega buscando trabajo, pásale este post.
Una postulación genérica va directo a la basura; postularte con precisión quirúrgica cambia las cosas por completo. Ese es el objetivo.