La integración de Modelos de Lenguaje Grande (LLMs) directamente en un frontend web moderno es sorprendentemente accesible. En este tutorial, construiremos un componente de Chatbot flotante inyectable en cualquier sitio web, conectándolo de forma segura a la API de OpenAI (GPT-4o-mini).
Requisitos Previos
Asegúrate de tener lo siguiente antes de empezar:
- Una cuenta en OpenAI Developer Platform.
- Una clave API generada y con saldo o créditos gratuitos.
- Un servidor básico (Node.js, Cloudflare Worker o Vercel Edge function) para actuar como proxy seguro.
- Conocimientos básicos de JavaScript (Fetch API, DOM manipulation).
La Regla de Oro: Protege tu API Key
Advertencia Crítica
NUNCA expongas tu OPENAI_API_KEY en el código JavaScript del cliente
(frontend).
Cualquiera podría inspeccionar el código, robarla y realizar peticiones a tu costa. Siempre envía la
petición a tu propio backend y que este se comunique con OpenAI.
1. El Backend: Proxy Seguro
Configuraremos un Endpoint sencillo. Usando Hono/Cloudflare Workers, el código para recibir el mensaje del frontend y reenviarlo a OpenAI sería así:
import { Hono } from 'hono'
import { cors } from 'hono/cors'
const app = new Hono()
app.use('/*', cors())
app.post('/api/chat', async (c) => {
const { messages } = await c.req.json();
const apiKey = c.env.OPENAI_API_KEY; // Obtenido del entorno seguro
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': \`Bearer \${apiKey}\`
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: messages,
stream: false // Para simplicidad. Soporte de stream requiere EventSource.
})
});
const data = await response.json();
return c.json({ reply: data.choices[0].message });
})
export default app;
2. Frontend: La Interfaz Flotante
Añade el siguiente contenedor HTML al final del <body> de tu página. Usa clases
Tailwind para un diseño limpio y moderno:
<!-- Chat Widget (Oculto por defecto, mostrable mediante JS) -->
<div id="chat-widget" class="fixed bottom-6 right-6 w-80 h-96 bg-gray-900 rounded-2xl shadow-2xl flex flex-col border border-gray-700 hidden">
<!-- Header -->
<div class="p-4 bg-emerald-600 rounded-t-2xl font-bold flex justify-between">
<span>Soporte iaDATA</span>
<button onclick="document.getElementById('chat-widget').classList.add('hidden')">✕</button>
</div>
<!-- Área de mensajes -->
<div id="chat-messages" class="flex-1 p-4 overflow-y-auto space-y-3"></div>
<!-- Input -->
<div class="p-3 border-t border-gray-700 flex gap-2">
<input id="chat-input" class="flex-1 bg-gray-800 rounded px-3 py-2 text-sm text-white" placeholder="Pregunta algo...">
<button id="chat-send" class="bg-emerald-600 px-3 py-2 rounded"><i class="fas fa-paper-plane"></i></button>
</div>
</div>
3. Lógica JavaScript y Memoria
Para que el LLM "recuerde" de qué estáis hablando, debe enviarse todo el historial de la conversación en cada petición (Role: system, user, assistant).
let conversation = [
{ role: "system", content: "Eres un asistente de iaDATA útil y conciso." }
];
const input = document.getElementById('chat-input');
const messagesDiv = document.getElementById('chat-messages');
async function askBot() {
const userText = input.value;
if (!userText) return;
// Mostrar mensaje de usuario
messagesDiv.innerHTML += \`\${userText}\`;
input.value = '';
// Actualizar contexto
conversation.push({ role: "user", content: userText });
// Petición al proxy (NO directamente a OpenAI)
const res = await fetch('https://tu-worker.workers.dev/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages: conversation })
});
const data = await res.json();
const botReply = data.reply.content;
// Añadir al contexto y renderizar
conversation.push({ role: "assistant", content: botReply });
messagesDiv.innerHTML += \`\${botReply}\`;
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
document.getElementById('chat-send').addEventListener('click', askBot);
Siguientes Pasos (Streaming)
Para una experiencia de usuario perfecta (como ChatGPT), en lugar de esperar la respuesta completa, puedes implementar Streaming. Esto requiere:
- Activar
stream: trueen la petición de tu Backend hacia OpenAI. - Devolver la respuesta del Worker usando
TransformStream. - En el Frontend, procesar la respuesta usando la Web Streams API o la librería
eventsource-parser.