# Flow Builder - Guía Completa ## Introducción El Flow Builder es el editor visual para crear chatbots sin código. Permite diseñar flujos de conversación arrastrando y conectando nodos. ## Interfaz ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Flujo: Bienvenida [Guardar] [Probar] [Activar] │ ├───────────┬─────────────────────────────────────────────────────────┤ │ NODOS │ CANVAS │ │ ───────── │ │ │ 📨 Mensaje│ [Nodos conectados con líneas] │ │ 🔘 Botones│ │ │ 📋 Lista │ │ │ ❓ Input │ │ │ ⑂ Condición │ │ ... │ │ ├───────────┼─────────────────────────────────────────────────────────┤ │ │ PANEL DE PROPIEDADES (al seleccionar un nodo) │ └───────────┴─────────────────────────────────────────────────────────┘ ``` --- ## Tipos de Nodos ### Nodos de Trigger (Inicio) #### welcome Inicia el flujo cuando un contacto escribe por primera vez. ```json { "type": "welcome", "id": "trigger_1", "data": { "label": "Primer mensaje" } } ``` #### keyword Inicia el flujo cuando el mensaje contiene palabras clave. ```json { "type": "keyword", "id": "trigger_2", "data": { "keywords": ["precio", "costo", "cuanto"], "match_type": "contains" } } ``` **Match types:** - `exact` - Coincidencia exacta - `contains` - Contiene la palabra - `starts_with` - Empieza con - `regex` - Expresión regular #### fallback Se activa cuando ningún otro flujo coincide. ```json { "type": "fallback", "id": "trigger_3", "data": { "label": "No entendido" } } ``` #### event Se activa por eventos externos (webhook, Odoo). ```json { "type": "event", "id": "trigger_4", "data": { "event_type": "odoo_order_confirmed", "filters": { "amount_total": { "gt": 1000 } } } } ``` --- ### Nodos de Mensaje #### text Envía un mensaje de texto. ```json { "type": "text", "id": "msg_1", "data": { "content": "¡Hola {{contact.name}}! 👋\n\n¿En qué te puedo ayudar?", "typing_delay": 1500 } } ``` **Variables disponibles:** - `{{contact.name}}` - Nombre del contacto - `{{contact.phone}}` - Teléfono - `{{agent.name}}` - Nombre del agente - `{{variables.xxx}}` - Variables capturadas #### image Envía una imagen. ```json { "type": "image", "id": "msg_2", "data": { "url": "https://example.com/producto.jpg", "caption": "Nuestro producto estrella" } } ``` #### video Envía un video. ```json { "type": "video", "id": "msg_3", "data": { "url": "https://example.com/demo.mp4", "caption": "Video demostrativo" } } ``` #### document Envía un documento (PDF, Excel, etc). ```json { "type": "document", "id": "msg_4", "data": { "url": "https://example.com/catalogo.pdf", "filename": "Catálogo 2024.pdf" } } ``` #### location Envía una ubicación. ```json { "type": "location", "id": "msg_5", "data": { "latitude": 19.4326, "longitude": -99.1332, "name": "Nuestra Oficina", "address": "Av. Reforma 123, CDMX" } } ``` #### buttons Envía mensaje con botones (máximo 3). ```json { "type": "buttons", "id": "msg_6", "data": { "content": "¿Qué necesitas?", "buttons": [ { "id": "ventas", "text": "Ventas" }, { "id": "soporte", "text": "Soporte" }, { "id": "otro", "text": "Otro" } ] } } ``` #### list Envía mensaje con lista de opciones (máximo 10). ```json { "type": "list", "id": "msg_7", "data": { "content": "Selecciona una categoría:", "button_text": "Ver opciones", "sections": [ { "title": "Productos", "rows": [ { "id": "laptops", "title": "Laptops", "description": "Computadoras portátiles" }, { "id": "phones", "title": "Teléfonos", "description": "Smartphones" } ] }, { "title": "Servicios", "rows": [ { "id": "soporte", "title": "Soporte Técnico" }, { "id": "garantia", "title": "Garantías" } ] } ] } } ``` #### template Usa un mensaje guardado como template. ```json { "type": "template", "id": "msg_8", "data": { "template_id": "uuid-del-template", "variables": { "producto": "{{variables.producto_seleccionado}}" } } } ``` --- ### Nodos de Lógica #### condition Bifurca según condiciones. ```json { "type": "condition", "id": "cond_1", "data": { "conditions": [ { "groups": [ { "field": "{{message.text}}", "operator": "contains", "value": "precio" } ], "logic": "AND" } ], "outputs": ["true", "false"] } } ``` **Operadores:** - Texto: `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `regex` - Número: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `between` - Lista: `in`, `not_in`, `is_empty`, `is_not_empty` - General: `exists`, `not_exists`, `is_null`, `is_not_null` #### switch Múltiples salidas según valor. ```json { "type": "switch", "id": "switch_1", "data": { "field": "{{variables.departamento}}", "cases": [ { "value": "ventas", "output": "ventas" }, { "value": "soporte", "output": "soporte" }, { "value": "cobranza", "output": "cobranza" } ], "default_output": "default" } } ``` #### wait_input Espera respuesta del usuario y la guarda. ```json { "type": "wait_input", "id": "input_1", "data": { "variable": "nombre_cliente", "timeout_seconds": 300, "timeout_output": "timeout", "validation": { "type": "text", "min_length": 2, "max_length": 100 }, "error_message": "Por favor ingresa un nombre válido." } } ``` #### delay Pausa antes de continuar. ```json { "type": "delay", "id": "delay_1", "data": { "seconds": 3, "random_range": [2, 5] } } ``` #### set_variable Asigna o modifica una variable. ```json { "type": "set_variable", "id": "set_1", "data": { "variable": "contador", "operation": "increment", "value": 1 } } ``` **Operaciones:** - `set` - Asignar valor - `increment` - Incrementar - `decrement` - Decrementar - `append` - Agregar a lista - `concat` - Concatenar texto #### random A/B Testing - bifurca aleatoriamente. ```json { "type": "random", "id": "ab_1", "data": { "test_name": "Test saludo", "variants": [ { "name": "A", "weight": 50, "output": "variant_a" }, { "name": "B", "weight": 50, "output": "variant_b" } ], "track_metric": "response_rate" } } ``` #### loop Repetir bloque. ```json { "type": "loop", "id": "loop_1", "data": { "max_iterations": 3, "condition": { "field": "{{variables.confirmado}}", "operator": "not_equals", "value": "si" }, "outputs": ["continue", "exit"] } } ``` #### go_to Salta a otro flujo o nodo. ```json { "type": "go_to", "id": "goto_1", "data": { "target_type": "flow", "target_id": "uuid-del-flujo", "pass_context": true } } ``` #### sub_flow Ejecuta un sub-flujo y retorna. ```json { "type": "sub_flow", "id": "sub_1", "data": { "flow_id": "uuid-del-subflujo", "input_variables": { "producto_id": "{{variables.producto}}" }, "output_variable": "resultado_subflujo" } } ``` #### javascript Ejecuta código JavaScript personalizado. ```json { "type": "javascript", "id": "js_1", "data": { "code": "const total = variables.precio * variables.cantidad;\nreturn { total, descuento: total > 1000 ? 0.1 : 0 };", "output_variable": "calculo" } } ``` --- ### Nodos de Validación #### validate_email Valida formato de email. ```json { "type": "validate_email", "id": "val_1", "data": { "input": "{{message.text}}", "output_variable": "email_valido", "outputs": ["valid", "invalid"] } } ``` #### validate_phone Valida formato de teléfono. ```json { "type": "validate_phone", "id": "val_2", "data": { "input": "{{message.text}}", "country": "MX", "output_variable": "telefono_valido", "outputs": ["valid", "invalid"] } } ``` #### validate_number Valida número en rango. ```json { "type": "validate_number", "id": "val_3", "data": { "input": "{{message.text}}", "min": 1, "max": 100, "output_variable": "cantidad", "outputs": ["valid", "invalid"] } } ``` #### validate_date Valida formato de fecha. ```json { "type": "validate_date", "id": "val_4", "data": { "input": "{{message.text}}", "format": "DD/MM/YYYY", "min_date": "today", "max_date": "+30days", "output_variable": "fecha", "outputs": ["valid", "invalid"] } } ``` #### validate_options Valida contra lista de opciones. ```json { "type": "validate_options", "id": "val_5", "data": { "input": "{{message.text}}", "options": ["1", "2", "3", "uno", "dos", "tres"], "case_insensitive": true, "output_variable": "opcion", "outputs": ["valid", "invalid"] } } ``` #### retry_input Reintenta captura con mensaje de error. ```json { "type": "retry_input", "id": "retry_1", "data": { "max_retries": 3, "error_message": "No entendí tu respuesta. Por favor intenta de nuevo.", "final_fallback": "transfer", "outputs": ["continue", "max_retries"] } } ``` --- ### Nodos de Acción #### transfer Transfiere a agente humano. ```json { "type": "transfer", "id": "act_1", "data": { "target_type": "queue", "target_id": "uuid-de-cola", "priority": "high", "message": "Te comunico con un agente. Por favor espera." } } ``` #### close Cierra la conversación. ```json { "type": "close", "id": "act_2", "data": { "resolution": "resolved", "send_csat": true, "message": "¡Gracias por contactarnos! ¿Hay algo más en que pueda ayudarte?" } } ``` #### tag Agrega o quita etiquetas. ```json { "type": "tag", "id": "act_3", "data": { "add": ["lead", "interesado"], "remove": ["nuevo"] } } ``` #### note Agrega nota interna. ```json { "type": "note", "id": "act_4", "data": { "content": "Cliente interesado en {{variables.producto}}. Presupuesto: {{variables.presupuesto}}" } } ``` #### notify Envía notificación. ```json { "type": "notify", "id": "act_5", "data": { "type": "email", "recipients": ["admin@empresa.com"], "subject": "Nuevo lead VIP", "message": "El cliente {{contact.name}} está interesado en {{variables.producto}}" } } ``` #### webhook Llama API externa. ```json { "type": "webhook", "id": "act_6", "data": { "url": "https://api.example.com/leads", "method": "POST", "headers": { "Authorization": "Bearer {{secrets.api_key}}" }, "body": { "name": "{{contact.name}}", "phone": "{{contact.phone}}", "product": "{{variables.producto}}" }, "output_variable": "api_response", "retry": { "max_attempts": 3, "delay_seconds": 5 } } } ``` #### ai_response Genera respuesta con IA. ```json { "type": "ai_response", "id": "act_7", "data": { "provider": "openai", "model": "gpt-4", "system_prompt": "Eres un asistente de ventas amable. Productos: {{odoo.products}}", "include_context": { "last_messages": 10, "contact_info": true, "odoo_data": true }, "allowed_actions": ["transfer", "create_lead"], "output_variable": "ai_response", "fallback_on_error": "transfer" } } ``` --- ### Nodos de Odoo Ver [Integración Odoo](../odoo-integration/README.md) para documentación completa. --- ## Variables del Sistema ``` # Contacto {{contact.id}} {{contact.name}} {{contact.phone}} {{contact.email}} {{contact.company}} {{contact.tags}} {{contact.odoo_id}} {{contact.created_at}} {{contact.custom.xxx}} # Conversación {{conversation.id}} {{conversation.status}} {{conversation.assigned_to}} {{conversation.queue}} {{conversation.started_at}} # Mensaje actual {{message.id}} {{message.text}} {{message.type}} {{message.media_url}} # Sistema {{system.date}} {{system.time}} {{system.datetime}} {{system.day_of_week}} {{system.business_hours}} {{system.timestamp}} # Agente (si asignado) {{agent.id}} {{agent.name}} {{agent.email}} # Odoo (si conectado) {{odoo.partner.id}} {{odoo.partner.name}} {{odoo.partner.email}} {{odoo.partner.balance}} {{odoo.last_order.name}} {{odoo.last_order.total}} {{odoo.last_order.state}} # Variables capturadas {{variables.xxx}} ``` --- ## Estructura de Flujo (JSON) ```json { "id": "uuid", "name": "Bienvenida", "description": "Flujo de bienvenida", "trigger_type": "welcome", "trigger_value": null, "nodes": { "nodes": [ { "id": "trigger_1", "type": "welcome", "position": { "x": 100, "y": 100 }, "data": { "label": "Inicio" } }, { "id": "msg_1", "type": "text", "position": { "x": 100, "y": 200 }, "data": { "content": "¡Hola!" } } ], "edges": [ { "id": "e1", "source": "trigger_1", "target": "msg_1", "sourceHandle": "default" } ], "viewport": { "x": 0, "y": 0, "zoom": 1 } }, "variables": { "producto": { "type": "string", "default": "" }, "cantidad": { "type": "number", "default": 1 } }, "is_active": true, "version": 1 } ``` --- ## Mejores Prácticas 1. **Nombra los nodos claramente** para fácil identificación 2. **Usa sub-flujos** para lógica repetida (validaciones, etc.) 3. **Siempre incluye fallback** para respuestas no entendidas 4. **Valida inputs** antes de usarlos en acciones 5. **Limita reintentos** para evitar loops infinitos 6. **Usa A/B testing** para optimizar mensajes 7. **Transfiere a humano** cuando el bot no puede resolver 8. **Guarda contexto** para conversaciones que se retoman después