- README principal con descripción del proyecto - Documento de diseño completo (arquitectura, DB, flujos) - Documentación de API REST y WebSocket - Guía del Flow Builder (30+ tipos de nodos) - Documentación de integración con Odoo - Guía de despliegue con Docker - Esquema de base de datos - Estructura de carpetas del proyecto - Archivo .env.example con todas las variables Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
787 lines
15 KiB
Markdown
787 lines
15 KiB
Markdown
# 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
|