RANDOM - Seleção Aleatória
O que é este Node?
O RANDOM é o node responsável por escolher aleatoriamente uma opção de uma lista com suporte a pesos para probabilidades customizadas.
Por que este Node existe?
Conversas e automações precisam de variabilidade e decisões probabilísticas. O RANDOM existe para:
- Variabilidade: Evitar respostas repetitivas e previsíveis
- A/B Testing: Distribuir usuários entre diferentes variações de flows
- Distribuição de carga: Rotacionar entre múltiplos recursos (APIs, agentes, etc.)
- Gamificação: Criar elementos de surpresa e aleatoriedade
- Seleção ponderada: Controlar probabilidade de cada opção com pesos
Como funciona internamente?
Quando o RANDOM é executado, o sistema:
- Valida opções: Verifica se há pelo menos uma opção disponível
- Verifica pesos: Se fornecidos, valida se há peso para cada opção
- Calcula seleção:
- Com pesos: Usa seleção aleatória ponderada (weighted random)
- Sem pesos: Cada opção tem chance igual (uniform random)
- Escolhe opção: Seleciona uma das opções baseado no algoritmo
- Retorna resultado: Devolve a opção escolhida para uso no flow
Código interno (logic-control-executor.service.ts:302-329):
private async executeRandom(parameters: any, context: any): Promise<any> {
const { options, weights } = parameters;
this.logger.log(`🎲 RANDOM - Choosing from ${options?.length || 0} options`);
if (!options || options.length === 0) {
throw new Error('No options provided for random selection');
}
let selectedOption;
if (weights && weights.length === options.length) {
// Weighted random selection
selectedOption = this.weightedRandomChoice(options, weights);
} else {
// Simple random selection
selectedOption = options[Math.floor(Math.random() * options.length)];
}
return {
success: true,
action: 'random_choice_made',
selectedOption: selectedOption,
allOptions: options,
weights: weights || null,
timestamp: new Date().toISOString()
};
}
Código do algoritmo de seleção ponderada (logic-control-executor.service.ts:381-393):
private weightedRandomChoice(options: any[], weights: number[]): any {
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
let random = Math.random() * totalWeight;
for (let i = 0; i < options.length; i++) {
random -= weights[i];
if (random <= 0) {
return options[i];
}
}
return options[options.length - 1]; // Fallback
}
Quando você DEVE usar este Node?
Use RANDOM quando precisar de seleção aleatória ou probabilística:
Casos de uso:
- Variar mensagens: Escolher entre múltiplas saudações
- A/B Testing: Distribuir usuários em grupos de teste
- Distribuir atendimento: Rotacionar entre atendentes disponíveis
- Gamificação: Sortear prêmios ou desafios
- Load balancing: Distribuir entre múltiplos servidores/APIs
- Personalização: Variar conteúdo para evitar monotonia
Quando NÃO usar RANDOM:
- Decisões baseadas em dados: Use CONDITION ou SWITCH
- Múltiplos critérios: Use SWITCH com cases específicos
- Lógica determinística: Se precisa do mesmo resultado sempre, use lógica condicional
Parâmetros Detalhados
options (array, obrigatório)
O que é: Array com as opções disponíveis para seleção. Pode conter strings, números ou objetos.
Flow completo para testar:
{
"name": "Teste RANDOM - Options Simples",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "random_1",
"type": "random",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Escolher Cor",
"parameters": {
"options": ["vermelho", "azul", "verde", "amarelo"]
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Salvar Escolha",
"parameters": {
"name": "cor_escolhida",
"value": "{{selectedOption}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Mostrar Resultado",
"parameters": {
"message": "🎨 A cor sorteada foi: {{cor_escolhida}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "random_1" },
{ "source": "random_1", "target": "variable_1" },
{ "source": "variable_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Execute o flow múltiplas vezes. Cada execução escolherá uma cor diferente aleatoriamente!
weights (array de números, opcional)
O que é: Array de números que define o peso (probabilidade) de cada opção. Deve ter o mesmo tamanho do array options.
Padrão: Todos os pesos iguais (distribuição uniforme)
Como funciona: - Peso 1 = probabilidade normal - Peso 2 = duas vezes mais chance - Peso 10 = dez vezes mais chance - Peso 0 = nunca será escolhido
Flow completo para testar:
{
"name": "Teste RANDOM - Weights (Ponderado)",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "random_1",
"type": "random",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Sortear Prêmio",
"parameters": {
"options": ["Comum", "Raro", "Épico", "Lendário"],
"weights": [70, 20, 8, 2]
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Salvar Prêmio",
"parameters": {
"name": "premio",
"value": "{{selectedOption}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Anunciar Prêmio",
"parameters": {
"message": "🎁 Você ganhou um prêmio {{premio}}!"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "random_1" },
{ "source": "random_1", "target": "variable_1" },
{ "source": "variable_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Execute várias vezes. "Comum" aparecerá 70% das vezes, "Raro" 20%, "Épico" 8%, "Lendário" 2%!
Cálculo de probabilidades:
Total de pesos = 70 + 20 + 8 + 2 = 100
Comum: 70/100 = 70% de chance
Raro: 20/100 = 20% de chance
Épico: 8/100 = 8% de chance
Lendário: 2/100 = 2% de chance
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| options | array | Sim | Array com as opções disponíveis para escolha |
| weights | array de números | Não | Pesos para cada opção (mesmo tamanho que options) |
Exemplo 1: Variar Saudações
Objetivo: Evitar mensagens repetitivas variando a saudação inicial
JSON para Importar
{
"name": "RANDOM - Saudações Variadas",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "random_1",
"type": "random",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Escolher Saudação",
"parameters": {
"options": [
"Olá! Como posso ajudar você hoje?",
"Oi! Em que posso ser útil?",
"Seja bem-vindo! Como posso auxiliar?",
"Olá! É um prazer atender você!",
"Oi! Estou aqui para ajudar!"
]
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Salvar Saudação",
"parameters": {
"name": "saudacao",
"value": "{{selectedOption}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Enviar Saudação",
"parameters": {
"message": "{{saudacao}}"
}
}
},
{
"id": "input_1",
"type": "input",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Receber Resposta",
"parameters": {
"message": "Descreva sua necessidade:",
"variable": "necessidade"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Confirmar",
"parameters": {
"message": "Entendi! Vou processar: {{necessidade}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1300, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "random_1" },
{ "source": "random_1", "target": "variable_1" },
{ "source": "variable_1", "target": "message_1" },
{ "source": "message_1", "target": "input_1" },
{ "source": "input_1", "target": "message_2" },
{ "source": "message_2", "target": "end_1" }
]
}
Saída esperada:
Sistema: [Uma das 5 saudações aleatoriamente]
Sistema: Descreva sua necessidade:
Usuário: Preciso de ajuda com pedido
Sistema: Entendi! Vou processar: Preciso de ajuda com pedido
Exemplo 2: A/B Testing com 3 Variações
Objetivo: Distribuir usuários em 3 grupos de teste para testar diferentes abordagens
JSON para Importar
{
"name": "RANDOM - A/B/C Testing",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "random_1",
"type": "random",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Distribuir Grupo",
"parameters": {
"options": ["Grupo_A", "Grupo_B", "Grupo_C"]
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Salvar Grupo",
"parameters": {
"name": "grupo_teste",
"value": "{{selectedOption}}"
}
}
},
{
"id": "switch_1",
"type": "switch",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Rotear por Grupo",
"parameters": {
"variable": "{{grupo_teste}}",
"cases": [
{
"value": "Grupo_A",
"output": "grupo_a"
},
{
"value": "Grupo_B",
"output": "grupo_b"
},
{
"value": "Grupo_C",
"output": "grupo_c"
}
]
}
}
},
{
"id": "message_a",
"type": "message",
"position": { "x": 900, "y": 0 },
"data": {
"label": "Abordagem A",
"parameters": {
"message": "🔵 GRUPO A: Bem-vindo! Você está no grupo de teste A - Abordagem Formal."
}
}
},
{
"id": "message_b",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Abordagem B",
"parameters": {
"message": "🟢 GRUPO B: E aí! Você tá no grupo B - Abordagem Casual."
}
}
},
{
"id": "message_c",
"type": "message",
"position": { "x": 900, "y": 200 },
"data": {
"label": "Abordagem C",
"parameters": {
"message": "🟡 GRUPO C: Hey! You're in group C - English Version."
}
}
},
{
"id": "merge_1",
"type": "merge",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Reunir Fluxos"
}
},
{
"id": "message_final",
"type": "message",
"position": { "x": 1300, "y": 100 },
"data": {
"label": "Continuar",
"parameters": {
"message": "Você foi atribuído ao: {{grupo_teste}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1500, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "random_1" },
{ "source": "random_1", "target": "variable_1" },
{ "source": "variable_1", "target": "switch_1" },
{ "source": "switch_1", "target": "message_a", "sourceHandle": "grupo_a" },
{ "source": "switch_1", "target": "message_b", "sourceHandle": "grupo_b" },
{ "source": "switch_1", "target": "message_c", "sourceHandle": "grupo_c" },
{ "source": "message_a", "target": "merge_1" },
{ "source": "message_b", "target": "merge_1" },
{ "source": "message_c", "target": "merge_1" },
{ "source": "merge_1", "target": "message_final" },
{ "source": "message_final", "target": "end_1" }
]
}
Saída esperada:
Sistema: [Mensagem do Grupo A, B ou C aleatoriamente - 33.3% cada]
Sistema: Você foi atribuído ao: Grupo_A (ou B ou C)
Exemplo 3: Sorteio de Prêmios com Pesos (Gamificação)
Objetivo: Sistema de sorteio com raridades diferentes (comum mais frequente, lendário raro)
JSON para Importar
{
"name": "RANDOM - Sorteio com Raridade",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "message_1",
"type": "message",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Introdução",
"parameters": {
"message": "🎰 Bem-vindo ao sorteio diário!\n\nGirando a roleta..."
}
}
},
{
"id": "delay_1",
"type": "delay",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Suspense",
"parameters": {
"duration": 2,
"unit": "seconds"
}
}
},
{
"id": "random_1",
"type": "random",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Sortear Prêmio",
"parameters": {
"options": [
"🥉 Bronze (10 pontos)",
"🥈 Prata (25 pontos)",
"🥇 Ouro (50 pontos)",
"💎 Diamante (100 pontos)",
"👑 Lendário (500 pontos)"
],
"weights": [50, 30, 15, 4, 1]
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Salvar Resultado",
"parameters": {
"name": "premio_sorteado",
"value": "{{selectedOption}}"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Anunciar Prêmio",
"parameters": {
"message": "🎉 PARABÉNS!\n\nVocê ganhou: {{premio_sorteado}}\n\n✨ Os pontos foram adicionados à sua conta!"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1300, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "message_1" },
{ "source": "message_1", "target": "delay_1" },
{ "source": "delay_1", "target": "random_1" },
{ "source": "random_1", "target": "variable_1" },
{ "source": "variable_1", "target": "message_2" },
{ "source": "message_2", "target": "end_1" }
]
}
Saída esperada:
Sistema: 🎰 Bem-vindo ao sorteio diário!
Girando a roleta...
[2 segundos de espera]
Sistema: 🎉 PARABÉNS!
Você ganhou: 🥉 Bronze (10 pontos)
✨ Os pontos foram adicionados à sua conta!
Probabilidades: - Bronze: 50% (metade das vezes) - Prata: 30% (quase 1/3 das vezes) - Ouro: 15% (raro) - Diamante: 4% (muito raro) - Lendário: 1% (extremamente raro)
Exemplo 4: Distribuir entre Atendentes
Objetivo: Distribuir atendimentos entre múltiplos atendentes com pesos baseados em disponibilidade
JSON para Importar
{
"name": "RANDOM - Distribuição de Atendentes",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "message_1",
"type": "message",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Receber Contato",
"parameters": {
"message": "👋 Olá! Estou conectando você com um atendente..."
}
}
},
{
"id": "random_1",
"type": "random",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Escolher Atendente",
"parameters": {
"options": ["Maria", "João", "Ana", "Carlos"],
"weights": [40, 30, 20, 10]
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Salvar Atendente",
"parameters": {
"name": "atendente",
"value": "{{selectedOption}}"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Apresentar Atendente",
"parameters": {
"message": "✅ Você foi conectado com {{atendente}}!\n\n{{atendente}} irá atender você agora. Como podemos ajudar?"
}
}
},
{
"id": "input_1",
"type": "input",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Receber Mensagem",
"parameters": {
"message": "Digite sua mensagem:",
"variable": "mensagem_usuario"
}
}
},
{
"id": "message_3",
"type": "message",
"position": { "x": 1300, "y": 100 },
"data": {
"label": "Confirmar Envio",
"parameters": {
"message": "📨 Mensagem enviada para {{atendente}}. Aguarde resposta..."
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1500, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "message_1" },
{ "source": "message_1", "target": "random_1" },
{ "source": "random_1", "target": "variable_1" },
{ "source": "variable_1", "target": "message_2" },
{ "source": "message_2", "target": "input_1" },
{ "source": "input_1", "target": "message_3" },
{ "source": "message_3", "target": "end_1" }
]
}
Saída esperada:
Sistema: 👋 Olá! Estou conectando você com um atendente...
Sistema: ✅ Você foi conectado com Maria!
Maria irá atender você agora. Como podemos ajudar?
Sistema: Digite sua mensagem:
Usuário: Preciso de ajuda com meu pedido
Sistema: 📨 Mensagem enviada para Maria. Aguarde resposta...
Nota: Maria recebe 40% dos atendimentos (mais experiente), Carlos apenas 10% (trainee).
Resposta do Node
Seleção simples (sem pesos):
{
"success": true,
"action": "random_choice_made",
"selectedOption": "azul",
"allOptions": ["vermelho", "azul", "verde", "amarelo"],
"weights": null,
"timestamp": "2025-01-15T10:30:00.000Z"
}
Seleção ponderada (com pesos):
{
"success": true,
"action": "random_choice_made",
"selectedOption": "Comum",
"allOptions": ["Comum", "Raro", "Épico", "Lendário"],
"weights": [70, 20, 8, 2],
"timestamp": "2025-01-15T10:30:00.000Z"
}
Entendendo Pesos (Weights)
Conceito Básico
Os pesos funcionam como uma "roleta" onde cada opção ocupa um espaço proporcional ao seu peso:
Pesos: [50, 30, 15, 4, 1]
Total: 100
Roleta:
[==================================================] Bronze (50%)
[==============================] Prata (30%)
[===============] Ouro (15%)
[====] Diamante (4%)
[=] Lendário (1%)
Exemplos de Distribuição
| Pesos | Resultado |
|---|---|
| [1, 1, 1] | 33.3% cada (igual) |
| [2, 1, 1] | 50%, 25%, 25% |
| [3, 2, 1] | 50%, 33%, 17% |
| [100, 1] | 99%, 1% (muito desbalanceado) |
Calculando Probabilidade
Probabilidade = Peso da Opção / Soma Total dos Pesos
Exemplo: [70, 20, 8, 2]
Total = 70 + 20 + 8 + 2 = 100
Opção 1: 70/100 = 70%
Opção 2: 20/100 = 20%
Opção 3: 8/100 = 8%
Opção 4: 2/100 = 2%
Casos de Uso Reais
| Caso | Options | Weights | Uso |
|---|---|---|---|
| A/B Test 50/50 | [A, B] | [1, 1] | Teste igualitário |
| A/B Test 80/20 | [A, B] | [80, 20] | Versão A prioritária |
| Atendentes | [Maria, João, Pedro] | [50, 30, 20] | Por experiência |
| Gamificação | [Comum, Raro, Épico] | [70, 25, 5] | Sistema de raridade |
| Mensagens | [Msg1, Msg2, Msg3] | null | Variação igual |
Boas Práticas
✅ SIM: - Use RANDOM para criar variabilidade e evitar monotonia - Combine com SWITCH para rotear baseado na escolha - Use weights para controlar distribuição (A/B testing com 80/20) - Salve o resultado em VARIABLE para usar depois - Use para distribuir carga entre múltiplos recursos
❌ NÃO: - Não use RANDOM para decisões que precisam de lógica (use CONDITION) - Não confie em RANDOM para dados críticos (não é criptograficamente seguro) - Não esqueça de salvar selectedOption em variável se precisar usar depois - Não use weights desbalanceados demais ([9999, 1]) a menos que intencional
Dicas
💡 A/B Testing balanceado: Use weights [1, 1] ou omita weights para distribuição 50/50 perfeita
💡 A/B Testing desbalanceado: Use [80, 20] quando quiser testar nova versão com cautela (80% versão antiga, 20% nova)
💡 Gamificação realista: Use progressão exponencial nos pesos: [64, 32, 16, 8, 4, 2, 1] para raridades
💡 Acessar resultado: O selectedOption retorna a opção escolhida - salve em VARIABLE para usar em nodes seguintes
💡 Combinar com SWITCH: Use RANDOM + VARIABLE + SWITCH para criar rotas diferentes baseadas na escolha aleatória
💡 Load balancing: Use weights para distribuir baseado em capacidade (servidor forte = peso maior)
Diferenças entre RANDOM e outros Nodes
| Node | Quando Usar |
|---|---|
| RANDOM | Escolha aleatória/probabilística |
| CONDITION | Decisão baseada em condição (if/else) |
| SWITCH | Múltiplos caminhos baseados em valor exato |
| LOOP | Repetir ações múltiplas vezes |
Próximos Nodes
→ CONDITION - Decisões baseadas em condições → SWITCH - Múltiplos caminhos baseados em valor → VARIABLE - Salvar e gerenciar variáveis → MERGE - Reunir múltiplos fluxos