Bot WhatsApp con IA para restaurantes: arquitectura completa con Claude + Evolution API
Varios restaurantes clientes nuestros reciben pedidos por WhatsApp. Antes de TRUJO, el flujo era: cliente escribe, empleado lee, empleado busca en carta, empleado responde, cliente confirma, empleado ingresa pedido en POS. Con pico de 40 mensajes simultáneos un viernes a las 8pm, eso se rompe. Este artículo documenta la arquitectura que construimos.
Por qué Evolution API y no la API oficial de Meta
La API oficial de WhatsApp Business Cloud de Meta puede alcanzar 3-5 segundos de latencia en hora pico. Para un restaurante que necesita respuesta en menos de 2 segundos, eso es demasiado. Evolution API implementa el protocolo Baileys (WhatsApp Web reverse-engineered) en un servidor Node.js. No es la ruta oficialmente soportada por Meta, pero para volúmenes bajos y medianos (menos de 1.000 mensajes/día por número) funciona de forma confiable en producción.
Corremos Evolution API en Docker con PostgreSQL como almacenamiento de sesiones y Redis para cache. El webhook apunta a nuestra API FastAPI para procesar cada mensaje entrante.
Estado de conversación en Redis
Cada conversación tiene estado persistido en Redis con TTL de 30 minutos. Si el cliente no responde en 30 minutos, la conversación se reinicia. El estado incluye: la etapa actual (greeting, browsing_menu, building_order, confirming, awaiting_payment, done), los ítems del pedido en construcción, el historial de mensajes reciente (máximo 10 turnos), y el tenant_id del restaurante.
La clave de Redis es "conv:{tenant_id}:{phone}", garantizando que dos restaurantes que compartan el mismo número de proveedor no interfieran entre sí.
Catálogo sincronizado desde el POS Loggro
El menú de cada restaurante vive en Redis, sincronizado cada 4 horas desde el POS Loggro. Los catálogos típicos rondan los 200 productos por sede. El sync elimina duplicados normalizando nombres (strip acentos, uppercase, trim espacios) antes de comparar. Los typos confirmados en Loggro (por ejemplo "hamburguesa" vs "hamburgueza") se filtran automáticamente, aunque la corrección definitiva en el POS la hace el administrador del cliente manualmente.
Claude Haiku como cerebro del bot
Elegimos Claude Haiku (claude-haiku-4-5) sobre Claude Sonnet por latencia: Haiku responde en 300-600ms, Sonnet en 1.2-2.5s. Para un bot de restaurante la diferencia es perceptible para el cliente.
El system prompt incluye: nombre del restaurante, los primeros 50 productos del catálogo en JSON (los más consultados por frecuencia histórica), el pedido en construcción actual, y reglas de negocio: solo ofrecer productos del menú, confirmar siempre el pedido completo antes de cerrar, precios en COP con IVA incluido, respuestas de máximo 3 oraciones, pago contra entrega o en caja.
El historial de conversación se trunca a los últimos 10 turnos para evitar que el context window explote en conversaciones largas. El costo promedio de Claude Haiku por conversación (8 turnos) es aproximadamente $0.0004 USD.
Webhook handler
El endpoint POST /api/v1/whatsapp/webhook recibe todos los eventos de Evolution API. Solo procesa eventos de tipo "messages.upsert" que no sean mensajes propios del bot (fromMe = false) y que contengan texto (no audio, imágenes u otros media). El procesamiento real ocurre en background_tasks para responder al webhook en menos de 200ms y evitar reintentos por timeout.
El aprendizaje más caro: modo supervisado
Empezamos con el bot en modo 24/7 autónomo. Los empleados rechazaban los pedidos que llegaban del bot porque no confiaban en si el cliente había confirmado correctamente. El administrador del cliente nos reportó el problema a la semana de despliegue.
Pivotamos a modo supervisado: el bot funciona solo en horario definido por restaurante, configurable en un JSON de schedule por tenant. Lunes a jueves 12-22h, viernes y sábados 11-23h, domingos cerrado por defecto. Fuera del horario, el bot responde con horario de atención y guarda el contacto para follow-up manual.
El reporte diario llega al dueño por Telegram a las 10pm: conversaciones iniciadas, pedidos completados, pedidos abandonados, tiempo promedio de conversación.
Langfuse para observabilidad
Tenemos Langfuse self-hosted en el VPS como sistema de trazabilidad de llamadas a Claude. Cada llamada registra: el prompt usado, la respuesta, el modelo, los tokens consumidos, la latencia y el tenant_id. Esto nos permite detectar si algún tenant está consumiendo desproporcionadamente, si el prompt necesita ajuste, o si hay patrones de abandono en el paso de confirmación.
Números reales
En hora pico (7-9pm viernes) manejamos entre 15-25 conversaciones simultáneas sin degradación observable. El costo de infraestructura para el bot (Evolution API + Redis conversaciones + llamadas Claude Haiku) es marginal frente al stack total — el LLM domina el costo variable, mientras que el hosting es prácticamente fijo y compartido con el resto de servicios.
El mayor reto no fue técnico: fue convencer a los empleados de que el bot no les iba a quitar el trabajo, sino quitarles el WhatsApp en la mano mientras están cocinando.