Cómo construí Elisa, el chatbot de mi portafolio
Un asistente que responde preguntas sobre mi CV con NLP.js, entrenado en el build y servido en ~40 ms desde Vercel.
2 min de lectura
- NLP
- SvelteKit
- Vercel
En este artículo
Mi portafolio tiene un chat: Elisa. Puedes preguntarle dónde trabajo, qué hice en una empresa concreta o si sé Docker, y te responde a partir de mi CV. En este artículo cuento cómo está hecha y qué decisiones tomé.
#Por qué no un LLM
La opción obvia hoy sería conectar un modelo de lenguaje grande con el CV como contexto. Lo descarté por tres razones:
- Costo: cada mensaje tendría un precio, y un portafolio no debería tenerlo.
- Control: quiero que Elisa diga exactamente lo que está en mi CV, sin inventar.
- Velocidad: una clasificación local responde en milisegundos.
En su lugar uso NLP.js (se abre en una pestaña nueva), que clasifica cada pregunta en una intención y extrae entidades como empresas o tecnologías.
#Datos separados de las frases
El conocimiento vive en un JSON con mis empresas, cargos, fechas y tecnologías. Las frases de entrenamiento viven en otro, con marcadores para las entidades:
{
"intent": "experiencia.empresa",
"utterances": ["qué hizo en @company", "cuál fue su rol en @company"]
}Así, «¿qué hizo en IBM?» y «¿qué hizo en Consorcio?» caen en la misma intención, y la respuesta se construye con los datos de la empresa detectada.
#Contexto entre preguntas
Si preguntas «¿qué hizo en IBM?» y luego «¿y qué stack?», Elisa recuerda de qué empresa hablabas. El servidor devuelve un pequeño objeto de contexto y el cliente lo reenvía en el siguiente mensaje.
#Entrenar en el build
Al principio el modelo se entrenaba al arrancar el servidor. En Vercel eso significaba casi un segundo extra en cada arranque en frío. Ahora un plugin de Vite entrena durante el build y la función solo importa el modelo:
function trainBot(): Plugin {
return {
name: 'train-bot',
apply: 'build',
async buildStart() {
const { trainBot } = await import('./scripts/train-bot.js');
await trainBot();
}
};
}| Antes | Ahora | |
|---|---|---|
| Cargar la librería | ~420 ms | ~30 ms |
| Preparar el modelo | ~105 ms | ~7 ms |
| Primera respuesta | ~450 ms | ~5 ms |
La mayor ganancia no vino del entrenamiento, sino de cargar solo el paquete de español en lugar de los 40 idiomas de
node-nlp.
#Lo que aprendí
Un clasificador de intenciones no razona, pero con buenos datos, reglas simples para las entidades y un conjunto de pruebas con preguntas reales se siente sorprendentemente natural. Y medir antes de optimizar, siempre.