Cómo construir un chatbot artesano para WordPress y superar a cualquier IA

2 comentarios

10.04.2026|

2 comentarios

Tiempo de lectura Lectura: 8 min, 54 s
Número de palabras Palabras: 1648
Número de visitas Visitas: 45
Icono de traducción
⚡ Resumen rápido
Asistente de JRMora
¡Hola! Soy el asistente virtual del blog. Tus preguntas mejorarán las respuestas ¿En qué puedo ayudarte hoy?

No me he venido muy arriba con el título, es posible crear un chat de asistencia para un uso muy específico que responda mejor que una IA. Dependerá de las ganas que le pongas a la construcción de una base de conocimiento que cubra todas las posibles respuestas que necesites ofrecer.

Este chat NO ES IA. Se trata de un Chatbot propio de lógica difusa (Fuzzy Search) en JavaScript (muy básico de momento). No se almacena nada fuera de este servidor, de hecho no se almacena nada tampoco en el servidor si no quieres. Todo sucede en tu navegador

Obviamente, no tendrás un chat conversacional ni una base de conocimiento infinita sobre cualquier tema porque de lo que se trata es de que sea útil para un visitante que busca cualquier cosa relacionada con tu página, tu actividad, tus servicios y/o tu persona. Las respuestas estarán limitadas a lo que te interesa que el chat conteste, que no es poco.

Dejo el código con algunos comentarios básicos sobre el funcionamiento por si quieres probarlo.

HTML. Pinta la caja del chat allí donde añadas este bloque de html.

<div id="jrmora-bot-container">
    <div id="jrmora-bot-header">
        <div id="jrmora-bot-status"></div>
        <strong>Título del asistente</strong>
    </div>

    <div id="jrmora-chat-window">
        <div class="msg-bot">
            ¡Hola! Soy el asistente virtual del blog. Tus preguntas mejorarán las respuestas ¿En qué puedo ayudarte hoy?
        </div>
    </div>

    <div id="jrmora-bot-input-area">
        <input type="text" id="jrmora-user-input" placeholder="Escribe tu duda aquí...">
        <button onclick="jrmoraSendMessage()">Enviar</button>
    </div>
</div>

Javascipt. Puedes añadirlo como un code snippet o en el functions.php de tu tema hijo. Aquí se define el comportamiento. En el código hay dos preguntas de ejemplo. El valor "tags" reune las palabras o frases que devolverán la respuesta contenida en el valor "res".

A partir de esta estructura puedes añadir tantas respuestas como necesites:

{
    "tags": ["palabra1","palara2 palabra3"],
    "res": "La respuesta aquí"
  },

JAVASCRIPT



const jrmoraConocimiento = [
         {
    "tags": ["licencia","licencia creative commons","que licencia","tipo de licencia","cc by-nc-nd","puedo usar","permiso uso","derechos de autor","propiedad intelectual","usar tus dibujos","compartir viñetas"],
    "res": "Para todas las imágenes firmadas por JRMora se aplica la licencia Atribución-NoComercial-SinDerivadas 4.0 Internacional (CC BY-NC-ND 4.0). Info: https://jrmora.com/licencia/"
  },
  {
    "tags": ["sobre el blog","autor","informacion del sitio","datos del autor","historia del blog","acerca de"],
    "res": "Aquí puedes consultar más datos sobre el autor y el blog: https://jrmora.com/about/"
  },
] 
// --- UTILIDADES ---

// Quita tildes y pasa a minúsculas
function normalizar(texto) {
    return texto.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}

// Busca URLs en el texto y las convierte en enlaces HTML
function enlazarTexto(texto) {
    const urlRegex = /(https?:\/\/[^\s]+)/g;
    return texto.replace(urlRegex, function(url) {
        const limpiaUrl = url.replace(/[.,]$/, "");
        return `<a href="${limpiaUrl}" target="_blank" style="color: #d32f2f; font-weight: bold; text-decoration: underline;">${limpiaUrl}</a>`;
    });
}

// --- LÓGICA PRINCIPAL ---

function jrmoraSendMessage() {
    const input = document.getElementById('jrmora-user-input');
    const windowChat = document.getElementById('jrmora-chat-window');
    const userText = input.value.trim();

    if (userText === "") return;

    // Muestra lo que el usuario ha escrito
    windowChat.innerHTML += `<div class="msg-user">${userText}</div>`;
    
    // Limpieza y Focus inmediato para usabilidad móvil
    input.value = "";
    input.focus(); 
    
    windowChat.scrollTop = windowChat.scrollHeight;

    const query = normalizar(userText);
    const mensajeError = "Lo siento. No soy una IA y no tengo una respuesta guardada para eso. Puedes consultar por palabras sueltas, buscar temas específicos con la lupa del blog o contactar directamente aquí: https://tuweb/contacto/";
    let respuestaFinal = mensajeError;

    // Recorre la base de conocimiento (jrmoraConocimiento debe estar definido antes)
    for (const item of jrmoraConocimiento) {
        const coincidencia = item.tags.some(tag => query.includes(normalizar(tag)));
        if (coincidencia) {
            respuestaFinal = item.res;
            break;
        }
    }

    // --- GUARDADO DE LOGS (PHP) ---
    fetch('/chat-logger.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            pregunta: userText,
            respuesta: (respuestaFinal === mensajeError) ? "DESCONOCIDA" : "OK"
        })
    }).catch(err => console.log('Error en registro:', err));

    // Muestra la respuesta con un pequeño retraso
    const respuestaConLinks = enlazarTexto(respuestaFinal);

    setTimeout(() => {
        windowChat.innerHTML += `<div class="msg-bot">${respuestaConLinks}</div>`;
        windowChat.scrollTop = windowChat.scrollHeight;
    }, 500);
}

// --- EVENTOS ---

document.addEventListener('DOMContentLoaded', () => {
    const inputElement = document.getElementById('jrmora-user-input');
    if(inputElement) {
        inputElement.addEventListener('keydown', function (e) {
            if (e.key === 'Enter') {
                e.preventDefault(); 
                jrmoraSendMessage();
            }
        });
    }
});

En // --- GUARDADO DE LOGS (PHP) --- se hace alusión a un arhivo llamado chat-logger.php (de uso opcional) si quieres que el chat vaya guardando en texto plano una relación de las consultas- Este es el código de chat-logger.php que debes añadir a la raíz donde tengas instalado tu blog.

chat-logger.php

<?php
// Solo aceptamos peticiones POST para mayor seguridad
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $json = file_get_contents('php://input');
    $data = json_decode($json, true);

    if ($data && isset($data['pregunta'])) {
        $fecha = date('Y-m-d H:i:s');
        $pregunta = strip_tags($data['pregunta']);
        $respuesta = strip_tags($data['respuesta']);
        
        // El nombre del archivo tiene un sufijo aleatorio por seguridad
        $archivoLog = 'registro_consultas_chat_45637.txt';
        
        $linea = "[$fecha] USER: $pregunta | BOT: $respuesta" . PHP_EOL;
        
        file_put_contents($archivoLog, $linea, FILE_APPEND);
    }
}

Puedes cambiar el nombre del archivo que se generará de: "registro_consultas_chat_45637.txt" y llamarlo como quieras.

Por último, para darle una capa más de seguridad a este archivo, añade este código al final de tu .htaccess:

 <Files "registro_consultas_chat_45637.txt">
      Order Deny,Allow
      Deny from all
  </Files>
</IfModule>

Esto creará un log en .txt en la raiz de tu sitio con lo que evitamos crear una tabla en la bases de datos. En el log se guardan las preguntas hechas y solo se anota la fecha y la hora de las preguntas, donde "OK" significa que el bot contestó algo encontrado en las palabras de la base de conocimiento y "DESCONOCIDA" que no encontró respuesta válida.

Esto no deja de ser una forma local de "entrenar" a tu bot revisando lo que la gente pregunta si lo consideras relevante. Te permite salir de tu propia burbuja de lenguaje y descubrir cómo preguntan realmente los usuarios. Una vez que has detectado esos patrones y has enriquecido tus cadenas de búsqueda, el log habrá cumplido su misión y podrás prescindir de él para que el sistema puede volar solo, ligero y limpio

CSS- Pues eso, por último el estilo para la caja de chat. Es el que uso aquí. Puedes trastearlo a tu gusto.

#jrmora-bot-container {
    max-width: 500px;
    width: 95%; 
    margin: 20px auto;
    border: 1px solid #e0e0e0;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 10px 25px rgba(0,0,0,0.1);
    background: #fff;
    display: flex;
    flex-direction: column;
    /* Hereda la fuente de tu blog */
}

#jrmora-bot-header {
    background: #222;
    color: #fff;
    padding: 15px;
    display: flex;
    align-items: center;
    gap: 10px;
}

#jrmora-bot-status {
    width: 10px;
    height: 10px;
    background: #00ff00;
    border-radius: 50%;
}

#jrmora-chat-window {
    height: 400px;
    max-height: 65vh; 
    overflow-y: auto;
    padding: 15px;
    background: #fdfdfd;
    display: flex;
    flex-direction: column;
    gap: 12px;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
}

.msg-bot {
    background: #f1f1f1;
    padding: 12px 16px;
    border-radius: 15px 15px 15px 0;
    max-width: 85%;
    align-self: flex-start;
    line-height: 1.4;
    color: #333;
    /* En PC se verá al tamaño de fuente de tus posts */
    font-size: 0.95em;
}

.msg-user {
    background: #222;
    color: #fff;
    padding: 12px 16px;
    border-radius: 15px 15px 0 15px;
    max-width: 85%;
    align-self: flex-end;
    line-height: 1.4;
    font-size: 0.95em;
}

#jrmora-bot-input-area {
    padding: 12px;
    border-top: 1px solid #eee;
    display: flex;
    gap: 8px;
    background: #fff;
    align-items: center;
}

#jrmora-user-input {
    flex: 1;
    padding: 12px;
    border: 1px solid #ddd;
    border-radius: 8px;
    outline: none;
    min-width: 0;
    /* Hereda fuente del sistema */
}

#jrmora-bot-input-area button {
    background: #222;
    color: #fff;
    border: none;
    padding: 12px 18px;
    border-radius: 8px;
    cursor: pointer;
    font-weight: bold;
    flex-shrink: 0;
}

#jrmora-bot-input-area button:active {
    background: #d32f2f;
}

/* --- AJUSTES ESPECÍFICOS PARA MÓVILES --- */
@media screen and (max-width: 768px) {
    /* Mensajes un poco más compactos para que no ocupen media pantalla */
    .msg-bot, .msg-user {
        font-size: 15px;
    }
    
    /* PARCHE CRÍTICO: 16px solo en el input evita el zoom de Firefox/Chrome/Safari */
    #jrmora-user-input {
        font-size: 16px !important;
    }
}

Las ventajas de crear un chat de asistencia así son muchas.

De entrada tienes el control absoluto de las respuestas porque las redactas tú.

Sin distracciones. No hay algoritmos caprichosos que alucinan interpretando las cosas a su rollo e inventando respuestas o creando respuestas sobre temas no relacionados con lo que te interesa contestar.

Siempre mejorable. Cuanto más trabajes en las posibles preguntas y sus respuestas, mejor comportamiento obtendrás.

Es seguro. Privacidad total. los datos no salen de tu servidor. Incluso, si lo deseas, puedes tenerlo funcionando sin que guarde absolutamente nada.

Rendimiento. Impacto prácticamente nulo en la velocidad de carga ofreciendo respuestas inmediatas en apenas unos milisegundos, no hay APIs externas ni peticiones de terceros. Nada.

Coste cero por uso de tokens de una IA. Solo invertirás tu tiempo ampliando y mejorando las posibles respuestas a las preguntas.

Este chat, de momento es básico, pero funcional. Paralelamente estoy probando una versión más compleja para el botón verde que verás en los post, con el texto "Resumen rápido" (también sin IA), que lee respuestas de tres fuentes: campos Personalizados, contenido del post y de la base de conocimiento del chatbot. Este botón sí está en una fase muy primitiva y aunque sigo haciendo pruebas para crear una pequeña infraestructura interna mejorada no estoy seguro de si lo seguiré desarrollando porque para obtener respuestas muy afinadas hay que replantearlo y trabajarlo mucho más.

Lo que no descarto es convertir en el futuro este chatbot de asistencia en un plugin para que la la introducción de tags y respuestas sea más simple, rápida e intuitiva.

Donar

Documentales sobre dibujantesTontolares, los titulares más tontos

2 comentarios en «Cómo construir un chatbot artesano para WordPress y superar a cualquier IA»

  1. Agh, la publicidad no me deja comentar!

    Oye, esta genial. esta mejor que la tonteria que estaba haciendo con ollama y qwen2.5:0.5b. voy a estudiarlo para ver si lo puedo aplicar

    • Eo, Drk0027, gracias por comentar la cosa :P
      Si en la pestaña de "Gestionar consentimiento" del banner de cookies le das a "rechazar" se supone que no debes ver ningún anuncio, aún así, si me dices qué anuncio era el que te impedía comentar, lo revisaré.

      Si te animaras a probar el chatbot y se te ocurre alguna posible mejora, o lo que sea, te agradecería cualquier comentario al respecto. Supongo que el siguiente paso será intentar empaquetarlo para convertirlo en plugin, de momento solo para uso personal.

      En tu blog he leído que también te liaste en su día con Ollama, hace poco también pasé por ahí y al final opté por pillar unos pocos créditos en Open Router para probar varios modelos con Void y pese a que tengo 64 GB de RAM, pero unos muy cortos 6 GB de gráfica, solo conseguí quemar tokens para nada. Pero no descarto volver a intentarlo con Ollama y Qwen2.5-Coder-7B (y opcionalmente Gemma 31B, o similar, para depurar pequeñas cosas).

Los comentarios están cerrados.

Este blog se aloja en LucusHost

LucusHost, el mejor hosting

Tu WordPress puede volar

Servicio de optimización de WordPress
Quiero velocidad

Servicio de optimización

Suscripción por e-mail

Recibe gratis los artículos completos en tu correo sin publicidad en el momento que se publiquen. Se envía el contenido íntegro del feed sin herramientas externas y sin anuncios.