Saltar al contenido principal
caso // Internal tool
En producción

Preventa IA

Preventa de productos frescos con recordatorios por WhatsApp y Telegram, IA que interpreta cada respuesta y revisión humana antes de confirmar el pedido.

2025 · En producción

  • Full-stack developer & architect
  • 3 meses
  • Privado
  • Laravel 13
  • PHP 8.3+
  • IA aplicada
  • LLM
  • MariaDB
  • WhatsApp Business
  • Telegram Bot
  • Internal tool
Preventa IA //
EN PRODUCCIÓN
PREVENTA IA · 2025
02 // FICHA DEL PROYECTO

Preventa IA en un vistazo.

Año

2025

Tipo

Internal tool

Estado

En producción

Rol

Full-stack developer & architect

Duración

3 meses

Licencia

Privado

Stack aplicado //

PHP 8.3+Laravel 13BladeTailwind CSS 3Alpine.jsVite 8MariaDB 11.4WhatsApp Business Cloud APITelegram Bot APICliente OpenAI-compatibleDocker ComposeLaravel SchedulerPHPUnit 12Laravel Pint
ASÍ SE VE //

Capturas reales de Preventa IA.

Capturas reales del sistema en marcha. Sin mocks, sin renders.

Recorrido por el ciclo de preventa: recordatorios automáticos por WhatsApp y Telegram, respuesta del cliente, interpretación con IA y bandeja de revisión humana antes de aprobar el pedido.

Panel

Dashboard del día

La primera pantalla del sistema: tarjetas con las métricas del día (recordatorios enviados, clientes pendientes, respuestas recibidas, pedidos pendientes de revisión y errores) junto a los pedidos y mensajes más recientes.

Revisión

Bandeja de pedidos

El corazón operativo: todos los pedidos interpretados por IA desde WhatsApp y Telegram en tarjetas con su mensaje original, resumen de IA, estado y fuente, con filtros por estado, fechas, cliente, revisor y origen.

Revisión

Revisión de pedido con IA

La pantalla de decisión: mensaje original del cliente junto al resumen de la IA con intención y confianza, el pedido interpretado con items y notas editables, y las acciones de aprobar, marcar como dudoso o descartar.

Clientes

Detalle de cliente

Ficha completa del cliente recurrente: nombre, teléfono, día de visita, pedido habitual, consentimiento y estado, con el historial de mensajes y pedidos que da contexto a la IA en cada interpretación.

Clientes

Listado de clientes

Gestión de la cartera con búsqueda y filtros avanzados por día de visita, consentimiento y estado activo o inactivo. Cada fila muestra el pedido habitual, el canal y el estado de consentimiento del cliente.

Clientes

Importación desde Excel/CSV

Alta masiva sin errores: subida de CSV o Excel, vista previa de los datos, detección de duplicados por teléfono, validación de campos con registro de errores por fila y descarga de un CSV de ejemplo generado con IA.

Mensajería

Mensajes entrantes

Historial de mensajes con filtros por proveedor, dirección, estado y cliente. Cada respuesta recibida por webhook queda registrada junto a su interpretación, cerrando el ciclo de trazabilidad.

Configuración

Configuración del sistema

Panel centralizado para recordatorios, mensajería e IA: horarios de envío, plantillas, proveedor activo y parámetros del modelo, con los valores sensibles (tokens y claves de API) enmascarados.

Administración

Usuarios y roles

Gestión de usuarios con roles de administrador y operador, activación y desactivación de cuentas y protección del admin principal, que no puede eliminarse ni desactivarse desde la interfaz.

03 // CONTEXTO

De dónde partía Preventa IA.

PUNTO DE PARTIDA //

Los distribuidores de productos perecederos gestionan a diario pedidos recurrentes de clientes fijos (restaurantes, bares, panaderías, tiendas). El proceso implica recordar a cada cliente por teléfono o mensajería, anotar los pedidos manualmente, interpretar cambios o solicitudes especiales y transferir la información al sistema de facturación. Con 20-50 clientes por distribuidor, es un proceso manual y propenso a errores, dependiente de la memoria del equipo, de escalabilidad limitada y sin trazabilidad de qué se pidió, cuándo ni cómo se interpretó cada mensaje.

LO QUE FALTABA //

Automatizar el primer contacto con el cliente (recordatorio y recepción de la respuesta) y la primera interpretación del pedido, manteniendo intacto el control humano: toda propuesta debe ser revisada y confirmada por un operador antes de procesarse.

EL OBJETIVO //

Construir una aplicación web interna que envíe recordatorios automáticos por WhatsApp o Telegram según el día de visita de cada cliente, reciba las respuestas por webhook, las interprete con IA en pedidos estructurados y prepare una bandeja de revisión humana para aprobar, dudar o descartar cada operación.

Para quién es //

  • Administrador del sistemaGestionar clientes (alta individual o importación masiva), usuarios con roles, configuración de recordatorios, mensajería e IA, y supervisar la operación desde el panel.
  • Operador / Revisor de pedidosRevisar los pedidos propuestos por la IA, ver el mensaje original junto a la interpretación, corregir si hace falta y aprobar, marcar como dudoso o descartar en un clic.
  • Cliente final (externo)Recibir el recordatorio en su chat habitual de WhatsApp o Telegram y responder en lenguaje natural, sin instalar nada ni aprender un canal nuevo.
  • Sistema automáticoEjecutar los procesos sin persona: scheduler de recordatorios, recepción de webhooks e interpretación con IA, con idempotencia y fallback seguro ante cualquier error.
04 // LA SOLUCIÓN

Qué se construyó y por qué.

Dos componentes principales: un scheduler envía recordatorios automáticos en dos rondas (09:00 y 14:00) según el día de visita de cada cliente, y un servicio de IA interpreta cada respuesta recibida por webhook convirtiéndola en un pedido estructurado. Un panel web centraliza clientes, pedidos, mensajes, importación y configuración, y ninguna interpretación se confirma sin pasar por la bandeja de revisión humana.

01

Recordatorios en dos rondas según el día de visita de cada cliente.

02

IA que interpreta y propone, nunca decide: toda interpretación genera un pedido pendiente de revisión.

03

Validación JSON robusta con fallback degradado ante cualquier error del modelo.

04

Multi-proveedor de mensajería con una interfaz común: WhatsApp y Telegram intercambiables.

05

Panel centralizado: clientes, pedidos, mensajes, importación y configuración en una sola web.

06

Importación desde Excel/CSV para arrancar sin depender de ERP ni catálogo de productos.

Lo que hace //

Tradeoff honesto //

Es un área que requiere mantenimiento constante: cada exportación nueva o variación de formato entre proveedores exige ampliar la normalización.

05 // ARQUITECTURA

Cómo está construido Preventa IA.

Monolito Laravel 13 con Blade, Tailwind CSS y Alpine.js en la presentación y MariaDB 11.4 como persistencia. Seis capas con responsabilidades separadas: presentación, HTTP, acciones, servicios, dominio e infraestructura. Los webhooks de WhatsApp y Telegram entran por la capa HTTP, las acciones orquestan el flujo (registrar mensaje, interpretar, crear pedido, revisar) y la bandeja de revisión humana es el punto de control obligatorio antes de cerrar un pedido.

Presentación (Blade + Tailwind + Alpine.js)

  • Vistas y layouts server-rendered con Blade y Tailwind CSS
  • Componentes Alpine.js: dashboard, bandeja de revisión con filtros, importación y formularios
  • Estética slate + indigo con Figtree, minimalista y orientada a operadores
  • Vite 8 para el bundling de assets JS y CSS

HTTP (Controladores y middleware)

  • Controladores por dominio: Client, Order, Message, Import y Settings
  • Middleware de autenticación, verificación de usuario activo y comprobación de roles
  • Form Requests para la validación de entrada

Acciones (Action Pattern)

  • InterpretIncomingMessageAction y CreateOrderFromAIInterpretationAction
  • ProcessRemindersAction con registro idempotente en reminder_logs
  • ProcessIncomingWhatsAppMessageAction y ProcessIncomingTelegramMessageAction
  • MarkReminderResponsesAction y ReviewOrderAction

Servicios

  • IA: AIOrderInterpreterService + OpenAiCompatibleClient + AIJsonValidator
  • Mensajería: WhatsAppService + WhatsAppWebhookParser y TelegramService + TelegramWebhookParser
  • Importación: ClientImportService + AIClientGeneratorService
  • Dashboard: DashboardMetricsService · Configuración: SettingsService

Dominio

  • Modelos: Client, Order, Message, AIInterpretation, ReminderLog, ImportLog, Setting y User
  • Contratos: MessagingService y AiClient (Strategy Pattern)
  • MessageObserver para cerrar el ciclo recordatorio-respuesta
  • Value Objects: AiContext, AiResponse, MessageStatus y ProcessRemindersResult

Infraestructura

  • MariaDB/MySQL como persistencia
  • Laravel Cache: configuración cacheada durante 5 minutos
  • Laravel Queue para tareas asíncronas
  • Laravel Scheduler para los recordatorios de 09:00 y 14:00

Flujo de datos //

  1. Scheduler ejecuta preventa:reminders (09:00) y selecciona clientes con visita hoy y consentimiento aprobado
  2. MessagingService envía el recordatorio por WhatsApp o Telegram y registra el envío en reminder_logs
  3. El cliente responde y el webhook (WhatsApp o Telegram) entrega el payload al parser correspondiente
  4. ProcessIncomingMessageAction registra el mensaje, actualiza last_contacted_at y MessageObserver marca los recordatorios como respondidos
  5. InterpretIncomingMessageAction envía el mensaje con contexto al AIOrderInterpreterService y AIJsonValidator valida y normaliza la respuesta
  6. Si el intent es relevante se crea un pedido pending_review; el operador lo aprueba, marca como dudoso o descarta en la bandeja

Autenticación //

Autenticación por sesión con Laravel Breeze y dos roles: administrador (clientes, usuarios, configuración y logs) y operador (bandeja de revisión y consulta de clientes), con middleware de usuario activo y comprobación de rol. Los webhooks no usan sesión: se autentican con validación HMAC-SHA256 (WhatsApp) y secret_token (Telegram).

Notas técnicas //

  • La IA nunca escribe directamente en la base de datos: solo genera propuestas que un humano debe validar.
  • Si la IA falla, el mensaje queda registrado con intent=unclear y needs_human_review=true: no se pierde ninguna respuesta.
  • La idempotencia de recordatorios se apoya en reminder_logs con timestamps por cliente y tipo de recordatorio.
  • El consentimiento del cliente (consent_status) bloquea el envío de recordatorios si no está en estado aprobado.
  • El admin principal no puede eliminarse ni desactivarse desde la interfaz.
  • Cambiar de proveedor de mensajería es cuestión de configuración, no de código.
06 // PROCESO

De la idea a la versión en marcha.

  1. 01

    Definición del problema

    Análisis del proceso manual de preventa: recordar clientes por teléfono o mensajería, anotar pedidos a mano, interpretar cambios y transferirlos al sistema de facturación. Manual, dependiente de la memoria, sin trazabilidad y de escalabilidad limitada.

  2. 02

    Modelo de datos y arquitectura

    Diseño de las entidades (clients, messages, orders, reminder_logs, ai_interpretations) y de las decisiones clave: interfaz MessagingService para desacoplar la mensajería, AIJsonValidator para validar la salida del LLM y revisión humana obligatoria por diseño.

  3. 03

    Backend Laravel 13

    Autenticación con Breeze, controladores por dominio, servicios de mensajería, IA, importación y dashboard, y comandos de recordatorios (preventa:reminders) con registro idempotente en reminder_logs.

  4. 04

    Frontend con Blade, Tailwind y Alpine.js

    Vistas server-rendered con la paleta slate + indigo y Figtree: dashboard operativo, bandeja de pedidos con filtros, importación con vista previa, configuración y gestión de usuarios.

  5. 05

    Mensajería multi-proveedor

    Implementación de WhatsApp Business Cloud API (plantillas y validación HMAC) y Telegram Bot API (webhook con secret_token), ambas bajo la misma interfaz MessagingService con proveedor activo configurable.

  6. 06

    Interpretación con IA

    Prompt de sistema con reglas estrictas (no confirmar, no inventar, dudar si es ambiguo), cliente HTTP OpenAI-compatible y AIJsonValidator que normaliza intents, valida confianza y acciones, y aplica fallback a unclear ante cualquier error.

  7. 07

    Bandeja de revisión humana

    Pantalla de detalle con el mensaje original del cliente, el resumen de la IA con confianza, el pedido interpretado editable y el historial de mensajes. Acciones de aprobar, dudoso o descartar con registro de revisor y timestamp.

  8. 08

    Dashboard, configuración y usuarios

    Métricas del día en tarjetas, panel de settings con horarios, plantillas y parámetros de IA con valores enmascarados, y CRUD de usuarios con roles y protección del admin principal.

  9. 09

    Importación masiva y generador CSV

    Carga desde CSV y Excel con vista previa, detección de duplicados por teléfono y registro de errores por fila. Generador de CSV de ejemplo con IA para preparar datos de prueba y verificar la importación.

  10. 010

    Testing completo

    33 archivos de tests con PHPUnit 12 cubriendo controladores, servicios, acciones, webhooks, parsers y comandos, con mocks específicos (FakeAiClient, FakeMessagingService) para pruebas aisladas.

07 // RETOS

Decisiones con criterio.

Problemas reales durante el desarrollo, soluciones aplicadas y compromisos asumidos.

01

Normalización de teléfonos entre proveedores

Problema //

Los teléfonos llegan en formatos muy distintos (+34612345678, 612345678, 612 345 678) y cada proveedor de mensajería puede devolver el número con variaciones propias, lo que rompe la búsqueda del cliente al recibir una respuesta.

Cómo se resolvió //

Implementación de un value object PhoneNumber que normaliza, compara y persiste los números en un formato único, usado tanto en la importación masiva como en el parseo de webhooks para localizar al cliente por teléfono o chat_id.

Tradeoff //

Es un área que requiere mantenimiento constante: cada exportación nueva o variación de formato entre proveedores exige ampliar la normalización.

02

Parsing de respuestas de IA

Problema //

El modelo puede devolver texto envuelto en bloques markdown, JSON malformado o campos faltantes. En preventa, un fallo de interpretación se traduce en un pedido equivocado al cliente.

Cómo se resolvió //

AIJsonValidator y OpenAiCompatibleClient implementan varias capas de parsing: extracción del JSON, validación de intents permitidos, confianza en rango 0-1 y acciones válidas (add/remove/replace/keep), con normalización de datos inconsistentes.

Tradeoff //

El parsing sigue siendo inherentemente frágil: la robustez se construye con capas de validación y fallback, no eliminando la posibilidad de que el modelo devuelva algo inesperado.

03

Webhooks y concurrencia

Problema //

Los webhooks de WhatsApp y Telegram pueden llegar en ráfagas: varios mensajes del mismo cliente o de clientes distintos en el mismo segundo, con riesgo de estados de recordatorio inconsistentes o pedidos duplicados.

Cómo se resolvió //

Manejo de la concurrencia en la actualización de estados de recordatorios (MessageObserver dispara MarkReminderResponsesAction) y en la creación de pedidos, apoyándose en el registro idempotente de reminder_logs y en la asociación de cada pedido a la interpretación de su mensaje.

Tradeoff //

No se introdujo una cola de procesamiento externa en el MVP: los webhooks se procesan de forma síncrona, lo que simplifica la infraestructura pero exige handlers idempotentes y rápidos.

04

Scheduler y zonas horarias

Problema //

El scheduler depende de la zona horaria configurada (Europe/Madrid): las rondas de 09:00 y 14:00 se calculan sobre ese huso. Cambiar de zona o soportar clientes en varias zonas requeriría modificaciones significativas.

Cómo se resolvió //

La zona horaria se centraliza en la configuración del sistema y el segundo recordatorio se calcula por umbral sent_at + gap configurable en lugar de por horario fijo, lo que mantiene la lógica estable ante reinicios o ejecuciones manuales.

Tradeoff //

El sistema asume una única zona horaria para toda la cartera: el soporte multi-zona queda fuera del alcance actual y exigiría rediseñar la selección de candidatos de los recordatorios.

08 // RESULTADOS

Métricas y consecuencias reales.

Archivos de test

33

controladores, servicios, acciones, webhooks, parsers y comandos

Proveedores de mensajería

2

WhatsApp Business y Telegram, intercambiables por configuración

Rondas de recordatorio

2

09:00 y 14:00 según el día de visita del cliente

Intents de IA

6

confirmar, modificar, cancelar, preguntar, unclear e irrelevante

Capas de arquitectura

6

presentación → HTTP → acciones → servicios → dominio → infraestructura

Revisión del pedido

Sin confirmación automática

ningún pedido se cierra sin pasar por revisión humana

Rendimiento //
  • Monolito server-rendered con Blade: mínimo JavaScript y carga inicial rápida del panel.
  • Scheduler nativo de Laravel sin workers externos ni cron manual para los recordatorios.
  • Validación de respuestas de IA en memoria con normalización antes de tocar la base de datos.
  • Configuración cacheada durante 5 minutos: los cambios se reflejan sin reiniciar el sistema.
Experiencia de uso //
  • El operador compara el mensaje original del cliente con la propuesta de la IA en una sola pantalla.
  • Aprobar, marcar como dudoso o descartar en un clic, con registro de revisor y timestamp.
  • El dashboard resume cómo va la ruta del día: enviados, respuestas, pendientes de revisar y errores.
  • El cliente recibe el recordatorio en su chat habitual y responde en lenguaje natural, sin instalar nada.
Impacto //
  • Elimina el primer cuello de botella de la preventa: recordar, contactar y anotar cada pedido a mano.
  • Aporta trazabilidad completa: cada envío, respuesta, interpretación y decisión queda registrado con timestamps.
  • La revisión humana obligatoria evita errores costosos en un negocio de márgenes ajustados.
  • Sistema en producción para un cliente externo del sector distribución: el impacto de negocio se mide sobre el uso diario.
09 // LO QUE APRENDÍ

Lo que deja este caso.

LO QUE FUNCIONÓ //
  • Interfaz MessagingService: cambiar de proveedor de mensajería es cuestión de configuración, no de código.
  • IA con fallback degradado: ningún mensaje se pierde aunque el modelo falle o devuelva JSON inválido.
  • Separación IA-humano por diseño: la revisión obligatoria preserva el control comercial y la confianza del operador.
  • AIJsonValidator: validar la salida del LLM antes de tocar la base de datos elimina los pedidos fantasma.
  • MessageObserver: el ciclo recordatorio-respuesta se cierra solo, sin intervención manual.
QUE MEJORARÍA //
  • Definir métricas de negocio desde el inicio (tiempo medio de revisión, % interpretaciones correctas): hoy no hay datos que validen el impacto real.
  • Medir el rendimiento de la importación masiva con archivos grandes antes de producción: el procesado en memoria puede quedarse corto con miles de registros.
  • Soportar múltiples zonas horarias desde el diseño: hoy el scheduler depende de Europe/Madrid.
  • Endurecer la seguridad y el QA antes del primer despliegue en producción, no dejarlos para el final.
LO QUE APRENDÍ //
  • La robustez de un sistema con LLM se construye fuera del modelo: prompt estricto, validador y fallback.
  • Una interfaz de contrato bien diseñada (MessagingService) vale más que mil configuraciones especiales.
  • Un MVP sin catálogo ni integración ERP entrega valor pronto y deja hooks claros para crecer después.
  • El consentimiento del cliente es una puerta funcional, no un trámite legal: bloquea envíos y evita riesgos regulatorios desde el primer día.
10 // SIGUE EXPLORANDO

Servicios, proyectos y lecturas relacionadas

Otros puntos de la web donde se aplica este mismo enfoque o se profundiza en temas cercanos.