Ir al contenido principal

Cómo Crear un Chatbot Web Personalizado con IA: Tutorial [2026]

22 min

Aprende a crear chatbots web personalizados. Tutorial con arquitectura, código, costos y alternativas sin código.

📧¿Te gusta este contenido?

Únete a 547+ profesionales que reciben tips de IA cada semana. Sin spam, cancela cuando quieras.

Cómo Crear un Chatbot Web Personalizado con IA: Tutorial [2026]

Los chatbots con IA se han vuelto esenciales para cualquier sitio web moderno. No solo mejoran la experiencia del usuario, sino que también pueden aumentar conversiones, reducir costos de soporte y automatizar procesos.

En este tutorial te muestro exactamente cómo crear un chatbot web personalizado desde cero. Cubriremos arquitectura, código, integración, despliegue y alternativas sin código si prefieres no programar.

Decisión Inicial: ¿Código o Sin Código?

Antes de empezar, necesitas decidir tu enfoque:

Opción 1: Con Código (Este Tutorial)

Ventajas:
  • Control total del chatbot
  • Personalización ilimitada
  • Costo más bajo a escala
  • Integración profunda con tu sistema

Desventajas:

  • Requiere desarrollo
  • Necesitas gestionar servidor
  • Requiere conocimiento técnico

Mejor para: Equipos técnicos, startups que necesitan control

Opción 2: Sin Código (Alternativas)

Ventajas:
  • Lanzar en minutos
  • No necesitas programador
  • Soporte incluido
  • Mantenimiento automático

Desventajas:

  • Menos personalización
  • Costo más alto a escala
  • Dependencia de proveedor

Mejor para: Pequeños negocios, presupuesto limitado de desarrollo


Arquitectura: Cómo Funciona un Chatbot Moderno

code
1┌─────────────────────────────────────────────────┐
2│ USUARIO EN NAVEGADOR │
3│ (Interfaz Chat en tu sitio web) │
4└────────────────┬────────────────────────────────┘
5
6
7┌─────────────────────────────────────────────────┐
8│ FRONTEND (Next.js + React) │
9│ - Interfaz de chat │
10│ - Gestión de mensajes │
11│ - Historial de conversación │
12└────────────────┬────────────────────────────────┘
13
14
15┌─────────────────────────────────────────────────┐
16│ API BACKEND (Next.js API Routes) │
17│ - Recibe mensajes del usuario │
18│ - Aplica RAG (busca contexto) │
19│ - Llama a OpenAI/Anthropic │
20│ - Retorna respuesta │
21└────────────────┬────────────────────────────────┘
22
23
24┌──────────────────────┬──────────────────────────┐
25│ OpenAI API │ Base de Datos │
26│ (Claude/GPT) │ (Vectores + Contexto) │
27└──────────────────────┴──────────────────────────┘

Conceptos Clave

RAG (Retrieval Augmented Generation):

Permite que tu chatbot tenga "memoria" de documentos específicos de tu empresa. Cuando el usuario pregunta, el chatbot busca la información relevante en tus documentos y usa eso para responder.

Embeddings:

Conversión de texto a vectores numéricos. Permite búsquedas semánticas (por significado, no por palabras clave).

Context Window:

El IA solo "ve" cierta cantidad de texto. Necesitas ser selectivo sobre qué contexto le das.


Tutorial Paso a Paso: Construir tu Chatbot

Requisito Previo

bash
1# Node.js 18+ instalado
2node --version
3 
4# Claves API:
5# 1. OpenAI API Key: https://platform.openai.com/api-keys
6# 2. (Opcional) Pinecone para embeddings: https://www.pinecone.io

Paso 1: Crear Proyecto Next.js

bash
1# Crear nuevo proyecto
2npx create-next-app@latest mi-chatbot --typescript
3 
4# Navegar al proyecto
5cd mi-chatbot
6 
7# Instalar dependencias necesarias
8npm install openai axios dotenv

Paso 2: Estructura de Carpetas

code
1mi-chatbot/
2├── app/
3│ ├── api/
4│ │ └── chat/
5│ │ └── route.ts # Endpoint del chatbot
6│ ├── page.tsx # Página principal
7│ └── layout.tsx
8├── components/
9│ ├── ChatInterface.tsx # Interfaz del chat
10│ ├── ChatMessage.tsx # Mensaje individual
11│ └── ChatInput.tsx # Input de usuario
12├── lib/
13│ ├── openai.ts # Configuración OpenAI
14│ ├── rag.ts # Lógica de RAG
15│ └── utils.ts # Funciones auxiliares
16├── data/
17│ └── knowledge-base.md # Tu información del chatbot
18├── .env.local # Variables de entorno
19└── package.json

Paso 3: Variables de Entorno

Crea archivo .env.local:

env
1OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
2OPENAI_MODEL=gpt-4o-mini
3NEXT_PUBLIC_API_URL=http://localhost:3000
4 
5# Si usas Pinecone para RAG
6PINECONE_API_KEY=xxxxx
7PINECONE_ENVIRONMENT=us-east-1
8PINECONE_INDEX_NAME=mi-chatbot

Paso 4: Configuración de OpenAI

Crear lib/openai.ts:

typescript
1import { OpenAI } from 'openai';
2 
3export const openai = new OpenAI({
4 apiKey: process.env.OPENAI_API_KEY,
5});
6 
7export const systemPrompt = `Eres un asistente de IA amable y útil para [Tu Empresa].
8Tu rol es ayudar a usuarios con preguntas sobre nuestros servicios.
9 
10Directrices:
11- Sé profesional pero accesible
12- Si no sabes algo, dilo honestamente
13- Ofrece ayuda práctica
14- Usa tonalidad de marca: [describe tu tono]
15- Responde siempre en español
16- Si necesitas información específica, pide al usuario
17 
18Información sobre la empresa:
19[Información de tu empresa aquí]`;
20 
21export interface ChatMessage {
22 role: 'user' | 'assistant';
23 content: string;
24}
25 
26export async function generateChatResponse(
27 messages: ChatMessage[],
28 context?: string
29): Promise<string> {
30 const systemMessage = context
31 ? `${systemPrompt}\n\nContexto relevante:\n${context}`
32 : systemPrompt;
33 
34 const response = await openai.chat.completions.create({
35 model: process.env.OPENAI_MODEL || 'gpt-4o-mini',
36 messages: [
37 { role: 'system', content: systemMessage },
38 ...messages,
39 ],
40 temperature: 0.7,
41 max_tokens: 1000,
42 });
43 
44 return response.choices[0].message.content || '';
45}

Paso 5: Lógica RAG (Contexto Personalizado)

Crear lib/rag.ts:

typescript
1// Implementación simple de RAG sin Pinecone
2// (Para producción, usa Pinecone o similar)
3 
4import fs from 'fs';
5import path from 'path';
6 
7interface Document {
8 id: string;
9 content: string;
10 metadata: {
11 title: string;
12 category: string;
13 };
14}
15 
16// Carga documentos de tu base de conocimiento
17function loadDocuments(): Document[] {
18 const knowledgeBasePath = path.join(
19 process.cwd(),
20 'data',
21 'knowledge-base.md'
22 );
23 
24 const content = fs.readFileSync(knowledgeBasePath, 'utf-8');
25 
26 // Divide el contenido por secciones
27 const sections = content.split('\n## ');
28 
29 return sections.map((section, index) => ({
30 id: `doc-${index}`,
31 content: section,
32 metadata: {
33 title: section.split('\n')[0],
34 category: 'general',
35 },
36 }));
37}
38 
39// Búsqueda simple por palabras clave (en producción usa embeddings)
40function searchDocuments(query: string, documents: Document[]): string {
41 const queryWords = query.toLowerCase().split(' ');
42 
43 const scored = documents.map(doc => ({
44 doc,
45 score: queryWords.filter(word =>
46 doc.content.toLowerCase().includes(word)
47 ).length,
48 }));
49 
50 const bestMatches = scored
51 .filter(item => item.score > 0)
52 .sort((a, b) => b.score - a.score)
53 .slice(0, 3);
54 
55 return bestMatches
56 .map(item => item.doc.content)
57 .join('\n---\n');
58}
59 
60export async function retrieveContext(query: string): Promise<string> {
61 const documents = loadDocuments();
62 return searchDocuments(query, documents);
63}

Paso 6: API Endpoint

Crear app/api/chat/route.ts:

typescript
1import { NextRequest, NextResponse } from 'next/server';
2import { generateChatResponse, ChatMessage } from '@/lib/openai';
3import { retrieveContext } from '@/lib/rag';
4 
5export async function POST(request: NextRequest) {
6 try {
7 const { messages } = await request.json();
8 
9 if (!messages || !Array.isArray(messages)) {
10 return NextResponse.json(
11 { error: 'Invalid messages format' },
12 { status: 400 }
13 );
14 }
15 
16 // Obtener contexto basado en último mensaje del usuario
17 const lastUserMessage = messages
18 .reverse()
19 .find((m: ChatMessage) => m.role === 'user');
20 
21 let context = '';
22 if (lastUserMessage) {
23 context = await retrieveContext(lastUserMessage.content);
24 }
25 
26 // Generar respuesta
27 const response = await generateChatResponse(messages, context);
28 
29 return NextResponse.json({
30 message: response,
31 context: context ? context.slice(0, 200) + '...' : null,
32 });
33 
34 } catch (error) {
35 console.error('Chat API error:', error);
36 return NextResponse.json(
37 { error: 'Failed to generate response' },
38 { status: 500 }
39 );
40 }
41}
42 
43// Configurar CORS si es necesario
44export function OPTIONS() {
45 return new NextResponse(null, {
46 status: 200,
47 headers: {
48 'Access-Control-Allow-Origin': '*',
49 'Access-Control-Allow-Methods': 'POST, OPTIONS',
50 'Access-Control-Allow-Headers': 'Content-Type',
51 },
52 });
53}

Paso 7: Componente Chat Interface

Crear components/ChatInterface.tsx:

typescript
1'use client';
2 
3import React, { useState, useRef, useEffect } from 'react';
4import ChatMessage from './ChatMessage';
5import ChatInput from './ChatInput';
6 
7interface Message {
8 id: string;
9 role: 'user' | 'assistant';
10 content: string;
11 timestamp: Date;
12}
13 
14export default function ChatInterface() {
15 const [messages, setMessages] = useState<Message[]>([
16 {
17 id: '1',
18 role: 'assistant',
19 content: '¡Hola! Soy tu asistente. ¿En qué puedo ayudarte?',
20 timestamp: new Date(),
21 },
22 ]);
23 
24 const [loading, setLoading] = useState(false);
25 const messagesEndRef = useRef<HTMLDivElement>(null);
26 
27 const scrollToBottom = () => {
28 messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
29 };
30 
31 useEffect(() => {
32 scrollToBottom();
33 }, [messages]);
34 
35 const handleSendMessage = async (content: string) => {
36 // Agregar mensaje del usuario
37 const userMessage: Message = {
38 id: Date.now().toString(),
39 role: 'user',
40 content,
41 timestamp: new Date(),
42 };
43 
44 setMessages(prev => [...prev, userMessage]);
45 setLoading(true);
46 
47 try {
48 const response = await fetch('/api/chat', {
49 method: 'POST',
50 headers: { 'Content-Type': 'application/json' },
51 body: JSON.stringify({
52 messages: messages.map(m => ({
53 role: m.role,
54 content: m.content,
55 })),
56 }),
57 });
58 
59 const data = await response.json();
60 
61 if (data.error) {
62 throw new Error(data.error);
63 }
64 
65 // Agregar respuesta del asistente
66 const assistantMessage: Message = {
67 id: (Date.now() + 1).toString(),
68 role: 'assistant',
69 content: data.message,
70 timestamp: new Date(),
71 };
72 
73 setMessages(prev => [...prev, assistantMessage]);
74 
75 } catch (error) {
76 console.error('Error:', error);
77 const errorMessage: Message = {
78 id: (Date.now() + 1).toString(),
79 role: 'assistant',
80 content: 'Disculpa, hubo un error. Por favor intenta de nuevo.',
81 timestamp: new Date(),
82 };
83 setMessages(prev => [...prev, errorMessage]);
84 } finally {
85 setLoading(false);
86 }
87 };
88 
89 return (
90 <div className="flex flex-col h-screen bg-white">
91 {/* Header */}
92 <div className="bg-blue-600 text-white p-4 shadow">
93 <h1 className="text-xl font-bold">Asistente de IA</h1>
94 <p className="text-sm text-blue-100">Estoy aquí para ayudarte</p>
95 </div>
96 
97 {/* Messages */}
98 <div className="flex-1 overflow-y-auto p-4 space-y-4">
99 {messages.map(msg => (
100 <ChatMessage key={msg.id} message={msg} />
101 ))}
102 {loading && (
103 <div className="flex justify-center">
104 <div className="animate-pulse text-gray-500">
105 Escribiendo...
106 </div>
107 </div>
108 )}
109 <div ref={messagesEndRef} />
110 </div>
111 
112 {/* Input */}
113 <ChatInput
114 onSendMessage={handleSendMessage}
115 disabled={loading}
116 />
117 </div>
118 );
119}

Paso 8: Componentes Auxiliares

Crear components/ChatMessage.tsx:

typescript
1'use client';
2 
3export interface Message {
4 role: 'user' | 'assistant';
5 content: string;
6 timestamp: Date;
7}
8 
9interface ChatMessageProps {
10 message: Message;
11}
12 
13export default function ChatMessage({ message }: ChatMessageProps) {
14 const isUser = message.role === 'user';
15 
16 return (
17 <div className={`flex ${isUser ? 'justify-end' : 'justify-start'}`}>
18 <div
19 className={`max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
20 isUser
21 ? 'bg-blue-500 text-white'
22 : 'bg-gray-200 text-gray-800'
23 }`}
24 >
25 <p className="text-sm">{message.content}</p>
26 <span className="text-xs opacity-70">
27 {message.timestamp.toLocaleTimeString()}
28 </span>
29 </div>
30 </div>
31 );
32}

Crear components/ChatInput.tsx:

typescript
1'use client';
2 
3import { useState } from 'react';
4 
5interface ChatInputProps {
6 onSendMessage: (content: string) => void;
7 disabled?: boolean;
8}
9 
10export default function ChatInput({ onSendMessage, disabled }: ChatInputProps) {
11 const [input, setInput] = useState('');
12 
13 const handleSubmit = (e: React.FormEvent) => {
14 e.preventDefault();
15 if (input.trim() && !disabled) {
16 onSendMessage(input);
17 setInput('');
18 }
19 };
20 
21 return (
22 <form onSubmit={handleSubmit} className="border-t p-4 bg-white">
23 <div className="flex gap-2">
24 <input
25 type="text"
26 value={input}
27 onChange={(e) => setInput(e.target.value)}
28 placeholder="Escribe tu pregunta..."
29 disabled={disabled}
30 className="flex-1 border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
31 />
32 <button
33 type="submit"
34 disabled={disabled || !input.trim()}
35 className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 disabled:opacity-50"
36 >
37 Enviar
38 </button>
39 </div>
40 </form>
41 );
42}

Paso 9: Página Principal

Actualizar app/page.tsx:

typescript
1import ChatInterface from '@/components/ChatInterface';
2 
3export const metadata = {
4 title: 'Mi Chatbot IA',
5 description: 'Asistente de IA para tu sitio web',
6};
7 
8export default function Home() {
9 return (
10 <main className="w-full h-screen">
11 <ChatInterface />
12 </main>
13 );
14}

Paso 10: Ejecutar Localmente

bash
1npm run dev

Abre http://localhost:3000 en tu navegador.


Integración en Sitio Existente

Si quieres agregar el chatbot como widget flotante en un sitio existente:

Crear Widget Embebible

Crear app/widget/route.tsx:

typescript
1export default function WidgetScript() {
2 const script = `
3(function() {
4 const iframe = document.createElement('iframe');
5 iframe.src = 'https://tu-dominio.com/chat';
6 iframe.style.cssText = \`
7 position: fixed;
8 bottom: 20px;
9 right: 20px;
10 width: 400px;
11 height: 500px;
12 border: none;
13 border-radius: 10px;
14 box-shadow: 0 5px 40px rgba(0,0,0,0.16);
15 z-index: 999999;
16 \`;
17 document.body.appendChild(iframe);
18})();
19 `;
20 
21 return new Response(script, {
22 headers: { 'Content-Type': 'application/javascript' },
23 });
24}

Usar en cualquier sitio:

html
1<script src="https://tu-chatbot.com/widget"></script>


Análisis de Costos

OpenAI (Por Millón de Tokens)

code
1gpt-4o-mini: $0.15 entrada, $0.60 salida
2gpt-4-turbo: $10 entrada, $30 salida
3claude-3.5: $3 entrada, $15 salida
4 
5Ejemplo: 1000 conversaciones/mes
6- Promedio: 500 tokens entrada, 300 salida
7- Costo mensual: ~$25-50 USD

Servidor

code
1Vercel (Free tier): $0
2Vercel (Pro): $20/mes
3AWS/DigitalOcean: $10-50/mes

Base de Datos (RAG)

code
1Pinecone: $0-100/mes
2Weaviate (self): $0 (costo servidor)
3ChromaDB: $0 (local)


Alternativas Sin Código

Si no quieres programar:

1. Chatbase ($99/mes)

code
1- Sube documentos
2- Genera chatbot automáticamente
3- Copia código embed
4- Perfecto para comenzar

2. Botpress (Free - $50/mes)

code
1- Editor visual
2- Integración con CRM
3- Análisis incluido
4- Más flexible que Chatbase

3. Voiceflow ($50/mes)

code
1- Herramienta visual
2- No-code/Low-code
3- Integración con APIs
4- Mejor para conversaciones complejas

4. Intercom ($99/mes)

code
1- Chatbot + Ticketing
2- CRM integrado
3- Muy profesional
4- Para empresas establecidas


Mejores Prácticas en Producción

1. Limitar Rate Limiting

typescript
1// lib/rateLimit.ts
2import { Ratelimit } from '@upstash/ratelimit';
3import { Redis } from '@upstash/redis';
4 
5const ratelimit = new Ratelimit({
6 redis: Redis.fromEnv(),
7 limiter: Ratelimit.slidingWindow(10, '1 h'),
8});
9 
10export async function checkRateLimit(ip: string) {
11 const { success } = await ratelimit.limit(ip);
12 return success;
13}

2. Logging y Monitoreo

typescript
1// Log todas las conversaciones para mejora continua
2async function logConversation(
3 userMessage: string,
4 assistantResponse: string,
5 userId: string
6) {
7 // Guardar en base de datos
8 await db.conversations.create({
9 userId,
10 userMessage,
11 assistantResponse,
12 timestamp: new Date(),
13 });
14}

3. Seguridad

typescript
1// Validar y sanitizar entrada
2import DOMPurify from 'isomorphic-dompurify';
3 
4function sanitizeInput(text: string): string {
5 return DOMPurify.sanitize(text);
6}
7 
8// Proteger contra prompt injection
9function detectPromptInjection(text: string): boolean {
10 const suspiciousPatterns = [
11 /ignore your instructions/i,
12 /system prompt/i,
13 /as an ai/i,
14 ];
15 return suspiciousPatterns.some(p => p.test(text));
16}

4. Caché de Respuestas

typescript
1// Para preguntas comunes, cachear respuestas
2const cache = new Map<string, string>();
3 
4export async function getCachedOrGenerate(
5 query: string,
6 generateFn: () => Promise<string>
7): Promise<string> {
8 if (cache.has(query)) {
9 return cache.get(query)!;
10 }
11 
12 const response = await generateFn();
13 cache.set(query, response);
14 return response;
15}


Despliegue a Producción

Opción 1: Vercel (Recomendado)

bash
1# Instalar Vercel CLI
2npm i -g vercel
3 
4# Deploy
5vercel
6 
7# Variables de entorno
8vercel env add OPENAI_API_KEY

Opción 2: DigitalOcean

bash
1# Crear droplet Ubuntu
2# Instalar Node.js
3curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
4sudo apt-get install -y nodejs
5 
6# Clonar repo y ejecutar
7git clone tu-repo
8cd mi-chatbot
9npm install
10npm run build
11npm start


FAQ: Preguntas Frecuentes

¿Cuánto cuesta un chatbot en producción?

Respuesta: Un chatbot pequeño (< 1000 conversaciones/mes):

  • OpenAI: $20-50/mes
  • Servidor: Vercel Free
  • Total: $20-50/mes

Chatbot mediano (10k conversaciones/mes):

  • OpenAI: $200-500/mes
  • Servidor: $20/mes
  • Total: $220-520/mes

¿Puedo usar modelos gratuitos como Llama?

Respuesta: Sí. Alternativas gratuitas:

  • Ollama (ejecutar Llama localmente)
  • HuggingFace API (limitado)
  • Perplexity API (barato)

Pero GPT-4o-mini tiene mejor calidad.

¿Cómo hago que el chatbot no alucine?

Respuesta:

  • Usa RAG (proporciona contexto concreto)
  • Limita knowledge base a información verificada
  • Establece límites en system prompt ("Si no sabes, dilo")
  • Monitorea respuestas regularmente

¿Funciona en dispositivos móviles?

Respuesta: Sí. Usa media queries en CSS:

css
1@media (max-width: 640px) {
2 .chat-container {
3 max-width: 100%;
4 height: 100vh;
5 }
6}

¿Puedo entrenar el chatbot con mis datos?

Respuesta: Hay dos formas:

  1. RAG (recomendado): Proporcionar contexto al chatbot
  2. Fine-tuning (caro): Entrenar modelo específicamente

- OpenAI fine-tuning: Caro y lento

- Mejor: Usa RAG


Conclusión

Ahora sabes exactamente cómo crear un chatbot web personalizado. Ya sea que lo hagas con código o sin código, tienes opciones.

Próximos pasos:

  1. Decide: ¿Código o sin código?
  2. Si código: Sigue el tutorial paso a paso
  3. Recolecta feedback de usuarios
  4. Mejora continuamente

¿Necesitas ayuda implementando esto? ¿Tienes dudas sobre algún paso? ¡Comenta abajo!

¿Crees que estás sacando partido a la IA en tu empresa?

Si la respuesta no es un sí rotundo, te estás dejando horas, dinero y ventaja competitiva cada día que pasa. Mientras tú lo valoras, tu competencia ya lo está usando para moverse el doble de rápido.

Te implemento un sistema de IA que ahorra tiempo, reduce costes y aumenta ingresos: 100% adaptado a tu stack, tus datos y tus procesos. En 4-8 semanas tienes una primera versión funcionando, no dentro de seis meses.

📬

¿Te ha gustado? Hay más cada semana

Únete a "IA Sin Humo" — la newsletter donde comparto lo que realmente funciona en inteligencia artificial. Sin teoría innecesaria, sin postureo.

📚

1 Tutorial

Paso a paso, práctico

🛠️

3 Herramientas

Probadas y útiles

💡

0 Bullshit

Solo lo que importa

+547 suscriptores • Cada martes • Cancela cuando quieras

Javier Santos - Especialista en IA & Machine Learning

Javier Santos

Consultor de IA para empresas. Comparto contenido sobre inteligencia artificial, automatización y desarrollo cada semana.