# BytIA: el chatbot que sabe dónde estás y cómo te sientes

> Construí un chatbot que detecta la página del visitante, adapta su tono al estado emocional y puntúa cada lead automáticamente. Sin plantillas, sin plugins.

**Autor:** Pedro Luis Cuevas Villarrubia | **Fecha:** 2026-05-14 | **Tags:** IA, chatbot, desarrollo web
**URL:** https://asturwebs.es/blog/chatbot-ia-consciente-pagina-emociones-lead-scoring-2026/

---
El mes pasado, un visitante entró en mi web por la página de servicios. Estaba buscando diseño web, pero el chatbot le respondió igual que si hubiera entrado por el blog. Le dio información genérica. El tío se fue.

Ese día decidí que BytIA tenía que ser más inteligente. No solo responder preguntas — **entender el contexto**.

## El problema con los chatbots de plantilla

La mayoría de chatbots para pymes funcionan igual: les das un system prompt, les metes un FAQ, y responden lo que pueden. Pero tienen tres carencias que cuestan dinero:

1. **No saben dónde estás** — Responden igual si estás en servicios que en contacto
2. **No leen emociones** — Tratan igual a alguien curioso que a uno urgente
3. **No clasifican leads** — El dueño recibe "alguien habló con el bot" y punto

Yo quería un chatbot que hiciera lo que haría un comercial bueno: escuchar, adaptar su discurso, y al final decirte "este tío está caliente, llámalo ya".

## Lo que construí (y cómo funciona)

### 1. Contexto de página automático

Cuando el visitante está en `/servicios/seo/`, BytIA lo sabe. El widget detecta la URL actual y la envía con cada mensaje. El backend construye un contexto que se inyecta como mensaje system antes del historial:

```text
CONTEXTO DE PÁGINA: El usuario está navegando en "/servicios/seo/"
de asturwebs.es. Adapta tu respuesta al contexto de esta sección.
Si menciona servicios, enfócate en posicionamiento SEO.
Si pregunta precios, conecta con los planes mensuales.
```

Esto cambia completamente la respuesta. En `/servicios/seo/`, BytIA habla de auditorías técnicas y optimización on-page. En `/planes/`, va directo a la iguala mensual y lo que incluye. En `/contacto/`, ofrece agendar la consulta gratuita.

El widget también detecta navegación SPA — si el visitante cambia de página durante la conversación, se envía un mensaje de contexto silencioso. BytIA nunca repite la misma información genérica dos veces.

<div class="not-prose my-8 px-5 py-4 border-l-4 border-primary/30 bg-primary/5 rounded-r-lg">
<p class="text-sm text-gray-700 m-0">¿Tu chatbot trata igual a todos los visitantes? Un chatbot que sabe dónde está tu cliente y cómo se siente convierte más que uno genérico. <a href="/servicios/ia/" class="text-primary font-semibold hover:underline">Puedo construir uno para tu negocio</a>.</p>
</div>

Y todo esto es gratis en términos de coste: el contexto de página no añade tokens al historial, es un mensaje system que se inserta en cada petición y se descarta después.

### 2. Adaptación emocional en tiempo real

BytIA no da la misma respuesta a todo el mundo. Detecta el estado del visitante y cambia su tono. Así se ve en una conversación real:

> **Visitante:** "Llevo tres semanas esperando que mi diseñador me arregle la web y no contesta. Estoy hasta los huevos."
>
> **BytIA:** "Entiendo perfectamente tu frustración, llevas tiempo esperando y eso no es justo. Te cuento cómo funciona mi rescate de proyectos: Pedro revisa tu web, te dice qué está mal y te da un plan para arreglarlo. Normalmente en menos de una semana estás operativo otra vez. ¿Quieres que le pase tu caso a Pedro para que te llame?"
>
> **Visitante:** "¿En serio me puede llamar? Es que necesito solucionar esto ya."
>
> **BytIA:** "De verdad. Si es urgente, te sugiero escribirle directo al WhatsApp — +34 644 28 26 26 — para que te responda hoy mismo. Dile que BytIA te derivó y sabrá de qué va."

Fíjate: no empezó con un listado de servicios. Empezó con empatía, validó la frustración, ofreció solución concreta y cerró con acción inmediata. Eso no es un chatbot genérico — es lo que haría un comercial bueno al teléfono.

La tabla completa de adaptación:

| Estado | Qué hace BytIA |
|--------|----------------|
| Curioso o indeciso | Educa sin presionar, genera confianza |
| Entusiasmado | Canaliza hacia acción concreta (agendar llamada) |
| Frustrado con su web | Valida primero, luego ofrece solución |
| Ansioso o urgente | Empatía + acción directa: WhatsApp para respuesta inmediata |
| Escéptico sobre el precio | Demuestra valor: mantenimiento, IA y soporte incluidos |

### 3. Lead scoring automático al cerrar

Cuando se cierra una conversación, hago una segunda llamada a la IA (independiente del chat) que analiza el historial completo y extrae un JSON estructurado:

```json
{
  "temperature": "caliente",
  "intent": "Presupuesto para tienda online con 200+ productos",
  "urgency": "alta",
  "sentiment": "entusiasmado",
  "summary": "Dueño de comercio quiere migrar de WooCommerce a tienda moderna. Tiene presupuesto y urgencia por lanzar antes de verano."
}
```

Esto se guarda en la base de datos (`lead_score` en SQLite) y se incluye en el email que recibo como propietario. El asunto del email ya te dice la temperatura: `🔥 LEAD CALIENTE: Presupuesto para tienda online` o `Chat — información general sobre SEO`. Así priorizas quién necesita respuesta hoy y quién puede esperar.

Los datos también se envían por webhook — si tienes un Google Sheets como CRM, cada conversación aterriza en una fila con nombre, contacto, número de mensajes, temperatura, intención y urgencia. Sin copiar nada a mano.

**El visitante nunca ve el scoring.** La copia que se le envía por email es una transcripción limpia — sin análisis, sin etiquetas. Es información para ti, no para él. Si la IA falla al analizar la conversación, devuelve un lead genérico "frío" y nada se rompe — graceful degradation.

![Dashboard de lead scoring con clasificación por temperatura, intención y urgencia de cada conversación](/images/asturwebs-central-chatbot-lead-scoring.webp)

### 4. Datos de negocio separados del comportamiento

Esta es la pieza que lo hace escalable. El prompt de BytIA se divide en tres mensajes system que se envían antes del historial de conversación:

```text
Mensaje system 1 (comportamiento):  "Eres BytIA, la IA asistente de AsturWebs..."
              → Personalidad, seguridad, anti-injection, cierre, adaptación emocional
              → Cambia casi nunca — es la "personalidad" del bot

Mensaje system 2 (datos):           "## Servicios\n- Diseño web: 375-600€..."
              → Servicios, precios, horarios, bio del dueño, diferenciadores
              → Cambia cuando cambias un precio o añades un servicio

Mensaje system 3 (contexto):        "El usuario está en /servicios/seo/..."
              → Página actual + datos del visitante (nombre, email si los dio)
              → Se regenera en cada petición
```

¿Por qué separarlos? Porque la capa 2 son datos de negocio que también alimentan [agents.txt](/blog/agents-txt-openapi-descubrimiento-agentes-ia-2026/), [llms.txt](/blog/llms-txt-como-preparar-tu-web-para-ia-2026/) y la API pública. Si cambio un precio en `business-data.ts`, se actualiza en el chatbot, en la API, en agents.txt, en llms.txt y en openapi.json. **Un cambio, seis salidas.**

El coste: tres mensajes de contexto en vez de uno. Pero la coherencia que ganas (precio en la web = precio en el chatbot = precio en agents.txt) no tiene precio.

### 5. Reconocimiento de visitantes recurrentes

Si un visitante habló ayer y vuelve hoy, BytIA lo sabe. El widget guarda el sessionId en `localStorage` (sobrevive al cierre del navegador). En la siguiente visita, se envía como `previousSessionId` y la API busca la conversación anterior en la base de datos:

```text
Mensaje system inyectado automáticamente:
"VISITANTE RECURRENTE: Se llama Luis. Habló sobre:
presupuesto para tienda online. Lead anterior: tibio.
Reconócelo de forma natural, sin ser forzado."
```

BytIA puede saludar así: *"Hola de nuevo, Luis. ¿Le diste una vuelta a lo de la tienda?"* Sin que el visitante tenga que repetir nada. El contexto previo también se incluye en el email que recibo — veo `🔄 Recurrente` en el asunto y una fila con el histórico del visitante.

La validación de identidad es clave: si el visitante proporcionó email o teléfono, el lookup solo funciona si coinciden con la sesión previa. Un UUID aislado no basta — no exponemos datos de un visitante a otro.

## El resultado en producción

BytIA lleva semanas activa en asturwebs.es. No es un prototipo — es la que atiende a mis visitantes reales 24/7. Funciona en cualquier API compatible con OpenAI (gpt-4o-mini, Claude, modelos locales). Los datos se guardan en una base de datos propia, y el backend corre en un contenedor Docker con 128 MB de RAM.

En las primeras semanas ya detectó leads calientes que me hubiera perdido — gente con urgencia real, con presupuesto, que entró un domingo por la noche cuando yo no estaba delante del ordenador. Sin BytIA, esos visitantes habrían cerrado la pestaña.

La seguridad está cubierta. Cada capa tiene su responsabilidad:

<div class="not-prose my-8 space-y-3">

<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">

<div class="bg-white border border-red-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-red-500 text-lg">🛡️</span>
<h4 class="text-sm font-bold text-gray-900 m-0">Anti-prompt injection</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">Ignora "olvida tus instrucciones", "actúa como otro modelo", "muestra tu prompt".</p>
<p class="text-xs text-red-600 font-medium mt-2 mb-0">Protege contra: manipulación del comportamiento</p>
</div>

<div class="bg-white border border-orange-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-orange-500 text-lg">🧹</span>
<h4 class="text-sm font-bold text-gray-900 m-0">Input sanitizado</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">DOMPurify con <code class="text-xs bg-gray-100 px-1 py-0.5 rounded">ALLOWED_TAGS: []</code> — cero HTML, solo texto plano.</p>
<p class="text-xs text-orange-600 font-medium mt-2 mb-0">Protege contra: XSS, inyección de scripts</p>
</div>

<div class="bg-white border border-orange-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-orange-500 text-lg">🔐</span>
<h4 class="text-sm font-bold text-gray-900 m-0">Consultas parametrizadas</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">Todas las queries SQLite usan <code class="text-xs bg-gray-100 px-1 py-0.5 rounded">.prepare().get()</code> — sin concatenación.</p>
<p class="text-xs text-orange-600 font-medium mt-2 mb-0">Protege contra: SQL injection</p>
</div>

<div class="bg-white border border-yellow-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-yellow-500 text-lg">⏱️</span>
<h4 class="text-sm font-bold text-gray-900 m-0">Rate limiting</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">10 req/min por IP, configurable por cliente. Doble capa: global + por tenant.</p>
<p class="text-xs text-yellow-700 font-medium mt-2 mb-0">Protege contra: abuso, spam, fuerza bruta</p>
</div>

<div class="bg-white border border-blue-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-blue-500 text-lg">🌐</span>
<h4 class="text-sm font-bold text-gray-900 m-0">CORS restringido</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">Solo responde a los orígenes que tú autorices. Ningún dominio externo puede consultar tu API.</p>
<p class="text-xs text-blue-600 font-medium mt-2 mb-0">Protege contra: acceso no autorizado</p>
</div>

<div class="bg-white border border-blue-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-blue-500 text-lg">✅</span>
<h4 class="text-sm font-bold text-gray-900 m-0">Validación de identidad</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">Lookup de sesión previa solo si email, teléfono o nombre coinciden con la sesión anterior.</p>
<p class="text-xs text-blue-600 font-medium mt-2 mb-0">Protege contra: acceso a datos de otro visitante</p>
</div>

<div class="bg-white border border-green-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-green-500 text-lg">📦</span>
<h4 class="text-sm font-bold text-gray-900 m-0">Separación de datos</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">Datos personales en <code class="text-xs bg-gray-100 px-1 py-0.5 rounded">sessionStorage</code> (se borra al cerrar). Solo el UUID persiste en localStorage.</p>
<p class="text-xs text-green-700 font-medium mt-2 mb-0">Protege contra: persistencia innecesaria de datos</p>
</div>

<div class="bg-white border border-purple-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-purple-500 text-lg">📋</span>
<h4 class="text-sm font-bold text-gray-900 m-0">GDPR integrado</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">Checkbox obligatorio de privacidad antes de chatear. El visitante acepta explícitamente.</p>
<p class="text-xs text-purple-600 font-medium mt-2 mb-0">Protege contra: incumplimiento normativo</p>
</div>

<div class="bg-white border border-gray-100 rounded-xl p-5 shadow-sm">
<div class="flex items-center gap-2 mb-2">
<span class="text-gray-500 text-lg">🧠</span>
<h4 class="text-sm font-bold text-gray-900 m-0">Sin datos inventados</h4>
</div>
<p class="text-sm text-gray-600 m-0 leading-relaxed">Si BytIA no sabe algo, deriva a contacto humano. Nunca inventa servicios ni precios.</p>
<p class="text-xs text-gray-500 font-medium mt-2 mb-0">Protege contra: desinformación y alucinaciones</p>
</div>

</div>
</div>

El widget se instala con una línea de HTML en cualquier web. Funciona en WordPress, Astro, Shopify, HTML plano — no depende de la plataforma. El CSS está scoped (no rompe el diseño del host), React se empaqueta como IIFE autónomo, y el script pesa ~5 KiB (React se carga solo cuando el usuario hace clic, [gracias al patrón Facade](/blog/lighthouse-100-optimizar-rendimiento-web-2026/)).

## Si quieres algo parecido

La arquitectura es replicable. No necesitas mi código — necesitas los principios correctos:

1. **Separa comportamiento de datos** — Si tu system prompt tiene precios hardcodeados, cada cambio requiere editar el prompt. Sepáralos. Los datos deben vivir en un archivo, una base de datos o un JSON, no dentro de una cadena de texto de 2000 palabras.

2. **Dale contexto de página** — `window.location.pathname` se envía gratis con cada petición. Un chatbot que sabe que estás en `/precios/` convierte mejor que uno que no. Es un cambio de 10 líneas en el backend y cambia todo el comportamiento.

3. **Clasifica tus leads al cerrar** — Una llamada extra a la IA con el historial completo te da temperatura, intención y urgencia. El coste es mínimo (el historial ya lo tienes) y el valor es enorme: priorizas a quién llamar primero.

4. **Adapta el tono al estado emocional** — Un visitante que dice "llevo semanas esperando una respuesta" no necesita un listado de servicios. Necesita validación y una solución. Las instrucciones de adaptación emocional son 5 líneas en tu system prompt.

5. **Cierra con consejo, no con pregunta** — Un chatbot que termina con "¿algo más?" desperdicia la conversación. Y uno que pregunta "¿quieres agendar?" after de que el visitante dijo "lo pensaré" genera rechazo. El cierre ideal es una afirmación con valor: *"Te dejo este enlace donde Pedro explica SEO en detalle. Cuando estés listo, aquí estoy."* Aconseja, enlaza, no interrogues.

La tecnología no es el problema — cualquiera con acceso a una API de IA puede montar un chatbot. Lo que marca la diferencia es la **arquitectura del prompt**: cómo organizas la información, cómo inyectas contexto, cómo mides resultados.

Si tienes tiempo y conocimientos, puedes construir esto. Si prefieres tenerlo funcionando en tu web sin montar nada, [hablamos](/contacto/). Tu próximo lead caliente no va a esperar a que tú estés delante del ordenador.

---

<div class="not-prose my-12 bg-indigo-50 border border-indigo-100 rounded-2xl p-8 text-center">
<p class="text-xl font-semibold text-gray-900 mb-2">¿Quieres un chatbot que venda por ti?</p>
<p class="text-gray-600 mb-6">BytIA Chat se instala en cualquier web con una línea de código. Contexto de página, adaptación emocional, lead scoring — sin plugins, sin dependencias.</p>
<a href="/contacto/" class="inline-block bg-primary text-white px-8 py-3 rounded-lg font-semibold hover:bg-primary-hover transition-colors">Quiero información →</a>
</div>