Skip to main content

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

1
Paso 1: Búsqueda de Usuarios Activos en Redis
2
Nodo: Redis: Buscar usuarios activos (Redis Keys)
3
// Operación: Keys
// Patrón: "ts_*"
4
Busca todas las claves que comienzan con ts_ (timestamps de usuarios con mensajes pendientes).
5
Respuesta ejemplo:
6
{
  "ts_51900769907": "1771550707733",
  "ts_51987654321": "1771550710245"
}
7
Paso 2: Expansión de Usuarios
8
Nodo: Code: Expandir usuarios (JavaScript Code)
9
Transforma el objeto de Redis en un array de items individuales para procesar cada usuario:
10
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"
  }
}));
11
Split en múltiples ejecuciones: n8n ejecuta los siguientes nodos una vez por cada usuario en paralelo.
12
Paso 3: Validación de Tiempo de Espera
13
Nodo: Redis: Leer TimestampIf
14
Lee el timestamp del último mensaje y verifica que hayan pasado más de 8 segundos desde el último mensaje del usuario:
15
// Condición
Date.now() - Number($json.value) > 8000
16
Lógica:
17
  • Si el usuario escribió hace < 8 segundos: NO procesar (puede seguir escribiendo)
  • Si pasaron > 8 segundos: SÍ procesar (finalizó su input)
  • 18
    Balance crucial: 8 segundos es el equilibrio entre respuesta rápida y evitar cortar al usuario mientras escribe.
    19
    Paso 4: Lectura y Limpieza del Buffer
    20
    4.1 Lectura de Datos
    21
    Tres nodos Redis en paralelo:
    22
  • 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>
  • 23
    Consumo atómico: Los datos se leen y eliminan inmediatamente para evitar procesamiento duplicado.
    24
    4.2 Formateo de Datos
    25
    Nodo: Extractormensajes (JavaScript Code)
    26
    Transforma los datos de Redis en el formato esperado por los agentes:
    27
    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
      }
    };
    
    28
    Ejemplo de transformación:
    29
  • Buffer Redis: "Hola\nQuiero\nUn ceviche"
  • Output: "Hola Quiero Un ceviche"
  • 30
    Paso 5: Control de Horario de Atención
    31
    Nodo: Verificar Horario (If) — Mencionado en documentación original
    32
    Antes de procesar la intención, verifica la hora actual del servidor (Zona horaria: America/Lima):
    33
  • Horario de atención: 10:00 AM - 11:00 PM
  • Fuera de horario: Envía mensaje automático de “Local Cerrado” con los horarios
  • Dentro de horario: Continúa al flujo de clasificación
  • 34
    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.
    35
    Paso 6: Validación de Pedido Pendiente
    36
    Nodo: Validar si tiene pedido o no (PostgreSQL)
    37
    Consulta la base de datos para verificar si el cliente tiene un pedido activo:
    38
    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
    
    39
    Configuración:
    40
  • alwaysOutputData: true → Siempre devuelve resultado (vacío si no hay pedido)
  • onError: continueErrorOutput → Continúa el flujo incluso si falla la query
  • 41
    Bifurcación según resultado
    42
    Nodo: ¿Tiene Pedido Pendiente? (If)
    43
    // Condición
    $json.id !== empty
    
    44
  • Tiene pedido:Detector de pedidos (Agente especializado)
  • No tiene pedido:Agente Clasificador (flujo normal)
  • 45
    Paso 7A: Flujo con Pedido Existente
    46
    Nodo: Detector de pedidos (AI Agent)
    47
    Propósito: Distinguir si el cliente quiere:
    48
  • Modificar su pedido existente (añadir, cambiar, actualizar)
  • Consultar el estado de su pedido
  • 49
    System Prompt (resumido):
    50
    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 }}"
    
    51
    Memoria: MongoDB Collection historial_detector
    52
    Análisis de Intención
    53
    Nodo: Analizador Intención Flexible (JavaScript Code)
    54
    Procesa la respuesta del agente y categoriza usando palabras clave:
    55
    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
      }
    };
    
    56
    Bifurcación de Acción
    57
    Nodo: ¿Qué eligió el cliente? (Switch)
    58
  • Output “No modifica”:Nodo INFO de pedido (envía estado actual por WhatsApp)
  • Output “Modifica”:Agente Clasificador (continúa al flujo normal)
  • 59
    Paso 7B: Flujo sin Pedido (Normal)
    60
    Nodo: Agente Clasificador (AI Agent)
    61
    Propósito: Categorizar la intención del mensaje en una de 4 rutas principales.
    62
    System Prompt (simplificado):
    63
    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.
    
    64
    Memoria: MongoDB Collection historial_clasificador Modelo: Google Gemini (fast model para clasificación rápida)
    65
    Contexto conversacional: El agente recuerda conversaciones previas del mismo número de teléfono, permitiendo continuidad en flujos multi-mensaje.
    66
    Paso 8: Enrutamiento a Agentes Especializados
    67
    Nodo: Clasificador especializado a cada Agente (Switch)
    68
    Dirige el flujo según la clasificación:
    69
    OutputAgente DestinoEstadoPEDIDOSAgente Pedidos✅ Producción (lógica completa)GENERALAgente General✅ ProducciónRESERVAS_MESAAgente Reserva Mesas⚠️ En desarrollo (falta lógica)RESERVAS_LOCALAgente Reservas Local⚠️ En desarrollo (falta lógica)
    70
    Paso 9: Agentes Especializados
    71
    9.1 Agente Pedidos (Producción)
    72
    Nodo: Agente Pedidos (AI Agent)
    73
    Rol: Wilson, asistente de pedidos de Picantería Lurwis
    74
    Características:
    75
  • Modelo: Google Gemini (thinking model para razonamiento complejo)
  • Memoria: MongoDB Collection historial_pedidos (25 mensajes de contexto)
  • Herramientas disponibles:
    1. consultar_categorias → PostgreSQL Tool (lista categorías activas)
    2. consultar_platos → PostgreSQL Tool (lista platos por categoría con precios)
    3. verificar_plato → PostgreSQL Tool (calcula precio exacto + cantidad + subtotal)
  • 76
    System Prompt (puntos clave):
    77

    Ver reglas principales del Agente Pedidos

    <REGLAS_DE_FORMATO>
    - NUNCA incluyas texto técnico como "[Used tools:" o "Tool:"
    - Respuesta es SOLO el mensaje final para WhatsApp
    - Usa *negrita* ÚNICAMENTE para:
      1. Total final
      2. Palabra "confirmo"
      3. Tipo de servicio (Recojo/Delivery)
      4. Método de pago
      5. Nombre del cliente
    </REGLAS_DE_FORMATO>
    
    <LIMITES_DEL_MENU>
    ⚠️ REGLA DE SENTIDO COMÚN:
    Eres asistente de PICANTERÍA (pescados, mariscos, comida criolla).
    - Si piden pizza, hamburguesa, pollo a la brasa, sushi: RECHAZAR amablemente
    - No digas "voy a buscar" esos platos
    - Ofrece categorías reales con humor
    </LIMITES_DEL_MENU>
    
    <POLITICA_DE_PRECIOS>
    - Precios de 'verificar_plato' son SAGRADOS
    - Si cliente regatea: RECHAZA el descuento, pregunta si acepta precio oficial
    - NO pidas "confirmo" hasta que acepte el precio
    </POLITICA_DE_PRECIOS>
    
    <FLUJO_OBLIGATORIO>
    1. Obtener nombre completo del cliente
    2. Navegar por categorías y platos usando herramientas
    3. Calcular subtotales con verificar_plato
    4. Preguntar método de pago (Yape/Plin/Efectivo/Tarjeta)
    5. Preguntar tipo de servicio (Delivery con dirección / Recojo en local)
    6. EXIGIR palabra exacta "confirmo" del cliente
    7. Generar JSON con estructura:
    {
      "nombre": "string",
      "direccion": "string o 'Recojo en local'",
      "pedido": "string descriptivo",
      "total": number,
      "metodopago": "string",
      "tiposervicio": "Delivery o Recojo"
    }
    </FLUJO_OBLIGATORIO>
    
    78
    Herramientas PostgreSQL:
    79

    Ver queries SQL de las herramientas

    1. consultar_categorias
    SELECT id, nombre 
    FROM categorias 
    WHERE activo = true 
    ORDER BY id
    
    2. consultar_platos
    SELECT 
      p.id,
      p.nombre,
      p.descripcion,
      json_agg(
        json_build_object('tamanio', pp.tamanio, 'precio', pp.precio)
        ORDER BY pp.precio
      ) AS precios
    FROM platos p
    JOIN plato_precios pp ON pp.plato_id = p.id
    WHERE p.categoria_id = {{ $fromAI('categoriaid', 'ID numérico de la categoría elegida', 'number') }}
    AND p.activo = true
    AND pp.activo = true
    GROUP BY p.id, p.nombre, p.descripcion
    ORDER BY p.nombre
    
    3. verificar_plato
    SELECT 
      p.nombre,
      pp.tamanio,
      pp.precio AS precio_unitario,
      {{ $fromAI('cantidad', 'Cantidad de unidades, default 1', 'number') }}::int AS cantidad,
      (pp.precio * {{ $fromAI('cantidad', 'Cantidad, default 1', 'number') }}::int) AS subtotal
    FROM platos p
    JOIN plato_precios pp ON pp.plato_id = p.id
    WHERE p.id = {{ $fromAI('platoid', 'ID numérico del plato', 'number') }}
    AND LOWER(pp.tamanio) = LOWER('{{ $fromAI('tamanio', 'Tamaño: Personal, Familiar o Único', 'string') }}')
    AND p.activo = true
    AND pp.activo = true
    
    80
    9.2 Agente General (Producción)
    81
    Nodo: Agente General (AI Agent)
    82
    Rol: Wilson - Info, especialista en atención al cliente
    83
    Características:
    84
  • Modelo: Google Gemini (fast model)
  • Memoria: MongoDB Collection historial_general (10 mensajes)
  • Herramientas: buscar_info_general (código estático)
  • 85
    System Prompt (reglas clave):
    86
    <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>
    
    87
    Información que maneja:
    88
  • Ubicación: Chiclayo (con enlace Google Maps)
  • Métodos de pago: Yape, Plin, Efectivo, Tarjeta
  • Horarios de atención
  • Redes sociales
  • 89
    9.3 Agente Reserva Mesas (En Desarrollo)
    90
    Nodo: Agente Reserva Mesas (AI Agent)
    91
    Estado: ⚠️ Lógica pendiente, collections de MongoDB por crear
    92
    Propósito: Gestionar reservas de mesas (2-12 personas)
    93
  • Obtener: Fecha, hora, número de comensales
  • Validar disponibilidad
  • Solicitar número de contacto
  • Registrar reserva
  • 94
    9.4 Agente Reservas Local (En Desarrollo)
    95
    Nodo: Agente Reservas Local (AI Agent)
    96
    Estado: ⚠️ Lógica pendiente, collections de MongoDB por crear
    97
    Propósito: Gestionar alquiler de local para eventos
    98
  • Identificar tipo de evento (cumpleaños, corporativo)
  • Recolectar: Fecha, cantidad de invitados, servicios extra
  • Explicar ventajas del local
  • Coordinar con humano para presupuesto formal
  • 99
    Paso 10: Procesamiento de Pedidos Confirmados
    100
    10.1 Extracción de Datos del Agente
    101
    Nodo: Code in JavaScript
    102
    Procesa la salida del Agente Pedidos para extraer el JSON estructurado:
    103
    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
    }}];
    
    104
    Validación clave:
    105
  • ✅ Cliente debe decir explícitamente “confirmo” o “confirma”
  • ✅ JSON debe contener estructura válida
  • ✅ Total debe ser > 0
  • 106
    10.2 Validación de Venta Final
    107
    Nodo: ¿Es Venta Final? (If)
    108
    // Condición
    $json.totalfinal > 0
    
    109
  • totalfinal = 0: Envía mensaje conversacional sin guardar en DB
  • totalfinal > 0: Procede a guardar en PostgreSQL
  • 110
    10.3 Determinar Operación (Insert o Update)
    111
    Nodo: ¿Es pedido nuevo o existente? (If)
    112
    // Condición
    $('Validar si tiene pedido o no').first().json.id !== empty
    
    113
  • Tiene pedido existente:Modificar Pedido (UPDATE)
  • No tiene pedido:Insertar Pedido (INSERT)
  • 114
    10.4 Modificar Pedido Existente
    115
    Nodo: Modificar Pedido (PostgreSQL)
    116
    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;
    
    117
    Manejo de error:
    118
  • Éxito:Enviar mensaje FInal Modificado (WhatsApp)
  • Error:Notificar Error DB - Modificar (WhatsApp con código de error)
  • 119
    10.5 Insertar Pedido Nuevo
    120
    Nodo: Insertar Pedido (PostgreSQL) — Mencionado en spec original, no visible en JSON
    121
    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;
    
    122
    Manejo de error:
    123
  • Éxito:Enviar mensaje FInal nuevo Pedido (WhatsApp)
  • Error:Notificar Error DB - Insertar (WhatsApp con código de error)
  • 124
    Paso 11: Notificaciones Finales por WhatsApp
    125
    Mensaje de Pedido Confirmado (Nuevo)
    126
    Nodo: Enviar mensaje FInal nuevo Pedido (WhatsApp)
    127
    ¡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 🦐
    
    128
    Mensaje de Pedido Modificado
    129
    Nodo: Enviar mensaje FInal Modificado (WhatsApp)
    130
    ¡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 🦐
    
    131
    Mensajes de Error
    132
    Formato de referencia:
    133
    😔 Lo sentimos, hubo un problema al procesar tu pedido.
    
    Por favor, intenta nuevamente en unos minutos o contáctanos directamente.
    
    Referencia del error: MOD-20260305143022
    

    Diagrama de Flujo Simplificado

    Arquitectura de Datos

    PostgreSQL (Transaccional)

    Credencial: Postgres Lurwis db (ID: 5nRuBkRnPEOnj0KP) Tablas principales:
    CREATE TABLE pedidos_picanteria (
      id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      telefono VARCHAR(20) NOT NULL,
      cliente_nombre VARCHAR(255),
      detalle_pedido JSONB NOT NULL,
      total_final DECIMAL(10,2) NOT NULL,
      metodo_pago VARCHAR(50),
      tipo_servicio VARCHAR(20), -- 'Delivery' o 'Recojo'
      estado_pedido VARCHAR(50) NOT NULL, -- 'confirmado', 'entregado', 'cancelado'
      fecha_creacion TIMESTAMP DEFAULT NOW()
    );
    
    CREATE INDEX idx_telefono ON pedidos_picanteria(telefono);
    CREATE INDEX idx_estado ON pedidos_picanteria(estado_pedido);
    
    CREATE TABLE categorias (
      id SERIAL PRIMARY KEY,
      nombre VARCHAR(100) NOT NULL,
      activo BOOLEAN DEFAULT true
    );
    
    CREATE TABLE platos (
      id SERIAL PRIMARY KEY,
      categoria_id INT REFERENCES categorias(id),
      nombre VARCHAR(200) NOT NULL,
      descripcion TEXT,
      activo BOOLEAN DEFAULT true
    );
    
    CREATE TABLE plato_precios (
      id SERIAL PRIMARY KEY,
      plato_id INT REFERENCES platos(id),
      tamanio VARCHAR(50), -- 'Personal', 'Familiar', 'Único'
      precio DECIMAL(10,2) NOT NULL,
      activo BOOLEAN DEFAULT true
    );
    

    MongoDB (Memoria Conversacional)

    Credencial: Memoria chats Lurwis (ID: rEzqy3CH5hFmOQ2S) Base de datos: picanteria_db Collections por agente:
    CollectionAgenteContext WindowEstado
    historial_clasificadorAgente ClasificadorDefault✅ Producción
    historial_detectorDetector de pedidosDefault✅ Producción
    historial_pedidosAgente Pedidos25 mensajes✅ Producción
    historial_generalAgente General10 mensajes✅ Producción
    historial_reservasReserva Mesas15 mensajes⚠️ Por crear
    historial_eventosReservas Local15 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

    ComponenteTecnologíaUso
    Schedulern8n Schedule TriggerEjecución cada 10 segundos
    LLM PrincipalGoogle GeminiAgentes de IA (modelos flash/thinking)
    Memoria ConversacionalMongoDB + LangChainContexto persistente por usuario
    Base de Datos TransaccionalPostgreSQLPedidos, menú, precios
    Buffer de MensajesRedisAgrupación temporal
    MensajeríaWhatsApp Business APIEnvío de respuestas
    Orquestadorn8nWorkflow engine

    Credenciales Configuradas

    Seguridad: Todas las credenciales están almacenadas en el sistema de credenciales cifrado de n8n. Nunca expongas estos IDs en el código del cliente.
    ServicioNombreID Interno
    PostgreSQLPostgres Lurwis db5nRuBkRnPEOnj0KP
    MongoDBMemoria chats LurwisrEzqy3CH5hFmOQ2S
    RedisBuffer LurwisLuOIeLYx2ps0SCoP
    Google GeminiModelo LurwisEGUpckskyMrPNfc4
    WhatsApp APIWhatsApp accountzJi8oUdszoG5oEqW

    Manejo de Errores

    Estrategias por Tipo de Error

    1
    Redis Vacío
    2
    Si no hay claves ts_*, el flujo termina sin error (ejecución vacía).
    3
    Buffer sin Contenido
    4
    El nodo Extractormensajes retorna array vacío si mensajesUnidos está vacío, deteniendo el flujo limpiamente.
    5
    Error de Agente de IA
    6
    Si un agente falla:
    7
  • Detector de pedidos: retryOnFail: false, waitBetweenTries: 5000
  • No propaga error, devuelve salida vacía
  • 8
    Error de PostgreSQL
    9
    Operaciones de INSERT/UPDATE tienen manejo de error:
    10
  • onError: continueErrorOutput
  • Ruta de error: Envía mensaje de disculpa al cliente por WhatsApp con código de referencia temporal
  • 11
    Error Global del Workflow
    12
    Configurado en settings del workflow:
    13
  • Conectado a “Error Workflow” que notifica al WhatsApp personal del administrador
  • Optimizaciones 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 telefono y estado_pedido

    Context Window por Agente

    AgenteMensajesJustificación
    Pedidos25Conversaciones largas con múltiples platos
    Reservas15Flujo más corto, datos puntuales
    General10Preguntas simples, pocas iteraciones
    Clasificador/DetectorDefaultSolo necesita último contexto

    Zona Horaria

    Todo el sistema opera en America/Lima (UTC-5).

    Estado del Proyecto

    MVP en Producción:
    • ✅ Agente Pedidos (funcional completo)
    • ✅ Agente General (funcional completo)
    • ⚠️ Agentes de Reservas (estructura creada, lógica pendiente)
    • ⚠️ CRM en desarrollo
    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

    1
    Completar Agentes de Reservas
    2
  • Implementar lógica de validación de disponibilidad
  • Crear collections de MongoDB
  • Conectar con sistema de calendario
  • 3
    Implementar Logs de Auditoría
    4
  • Guardar historial completo en logs_auditoria
  • Implementar consultas de métricas
  • 5
    Desarrollar CRM
    6
  • Dashboard de pedidos en tiempo real
  • Gestión de estado de pedidos
  • Reportes de ventas
  • 7
    Mejorar Seguridad
    8
  • Implementar validación de firma HMAC en Receptor
  • Encriptar datos sensibles en PostgreSQL
  • Rotar credenciales de API periódicamente
  • Recursos Relacionados