Ir al contenido principal
Desarrollo & IA

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

8 de febrero de 2026
22 min

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

Javier Santos

Especialista en IA & Machine Learning

📧¿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!

📬

¿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