Documentation Index
Fetch the complete documentation index at: https://mintlify.com/KevinhosUTP/Automatizacion-Lurwis/llms.txt
Use this file to discover all available pages before exploring further.
Descripción General
El flujo Procesador es el cerebro de la automatización de Lurwis. Funciona de manera asíncrona al flujo Receptor, consumiendo mensajes agrupados desde Redis y orquestando múltiples agentes de inteligencia artificial especializados. Utiliza Google Gemini como modelo de lenguaje principal, MongoDB para memoria conversacional persistente, y PostgreSQL para gestión transaccional de pedidos.Arquitectura basada en agentes: El sistema utiliza 5 agentes especializados (Clasificador, Detector, Pedidos, Reservas Mesas, Reservas Local, General) cada uno con su propia memoria y herramientas.
Trigger: Schedule Cron
Nodo:Schedule Trigger
El flujo NO depende de webhooks externos, sino que se ejecuta automáticamente mediante un trigger de tipo Schedule:
- Intervalo: Cada 10 segundos
- Configuración:
secondsInterval: 10 - Propósito: Buscar activamente en Redis usuarios con mensajes pendientes de procesar
Diseño asíncrono: El intervalo de 10 segundos es intencional para permitir que el flujo Receptor agrupe múltiples mensajes cortos enviados en rápida sucesión.
Flujo de Trabajo Completo
const result = $input.first().json;
const keys = Object.keys(result).filter(k => k.startsWith('ts_'));
if (keys.length === 0) return [];
return keys.map(key => ({
json: {
tsKey: key, // "ts_51900769907"
userId: key.replace('ts_', ''), // "51900769907"
bufferKey: key.replace('ts_', 'buffer_'), // "buffer_51900769907"
metaKey: key.replace('ts_', 'meta_') // "meta_51900769907"
}
}));
Split en múltiples ejecuciones: n8n ejecuta los siguientes nodos una vez por cada usuario en paralelo.
Lee el timestamp del último mensaje y verifica que hayan pasado más de 8 segundos desde el último mensaje del usuario:
Balance crucial: 8 segundos es el equilibrio entre respuesta rápida y evitar cortar al usuario mientras escribe.
Redis: Leer Buffer → Lee buffer_<userId> (mensajes concatenados)Redis: Leer Meta → Lee meta_<userId> (Phone Number ID para responder)Redis: Eliminar Buffer → Elimina buffer_<userId>Redis: Eliminar Timestamp → Elimina ts_<userId>const bufferData = $('Redis: Leer Buffer').item.json;
const metaData = $('Redis: Leer Meta').item.json;
const userData = $('Code: Expandir usuarios').item.json;
const rawBuffer = bufferData.value || '';
const idMensajero = metaData.value || '947279508470714';
// Unir mensajes separados por \n en un solo texto con espacios
const mensajesUnidos = rawBuffer
.split('\n')
.map(m => m.trim())
.filter(m => m.length > 0)
.join(' ');
// Guardia: si no hay mensaje, detener sin error
if (!mensajesUnidos || mensajesUnidos.length === 0) return [];
return {
json: {
from: userData.userId,
'text.body': mensajesUnidos,
text: { body: mensajesUnidos },
'id mensajero': idMensajero,
'id cliente': `buf_${Date.now()}`,
profile_name: 'Cliente',
buffered: true,
messageCount: rawBuffer.split('\n').filter(m => m.trim()).length
}
};
Antes de procesar la intención, verifica la hora actual del servidor (Zona horaria: America/Lima):
Nota: Este nodo no aparece explícitamente en el JSON del workflow pero está documentado en la especificación original. Puede estar implementado en una versión anterior o ser parte de lógica futura.
SELECT id, detalle_pedido, total_final, estado_pedido
FROM pedidos_picanteria
WHERE TRIM(telefono) = TRIM('{{ $('Extractormensajes').item.json.from }}')
AND estado_pedido NOT IN ('entregado', 'cancelado')
LIMIT 1
alwaysOutputData: true → Siempre devuelve resultado (vacío si no hay pedido)onError: continueErrorOutput → Continúa el flujo incluso si falla la queryDetector de pedidos (Agente especializado)Agente Clasificador (flujo normal)Analiza la intención del cliente:
Si el cliente quiere:
- Hacer un pedido nuevo
- Modificar su pedido actual
- Añadir más productos
Responde: "ACCIÓN:MODIFICAR - [explicación]"
Si el cliente SOLO quiere:
- Saber el estado de su pedido
- Consultar información
Responde: "ACCIÓN:CONSULTAR - El estado actual es: {{ estado_pedido }}"
const respuesta = $json.output.toLowerCase();
// Palabras clave que indican modificación
const palabrasModificar = [
'modificar', 'cambiar', 'añadir', 'agregar',
'actualizar', 'editar', 'quiero', 'comprar',
'pedir', 'delivery', 'pedido', 'orden'
];
// Palabras clave que indican solo consulta
const palabrasConsultar = [
'estado', 'consultar', 'saber', 'información',
'cuánto falta', '¿cómo va', 'avance'
];
// Contar coincidencias y decidir
let accion = (puntosModificar > puntosConsultar) ? "MODIFICAR" : "CONSULTAR";
return {
json: {
output: $json.output,
accion: accion,
puntos_modificar: puntosModificar,
puntos_consultar: puntosConsultar
}
};
Nodo INFO de pedido (envía estado actual por WhatsApp)Agente Clasificador (continúa al flujo normal)Eres un clasificador experto de intenciones para un restaurante.
⚠️ REGLA DE ORO (PRIORIDAD MÁXIMA):
Si el usuario menciona palabras como "pedido", "pedir", "comer", "hambre",
"quiero", "menú" o nombres de comida, la categoría ES "PEDIDOS".
⚠️ REGLA DE CONTINUIDAD:
Revisa el historial. Si ya clasificaste como PEDIDOS y el mensaje actual
es una respuesta dentro de ese flujo (nombre, dirección, confirmación),
responde con LA MISMA categoría.
Categorías:
1. PEDIDOS (Prioridad Alta)
2. RESERVAS_MESA
3. RESERVAS_LOCAL
4. GENERAL (Prioridad Baja)
Responde SOLO con: PEDIDOS, RESERVAS_MESA, RESERVAS_LOCAL o GENERAL.
Memoria: MongoDB Collection
historial_clasificador
Modelo: Google Gemini (fast model para clasificación rápida)Contexto conversacional: El agente recuerda conversaciones previas del mismo número de teléfono, permitiendo continuidad en flujos multi-mensaje.
PEDIDOSGENERALRESERVAS_MESARESERVAS_LOCALhistorial_pedidos (25 mensajes de contexto)consultar_categorias→ PostgreSQL Tool (lista categorías activas)consultar_platos→ PostgreSQL Tool (lista platos por categoría con precios)verificar_plato→ PostgreSQL Tool (calcula precio exacto + cantidad + subtotal)
historial_general (10 mensajes)buscar_info_general (código estático)<reglas_criticas>
1. SILENCIO SOBRE HERRAMIENTAS: NUNCA digas "voy a consultar" o "déjame revisar"
2. Ejecuta herramientas de forma invisible
3. NO ALUCINAR: NUNCA inventes horarios, direcciones o precios
4. Si preguntan cosas fuera de contexto (precios, disponibilidad), deriva a otras áreas
</reglas_criticas>
<regla_de_derivacion>
Si notas intención de compra, di EXACTAMENTE:
"Si deseas ver nuestro menú y ordenar, por favor responde: *Quiero hacer un pedido* 📝🦐"
</regla_de_derivacion>
const agenteOutput = $('Agente Pedidos').first()?.json?.output ?? '';
const textoUsuario = $('Extractormensajes').first().json.text?.body ?? '';
// DETECCIÓN CONFIRMACIÓN ESTRICTA
const esConfirmacion = /\bconfirm[oa]\b/i.test(textoUsuario);
// Si NO dijo "confirmo", forzar totalfinal = 0 (NO va a DB)
if (!esConfirmacion) {
return [{ json: {
output: agenteOutput,
totalfinal: 0, // FRENO DE MANO
from: datosUsuario.from,
'id mensajero': datosUsuario.idMensajero
}}];
}
// SI DIJO CONFIRMO, extraer JSON
const matchJSON = agenteOutput.match(/\{[\s\S]*?\}/);
if (matchJSON) {
try {
datosParsed = JSON.parse(matchJSON[0]);
} catch(e) {}
}
const totalFinal = parseFloat(datosParsed?.total ?? 0) || 0;
// Ocultar JSON del mensaje al cliente
const outputFinal = agenteOutput.replace(/\{[\s\S]*?\}/g, '').trim();
return [{ json: {
output: outputFinal,
nombre: datosParsed?.nombre,
direccion: datosParsed?.direccion,
pedidodetallado: datosParsed?.pedido,
total: totalFinal,
metodopago: datosParsed?.metodopago,
tiposervicio: datosParsed?.tiposervicio,
totalfinal: totalFinal,
from: datosUsuario.from,
'id mensajero': datosUsuario.idMensajero
}}];
UPDATE pedidos_picanteria
SET
cliente_nombre = '{{ $json.nombre }}',
detalle_pedido = '{{ JSON.stringify({descripcion: $json.pedidodetallado}) }}'::jsonb,
total_final = {{ $json.total }},
metodo_pago = '{{ $json.metodopago }}',
tipo_servicio = '{{ $json.tiposervicio }}',
estado_pedido = 'confirmado'
WHERE id = '{{ $('Validar si tiene pedido o no').first().json.id }}'::uuid
RETURNING id;
Enviar mensaje FInal Modificado (WhatsApp)Notificar Error DB - Modificar (WhatsApp con código de error)INSERT INTO pedidos_picanteria (
telefono,
cliente_nombre,
detalle_pedido,
total_final,
metodo_pago,
tipo_servicio,
estado_pedido,
fecha_creacion
) VALUES (
'{{ $json.from }}',
'{{ $json.nombre }}',
'{{ JSON.stringify({descripcion: $json.pedidodetallado}) }}'::jsonb,
{{ $json.total }},
'{{ $json.metodopago }}',
'{{ $json.tiposervicio }}',
'confirmado',
NOW()
)
RETURNING id;
Enviar mensaje FInal nuevo Pedido (WhatsApp)Notificar Error DB - Insertar (WhatsApp con código de error)¡Pedido Confirmado! ✅
Hola {{ nombre }} 👋, hemos recibido tu orden exitosamente ¡tu orden ya está en cocina! 👨🍳.
🧾 *Resumen del Pedido:*
{{ pedidodetallado }}
💰 *Total a pagar:* S/ {{ total }}
💳 *Método de pago:* {{ metodopago }}
🛵 *Tipo de servicio:* {{ tiposervicio }}
En cuanto su pedido esté listo le avisaremos 💫.
Gracias por su compra, Picantería Lurwis les desea buen provecho 🦐
¡Pedido modificado Confirmado! ✅
Listo! {{ nombre }} 👋, hemos actualizado tu orden correctamente con los cambios. Así quedó tu pedido final:
🧾 *Nuevo resumen del Pedido:*
{{ pedidodetallado }}
💰 *Total a pagar:* S/ {{ total }}
💳 *Método de pago:* {{ metodopago }}
🛵 *Tipo de servicio:* {{ tiposervicio }}
En cuanto su pedido esté listo le avisaremos 💫.
Gracias por su compra, Picantería Lurwis les desea buen provecho 🦐
Diagrama de Flujo Simplificado
Arquitectura de Datos
PostgreSQL (Transaccional)
Credencial:Postgres Lurwis db (ID: 5nRuBkRnPEOnj0KP)
Tablas principales:
Esquema de pedidos_picanteria
Esquema de pedidos_picanteria
Esquema de categorias y platos
Esquema de categorias y platos
MongoDB (Memoria Conversacional)
Credencial:Memoria chats Lurwis (ID: rEzqy3CH5hFmOQ2S)
Base de datos: picanteria_db
Collections por agente:
| Collection | Agente | Context Window | Estado |
|---|---|---|---|
historial_clasificador | Agente Clasificador | Default | ✅ Producción |
historial_detector | Detector de pedidos | Default | ✅ Producción |
historial_pedidos | Agente Pedidos | 25 mensajes | ✅ Producción |
historial_general | Agente General | 10 mensajes | ✅ Producción |
historial_reservas | Reserva Mesas | 15 mensajes | ⚠️ Por crear |
historial_eventos | Reservas Local | 15 mensajes | ⚠️ Por crear |
Session Keys: Todas las memorias usan el número de teléfono (
from) como clave de sesión, permitiendo contexto persistente por usuario.Redis (Buffer Temporal)
Credencial:Buffer Lurwis (ID: LuOIeLYx2ps0SCoP)
Patrón de claves consumidas:
buffer_<userId>→ Mensajes concatenados (consumido y eliminado)ts_<userId>→ Timestamp último mensaje (consumido y eliminado)meta_<userId>→ Phone Number ID (consumido pero persiste hasta TTL)
Tecnologías Utilizadas
| Componente | Tecnología | Uso |
|---|---|---|
| Scheduler | n8n Schedule Trigger | Ejecución cada 10 segundos |
| LLM Principal | Google Gemini | Agentes de IA (modelos flash/thinking) |
| Memoria Conversacional | MongoDB + LangChain | Contexto persistente por usuario |
| Base de Datos Transaccional | PostgreSQL | Pedidos, menú, precios |
| Buffer de Mensajes | Redis | Agrupación temporal |
| Mensajería | WhatsApp Business API | Envío de respuestas |
| Orquestador | n8n | Workflow engine |
Credenciales Configuradas
| Servicio | Nombre | ID Interno |
|---|---|---|
| PostgreSQL | Postgres Lurwis db | 5nRuBkRnPEOnj0KP |
| MongoDB | Memoria chats Lurwis | rEzqy3CH5hFmOQ2S |
| Redis | Buffer Lurwis | LuOIeLYx2ps0SCoP |
| Google Gemini | Modelo Lurwis | EGUpckskyMrPNfc4 |
| WhatsApp API | WhatsApp account | zJi8oUdszoG5oEqW |
Manejo de Errores
Estrategias por Tipo de Error
El nodo
Extractormensajes retorna array vacío si mensajesUnidos está vacío, deteniendo el flujo limpiamente.retryOnFail: false, waitBetweenTries: 5000Optimizaciones y Consideraciones
Performance
Ejecución paralela: n8n procesa múltiples usuarios simultáneamente cuando hay varios buffers listos.
- Modelo rápido para clasificación: Usa Gemini Flash (fast) en Clasificador y Detector
- Modelo pensante para pedidos: Usa Gemini (thinking) en Agente Pedidos para lógica compleja
- Índices de PostgreSQL: Tablas tienen índices en
telefonoyestado_pedido
Context Window por Agente
| Agente | Mensajes | Justificación |
|---|---|---|
| Pedidos | 25 | Conversaciones largas con múltiples platos |
| Reservas | 15 | Flujo más corto, datos puntuales |
| General | 10 | Preguntas simples, pocas iteraciones |
| Clasificador/Detector | Default | Solo necesita último contexto |
Zona Horaria
Todo el sistema opera en America/Lima (UTC-5).Estado del Proyecto
Según notas del desarrollador:“Solamente está probada para ver si funciona correctamente, sería cuestión de cambiar el orden ya, ya funcionó, ahora toca ponerlo bonito xd.”
Monitoreo y Auditoría
Tabla de logs (mencionada en spec original):logs_auditoria→ Guarda historial de chat al confirmar pedidos- Permite trazabilidad completa de conversaciones
Integración con CRM
En desarrollo: Sistema de gestión de relaciones con clientes que consumirá los datos de PostgreSQL. La base de datos tiene Row-Level Security (RLS) configurado para acceso seguro.
Diagrama de Arquitectura Completa
Próximos Pasos
Recursos Relacionados
- Flujo Receptor - Punto de entrada de webhooks
- Arquitectura del Sistema - Visión general
- Google Gemini Integration - Configuración de AI y modelos