MODULO 5.3

🏛️ Arquitetura Multibot

Os padroes de engenharia por tras de sistemas multibot em producao. Hub-and-spoke, routing por intent, message queues, load balancing e degradacao graceful. Da teoria ao codigo funcional.

6
Topicos
~55min
Duracao
Avancado
Nivel
Teoria + Pratica
Tipo
1

🔀 Hub-and-Spoke: O Padrao Central

O padrao hub-and-spoke e a arquitetura dominante em sistemas multibot. Um ponto central (hub) recebe todas as mensagens e distribui para bots especialistas (spokes). E o mesmo padrao de aeroportos: tudo passa pelo hub, de la vai pra cada destino.

🎯 Conceito Principal

No hub-and-spoke, o hub (router) tem tres responsabilidades: receber a mensagem, classificar a intencao e despachar para o spoke correto. Ele nao processa a mensagem. Ele decide quem processa. Os spokes (bots especialistas) recebem, processam e retornam a resposta ao hub, que devolve ao usuario.

O hub mantem controle total do fluxo. Ele sabe qual bot respondeu, quanto tempo levou, se houve erro. Isso e fundamental para monitoramento, logging e debugging. Sem hub centralizado, voce perde visibilidade do sistema.

  • Entrada unica: Usuario fala com um unico ponto. Nao precisa saber que existem multiplos bots por tras
  • Roteamento transparente: A experiencia do usuario e seamless. Ele nao percebe a troca de bot
  • Observabilidade central: Todas as metricas passam pelo hub: latencia, erros, routing decisions

📐 Arquitetura Hub-and-Spoke

👤
Usuario
🧠
Router Hub
🎧
Suporte
Ollama
💰
Vendas
GPT-4o
✍️
Conteudo
Claude
🔄
Fallback
General

💻 Como o Hub Decide (Pseudocodigo)

// Hub: ponto central de decisao
class RouterHub {
constructor() {
this.spokes = {
suporte: new SupportBot({ model: 'ollama/qwen2.5:14b' }),
vendas: new SalesBot({ model: 'openai/gpt-4o' }),
conteudo: new ContentBot({ model: 'anthropic/claude-sonnet' }),
fallback: new FallbackBot({ model: 'ollama/llama3.2:3b' }),
}
}
async handle(message) {
const start = Date.now()
const { intent, confidence } = await this.classify(message)
const bot = confidence > 0.8
? this.spokes[intent]
: this.spokes.fallback
const response = await bot.respond(message)
// Log para observabilidade
this.log({ intent, confidence, bot: bot.name, latency: Date.now() - start })
return response
}
}

💡 Dica Pratica

O hub deve ser stateless. Ele nao guarda contexto de conversa. Ele classifica, despacha e loga. Se o hub cair e reiniciar, nao perde nada. O estado fica nos bots especialistas ou numa camada de persistencia separada (Redis, SQLite). Isso torna o hub facil de escalar horizontalmente.

2

🧠 Router: Classificacao de Intent

O router e o cerebro do sistema multibot. Ele recebe texto cru do usuario e decide para qual bot especialista enviar. A qualidade do routing determina a qualidade de todo o sistema. Router ruim = usuario falando com o bot errado = experiencia horrivel.

🎯 Conceito Principal

Existem tres abordagens para classificacao de intent, cada uma com seu custo-beneficio:

  • Keyword matching: Mais rapido (~1ms), mais barato (zero custo), mas limitado. Funciona para dominios com vocabularios distintos. Falha com linguagem ambigua
  • LLM-based routing: Mais preciso (90-95%), mais lento (~200-500ms com Ollama, ~500-1500ms com API). Entende nuance, sarcasmo, perguntas indiretas
  • ML classifier (fine-tuned): Melhor equilibrio. Treina um modelo pequeno nos seus dados. Rapido (~10ms), preciso (92-97%), mas precisa de dados de treino
KW

Keyword Matching

// Rapido, simples, limitado
const intents = {
suporte: ['bug', 'erro', 'problema', 'nao funciona', 'ajuda', 'ticket'],
vendas: ['preco', 'plano', 'desconto', 'comprar', 'trial', 'demo'],
conteudo: ['blog', 'post', 'artigo', 'newsletter', 'redes sociais'],
}
function classify(msg) {
const lower = msg.toLowerCase()
for (const [intent, keywords] of Object.entries(intents)) {
if (keywords.some(kw => lower.includes(kw))) return { intent, confidence: 0.9 }
}
return { intent: 'fallback', confidence: 0 }
}
Latencia: ~1ms Custo: $0 Acuracia: ~75-85%
LLM

LLM-Based Routing

// Preciso, entende nuance, mais lento
async function classify(message) {
const prompt = `Classifique a mensagem do usuario em uma das categorias:
- suporte (duvidas tecnicas, bugs, problemas)
- vendas (pricing, planos, compra, upgrade)
- conteudo (criacao, blog, social media)
- geral (saudacoes, conversa, outros)
Responda APENAS com JSON: {"intent": "...", "confidence": 0.X}
Mensagem: "${message}"`
const result = await ollama.chat({
model: 'qwen2.5:3b', // modelo leve para classificacao
messages: [{ role: 'user', content: prompt }],
format: 'json',
})
return JSON.parse(result.message.content)
}
Latencia: ~200-500ms Custo: $0 (local) Acuracia: ~90-95%
ML

ML Classifier (Fine-tuned)

Treina um modelo leve (DistilBERT, sentence-transformers) com dados reais do seu produto. Precisa de ~500 mensagens rotuladas por categoria. Rapido como keyword, preciso como LLM.

Quando usar: Volume alto (1000+ msgs/dia), categorias estaveis, latencia critica (<10ms).
Latencia: ~5-10ms Custo: $0 (inference) Acuracia: ~92-97%

📊 Thresholds de Confianca

O router nao deve enviar mensagens para especialistas quando nao tem certeza. Defina thresholds:

0.85+
Envia direto
Pro especialista com confianca
0.60-0.84
Envia com fallback
Tenta especialista, se falhar vai pro geral
<0.60
Fallback direto
Nao arrisca, manda pro bot geral

💡 Dica Pratica

Comece com keyword matching + LLM fallback. Se a keyword acerta, usa ela (rapido e gratis). Se nao acha keyword, passa pro LLM classificar. Isso te da velocidade na maioria dos casos e precisao nos casos ambiguos. E a estrategia que o OpenClaw usa no router.ts.

3

📨 Message Queue e Async Processing

Nem toda mensagem precisa de resposta instantanea. Quando bots fazem tarefas longas (pesquisa, geracao de relatorio, analise de dados), voce precisa de processamento assincrono. Message queues sao a cola que mantem o sistema funcionando sem bloquear.

🎯 Conceito Principal

Uma message queue e uma fila onde mensagens esperam para ser processadas. O router coloca a mensagem na fila, e o bot especialista pega quando estiver disponivel. Isso desacopla quem envia de quem processa. Se o bot de conteudo esta ocupado gerando um artigo de 3000 palavras, as proximas mensagens ficam na fila em vez de dar timeout.

Opcoes populares em Node.js: BullMQ (filas com Redis, robusto), Redis Pub/Sub (broadcast simples), Database Polling (mais simples, sem dependencia extra). Para projetos pequenos, database polling com SQLite funciona perfeitamente.

📊 Comparativo de Message Queues

Tecnologia Setup Escala Persistencia Ideal para
BullMQ + Redis Medio Alta Sim Producao seria
Redis Pub/Sub Facil Alta Nao Broadcast real-time
SQLite Polling Facil Baixa Sim MVP, bot pessoal
RabbitMQ Complexo Muito alta Sim Enterprise

💻 BullMQ: Fila de Tarefas com Prioridade

// Produtor: router adiciona mensagens na fila
import { Queue } from 'bullmq'
const botQueue = new Queue('bot-tasks', { connection: redis })
async function dispatch(intent, message, userId) {
await botQueue.add(intent, {
message,
userId,
timestamp: Date.now(),
}, {
priority: intent === 'suporte' ? 1 : 5, // suporte = prioridade alta
attempts: 3, // retry automatico
backoff: { type: 'exponential', delay: 2000 },
})
}
// Consumidor: bot especialista processa da fila
import { Worker } from 'bullmq'
const worker = new Worker('bot-tasks', async (job) => {
const bot = bots[job.name] // seleciona bot pelo intent
const response = await bot.respond(job.data.message)
await sendToUser(job.data.userId, response)
}, { connection: redis, concurrency: 5 })

Priority Queues: Nem Toda Mensagem e Igual

Um usuario com problema no login nao pode esperar atras de um pedido de geracao de conteudo. Priority queues garantem que mensagens urgentes passam na frente.

Prioridade 1 (Critica)
Suporte: bugs, login, pagamento
SLA: <30 segundos
Prioridade 3 (Normal)
Vendas: pricing, demos, trials
SLA: <2 minutos
Prioridade 5 (Baixa)
Conteudo: artigos, newsletter
SLA: <5 minutos

💡 Dica Pratica

Envie uma mensagem de "processando" para o usuario imediatamente. Quando a mensagem entra na fila, responda ao usuario: "Entendi, estou analisando..." Isso evita que o usuario repita a mensagem ou ache que o bot travou. Quando o processamento terminar, envie a resposta completa. O OpenClaw faz isso com bot.sendChatAction('typing').

4

⚖️ Load Balancing entre Bots

Quando um bot especialista nao da conta do volume, voce precisa de multiplas instancias do mesmo bot. Load balancing distribui a carga entre elas. E o mesmo conceito de servidores web, aplicado a agentes de IA.

🎯 Conceito Principal

Load balancing para bots de IA tem nuances diferentes de web servers. O tempo de processamento e variavel (uma pergunta simples leva 2s, uma analise complexa leva 30s), entao algoritmos simples como round-robin podem sobrecarregar instancias. Least-connections tende a funcionar melhor para bots.

Outra estrategia e weighted routing: direcione mais trafego para instancias com modelos mais rapidos ou hardware melhor. E quando a carga aumenta, auto-scaling adiciona novas instancias automaticamente.

🔄

Round-Robin

Distribui mensagens sequencialmente entre instancias. Instancia 1, 2, 3, 1, 2, 3...

+ Simples de implementar
- Ignora carga atual de cada instancia
Bom quando todas as tarefas tem tempo similar
📉

Least-Connections

Envia para a instancia com menos tarefas ativas no momento. Adapta-se automaticamente a cargas variaveis.

+ Melhor distribuicao para tarefas de IA
+ Evita sobrecarga de instancias lentas
Recomendado para bots em producao
⚖️

Weighted

Atribui pesos por instancia. Instancia com GPU recebe peso 3x. Instancia com CPU recebe peso 1x.

+ Aproveita hardware heterogeneo
+ Combina com least-connections
Bom quando tem mix de cloud + local

💻 Load Balancer com Least-Connections

class BotLoadBalancer {
constructor(instances) {
this.instances = instances.map(i => ({ ...i, active: 0 }))
}
pickInstance() {
// Filtra instancias saudaveis, pega a com menos conexoes ativas
const healthy = this.instances.filter(i => i.healthy)
return healthy.sort((a, b) => a.active - b.active)[0]
}
async dispatch(message) {
const instance = this.pickInstance()
instance.active++
try {
return await instance.bot.respond(message)
} finally {
instance.active--
}
}
}
// Uso
const supportLB = new BotLoadBalancer([
{ bot: new SupportBot('ollama:14b'), healthy: true, weight: 3 },
{ bot: new SupportBot('ollama:3b'), healthy: true, weight: 1 },
])

📈 Auto-Scaling por Demanda

  • Metrica de trigger: Fila do bot com mais de 10 mensagens pendentes? Sobe nova instancia. Abaixo de 3 por 5 minutos? Desce
  • Limites: Defina min/max instancias. Min=1 (sempre ligado), Max=5 (nao estourar custo)
  • Cooldown: Espere 2 minutos entre scale up e scale down pra evitar flapping

💡 Dica Pratica

Para bots pessoais, voce nao precisa de load balancing. Um processo com concurrency=5 no BullMQ ja da conta de um usuario (voce). Load balancing so importa quando voce tem 100+ usuarios simultaneos. Nao adicione complexidade que voce nao precisa ainda.

5

🛡️ Fallback e Degradacao Graceful

Bots vao falhar. APIs caem, modelos ficam lentos, rate limits sao atingidos. O que define um sistema robusto nao e a ausencia de falhas, e como ele se comporta quando falha. Degradacao graceful garante que o usuario sempre recebe alguma resposta, mesmo que nao seja a ideal.

🎯 Conceito Principal

Degradacao graceful segue uma cadeia de fallback: se o bot especialista falha, tenta um bot generico. Se o bot generico falha, tenta uma resposta pre-programada. Se tudo falha, informa o usuario que vai analisar e retornar. Nunca silencio. Nunca erro cru.

Os tres pilares sao: health checks (detectar falha proativamente), circuit breaker (parar de chamar bot que esta falhando) e fallback chain (sempre ter um plano B, C e D).

🔗 Cadeia de Fallback

1
Bot Especialista - Tenta o bot ideal para a intencao detectada
OK → responde
falhou ↓
2
Bot Generalista - LLM generico tenta responder sem especializacao
OK → responde
falhou ↓
3
Resposta Pre-Programada - Template estatico por categoria de intent
Sempre funciona
impossivel ↓
4
Mensagem de Erro Amigavel - "Estou com dificuldades. Vou analisar e retornar."
Ultimo recurso

💓 Health Checks

// Verifica saude de cada bot a cada 30s
async function healthCheck(bot) {
try {
const start = Date.now()
await bot.ping() // mensagem de teste
const latency = Date.now() - start
return {
healthy: latency < 5000,
latency,
}
} catch {
return { healthy: false }
}
}

Circuit Breaker

// Para de chamar bot que falhou 3x seguidas
class CircuitBreaker {
failures = 0
state = 'closed' // closed | open | half-open
async call(fn) {
if (this.state === 'open')
throw new Error('Circuit open')
try {
const result = await fn()
this.failures = 0
return result
} catch (e) {
if (++this.failures >= 3)
this.openCircuit()
throw e
}
}
}

💡 Dica Pratica

Logue todas as falhas e fallbacks. Se voce nao sabe que o bot de vendas caiu 47 vezes ontem e o fallback respondeu no lugar, voce nao sabe que tem um problema. Crie alertas: se o fallback rate passa de 10%, algo precisa de atencao. O dashboard do OpenClaw mostra isso em tempo real.

6

🏗️ Exercicio: Implementar Router Central

Hora de colocar a arquitetura em pratica. Voce vai construir um router funcional que classifica intent e despacha para 2+ bots especialistas com fallback. Ao final, voce tera um sistema multibot minimo viavel rodando.

🏗️

Exercicio: Router Central com Fallback

Tempo estimado: 30-40 minutos

1

Configure o Projeto

Crie a estrutura basica com Node.js. Instale dependencias minimas.

mkdir multibot-router && cd multibot-router
npm init -y
npm install node-telegram-bot-api dotenv
# Opcional: Ollama para classificacao local
npm install ollama
# Estrutura
mkdir -p src/bots
touch src/router.js src/bots/support.js src/bots/sales.js src/bots/fallback.js
2

Implemente o Classificador

Comece com keyword matching. Depois adicione LLM como fallback para mensagens ambiguas.

// src/router.js
const INTENTS = {
support: ['bug', 'erro', 'problema', 'nao funciona', 'como faco'],
sales: ['preco', 'plano', 'comprar', 'upgrade', 'trial', 'demo'],
}
function classify(message) {
const lower = message.toLowerCase()
for (const [intent, kws] of Object.entries(INTENTS)) {
if (kws.some(kw => lower.includes(kw)))
return { intent, confidence: 0.9 }
}
return { intent: 'fallback', confidence: 0 }
}
3

Crie 2 Bots Especialistas

Cada bot com system prompt unico e funcao respond(). Pode usar Ollama local ou API.

// src/bots/support.js
class SupportBot {
system = `Voce e um agente de suporte tecnico.
Seja empatico, resolva rapido, consulte a FAQ.`
async respond(message) {
return await ollama.chat({
model: 'qwen2.5:14b',
messages: [
{ role: 'system', content: this.system },
{ role: 'user', content: message },
]
})
}
}
4

Implemente o Fallback com Try/Catch

Se o especialista falha, tenta o generalista. Se o generalista falha, resposta estatica.

async function handleMessage(message) {
const { intent, confidence } = classify(message)
const bot = confidence > 0.8 ? bots[intent] : bots.fallback
try {
return await bot.respond(message)
} catch (err) {
console.error(`Bot ${intent} falhou:`, err.message)
try {
return await bots.fallback.respond(message)
} catch {
return 'Desculpe, estou com dificuldades. Tente novamente em instantes.'
}
}
}
5

Conecte ao Telegram e Teste

Integre o router com o Telegram Bot API. Teste com 10 mensagens de tipos diferentes.

# Mensagens de teste
Suporte: "meu login nao funciona" → SupportBot
Suporte: "encontrei um bug na tela de pagamento" → SupportBot
Vendas: "qual o preco do plano enterprise?" → SalesBot
Vendas: "quero fazer upgrade" → SalesBot
Fallback: "oi, tudo bem?" → FallbackBot
6

Adicione Logging de Routing

Cada mensagem processada gera um log: intent, confianca, bot selecionado, latencia.

// Log de cada decisao de routing
console.log(JSON.stringify({
ts: new Date().toISOString(),
message: message.substring(0, 50),
intent,
confidence,
bot: bot.name,
latency_ms: Date.now() - start,
fallback: usedFallback,
}))

Criterios de Sucesso

Router classifica 3+ categorias de intent
2+ bots especialistas com prompts distintos
Fallback chain funcional (especialista → geral → estatico)
Logging de routing em cada mensagem
10 mensagens testadas com routing correto
Threshold de confianca implementado

🌟 Bonus: Evolua para LLM-Based Routing

Substitua o keyword matching por classificacao LLM usando Ollama local. Use qwen2.5:3b ou llama3.2:1b como classificador. Compare a acuracia com keyword matching e documente os resultados.

Extra credit: Adicione um health check que verifica se cada bot esta respondendo a cada 30 segundos. Se um bot falha 3x seguidas, marque como unhealthy e redirecione automaticamente pro fallback.

📋 Resumo do Modulo

Hub-and-spoke: router central recebe tudo, despacha para especialistas - Hub stateless, observabilidade centralizada, experiencia seamless pro usuario.
Router: keyword matching + LLM fallback e o melhor inicio - Rapido na maioria dos casos, preciso nos ambiguos. Thresholds de confianca evitam routing errado.
Message queues desacoplam router de bots - BullMQ para producao, SQLite polling para MVP. Priority queues para SLA por tipo de mensagem.
Least-connections e a melhor estrategia de load balancing para bots - Tempo de processamento variavel invalida round-robin simples. Auto-scaling por tamanho de fila.
Fallback chain: especialista → generalista → estatico → erro amigavel - Health checks + circuit breaker detectam e isolam falhas. Nunca silencio, nunca erro cru.
Seu router central esta funcional com classificacao, despacho e fallback - Base solida para evoluir: adicionar bots, melhorar classificacao, conectar canais.