Appearance
Arquitetura do flow-ai
Plataforma de atendimento multicanal — WhatsApp, WebChat e Instagram — com suporte a chatbot, agentes de IA (LLM) e handoff humano. A arquitetura é orientada a eventos: serviços internos comunicam-se exclusivamente por Redis Streams, sem chamadas HTTP entre si. HTTP existe apenas nas bordas — entrada dos canais (Meta, Instagram) e entrada dos frontends.
Visão de 30 segundos
Meta Cloud API Instagram Graph API Browser
│ │ │
│ webhook │ webhook │ Socket.io
▼ ▼ ▼
flow-ai-meta-api flow-ai-ig-api flow-ai-webchat-gateway
(:4444) (:4445) (:4001)
│ │ │
└─────────────────────┼──────────────────────┘
│ stream:incoming
▼
flow-ai-orchestrator (sem HTTP)
(persiste, gerencia sessão, roteia)
┌───────────────┼────────────────┐
stream:flow stream:agent stream:helpdesk
│ │ │
▼ ▼ ▼
flow-ai-engine flow-ai-agent flow-ai-core (:3333)
(executa o flow) (loop LLM/tools) (helpdesk, CRUD, API)
│ │ │
└───────┬───────┘ │ Socket.io
│ │
stream:outgoing-{meta,chat,ig} ▼
│ flow-ai-desk / flow-ai-core-ui
┌─────────────┼──────────────┐ (frontends)
▼ ▼ ▼
flow-ai-meta-api webchat-gw flow-ai-ig-api
(envia p/ Meta) (envia browser)(envia p/ IG)Nota:
stream:agentsó é usado quando a sessão está no modoagent; oflow-ai-agentexecuta o loop de tool-calling do LLM e devolve respostas ao canal de origem.
Serviços
flow-ai-core — porta 3333
API administrativa + helpdesk em tempo real.
Responsabilidades:
- CRUD de todas as entidades de negócio (Router, Flow, WhatsAppNumber, Tickets, Queues, etc.)
- Fronteira de criptografia: único serviço que conhece
CRYPTO_SECRETe popula os caches Redis com segredos decifrados - Helpdesk em tempo real via Socket.io: emissão de eventos para agentes, contagem de tickets em espera
- Warm-up dos caches Redis no boot (bloqueante antes do
listen()) - Serve
flow-ai-core-uieflow-ai-deskvia REST + Socket.io
Stack: Fastify 5, Prisma, PostgreSQL, Socket.io, JWT, Redis
flow-ai-meta-api — porta 4444
Borda burra entre a Meta e o sistema interno.
Responsabilidades:
- Recebe webhooks da Meta, valida verify token + assinatura HMAC-SHA256 e normaliza o payload
- Publica mensagens recebidas em
stream:incoming(estream:status,stream:templates,stream:wa-flow-logs) - Consome
stream:outgoing-meta(gruposend) e envia mensagens para a Meta Cloud API - Resolve credenciais e app secret exclusivamente via Redis — nunca toca o Postgres
Stack: Fastify 5, Redis Streams
flow-ai-ig-api — porta 4445
Borda do canal Instagram — análogo ao flow-ai-meta-api para o Instagram.
Responsabilidades:
- Recebe webhooks do Instagram, valida verify token (
IG_VERIFY_TOKEN) e normaliza DMs emstream:incoming - Moderação de comentários de post/live: publica em
stream:ig-commentse registra ativações emstream:ig-comment-activations - Consome
stream:outgoing-ig(gruposend) e envia mensagens via Instagram Graph API - Resolve credenciais e roteamento exclusivamente via caches
ig:*no Redis — nunca toca o Postgres
Stack: Fastify 5, Redis Streams
flow-ai-orchestrator — sem HTTP
Consumer de stream:incoming. Ponto de coordenação central.
Responsabilidades:
- Persiste mensagens recebidas no banco
- Cria ou retoma
SessionStateno Redis - Em nova sessão: resolve
routerId+initialFlowIdpor canal —wa:router:{phoneNumberId}(WhatsApp),wc:channel:{channelId}(WebChat) ouig:router:{igUserId}(Instagram) - Roteia para
stream:flow(modo flow),stream:agent(modo agent) oustream:helpdesk(modo human) - Persiste as mensagens outbound (consome
stream:outgoing-metaestream:outgoing-chatno grupopersist) - Persiste status de entrega das mensagens outbound (
stream:status)
Stack: Prisma, PostgreSQL, Redis Streams
flow-ai-engine — sem HTTP
Consumer de stream:flow. Executa a máquina de estados do flow.
Responsabilidades:
- Carrega
SessionState+PublishedFlow+ variáveis do router/flow do Redis - Percorre blocos: avalia condições, executa actions, aguarda input do usuário
- Publica respostas no stream de saída conforme o prefixo do
sessionId—stream:outgoing-meta(WhatsApp),stream:outgoing-chat(wc-*, WebChat) oustream:outgoing-ig(ig-*, Instagram) - Publica handoff em
stream:helpdeskquando entra numHumanAttendanceBlock - Ao pausar num
AgentBlock, publica emstream:agentpara transferir o controle aoflow-ai-agent - Emite
stream:analytics,stream:tool-executionsestream:summarydurante a execução - Suporta scripts V8 Isolate sandboxado, chamadas HTTP, redirecionamentos entre flows
Stack: isolated-vm (sandbox), OpenAI SDK, flow-ai-runtime, Prisma, Redis Streams
flow-ai-agent — sem HTTP
Consumer de stream:agent. Executa o loop de tool-calling do LLM quando a sessão está no modo agent.
Responsabilidades:
- Consome
stream:agent(grupoagent, consumer serial único) - Roda o loop LLM (Responses API): monta contexto, chama tools, encadeia agentes (orchestrator → specialist)
- Mantém
SessionState.mode = "agent"e oAgentState(histórico,previousResponseId, pilha de agentes) - Emite respostas ao canal em
stream:outgoing-meta/stream:outgoing-chat - Emite
stream:agent-turns(AI Debugger),stream:llm-usage(custo/tokens) estream:summary - Cria/fecha tickets de IA e faz handoff via
stream:helpdesk; ao encerrar, retoma o flow viastream:flow
Nota: o
flow-ai-agentainda não roteia parastream:outgoing-ig. Uma sessão de agente de IA respondendo em uma conversaig-*publica emstream:outgoing-meta(canal incorreto) — verservices/flow-ai-agent/src/tools/response.ts.
Stack: @anthropic-ai/sdk, OpenAI SDK, flow-ai-runtime, Prisma, Redis Streams
flow-ai-webchat-gateway — porta 4001
Canal WebChat — equivalente ao flow-ai-meta-api para o browser.
Responsabilidades:
- Autentica conexões Socket.io via
sessionKey(bcrypt) +channelId - Cria/retoma
WebChatIdentityno Postgres - Publica mensagens do browser em
stream:incomingcomchannelType: "webchat" - Consome
stream:outgoing-chate entrega ao socket correto via roomwc-{channelId}-{userId}
Stack: Socket.io, Prisma, Redis, bcrypt
Modos de sessão
O SessionState carrega um campo mode (SessionMode) que determina para qual stream o orchestrator roteia cada mensagem recebida:
mode | Roteia para | Quem processa |
|---|---|---|
flow | stream:flow | flow-ai-engine (máquina de estados do flow) |
agent | stream:agent | flow-ai-agent (loop LLM de tool-calling) |
human | stream:helpdesk | flow-ai-core (atendimento humano) |
Nota: não existe modo
frozen. Uma sessão travada por erro de runtime permanece emmode: "human"e carrega o campofrozenByError(SessionFrozenError). OAgentStatesó está presente quandomode === "agent".
Packages
| Package | Responsabilidade |
|---|---|
flow-ai-database | Schema Prisma centralizado + client singleton. Fonte da verdade do banco. |
flow-ai-redis | Funções de acesso ao Redis: streams, caches, pub/sub, sessões, constantes de chaves e graceful shutdown. |
flow-ai-types | Tipos TypeScript compartilhados: SessionState, eventos de stream, ai-agents, instagram, webchat, FlowDefinition, DTOs. |
flow-ai-whatsapp-flow-engine | Engine declarativo para WhatsApp Flows Meta — validação, steps HTTP, criptografia RSA+AES. |
flow-ai-runtime | Primitivas de execução de Tool compartilhadas por flow-ai-engine e flow-ai-agent: interpolação, avaliação de condições, sandbox isolated-vm, executeTool, executeCallAI, executeFetchMedia. |
flow-ai-telemetry | Bootstrap OpenTelemetry: initTelemetry(serviceName) (async, ordem forçada), spans e propagação de contexto de trace entre mensagens de stream. Config dirigida pela UI via observability:config. |
flow-ai-logger | Factory de logger pino (createLogger); níveis via LOG_LEVEL / LOG_LEVEL_{SERVICE}, pino-pretty em dev. |
flow-ai-benchmark | Framework de load/chat-test, CLI flow-benchmark; consumido pelo flow-ai-core. |
Apps (frontends)
| App | Porta | Descrição |
|---|---|---|
flow-ai-core-ui | 3001 | Studio de configuração — routers, builder de flows, números WhatsApp, variáveis, templates. |
flow-ai-desk | 3000 | Desk de atendimento humano — tickets em tempo real, histórico de conversas, transferências. |
flow-ai-docs | 5173 | Este portal de documentação — VitePress + Scalar API Reference. |
Stack dos frontends: Next.js 15, React 19, TypeScript, Tailwind CSS, Socket.io-client
Streams Redis
Todos os serviços internos se comunicam por streams. Nenhuma chamada HTTP interna existe.
Nomes e grupos canônicos em packages/flow-ai-redis/src/constants.ts; mapa de grupos em STREAM_GROUP_MAP (streams.ts).
| Stream | Produtor | Consumidor(es) — grupo | Conteúdo |
|---|---|---|---|
stream:incoming | meta-api, ig-api, webchat-gateway | orchestrator | Mensagem recebida do usuário (sempre em formato WhatsApp) |
stream:flow | orchestrator, engine, agent, core | engine (engine) | Evento para execução do flow |
stream:helpdesk | orchestrator, engine, agent, core | core (core) | Handoff e mensagens em atendimento humano |
stream:outgoing-meta | engine, agent, core | meta-api (send) + orchestrator (persist) | Mensagem a enviar via WhatsApp |
stream:outgoing-chat | engine, agent, core | webchat-gateway (webchat) + orchestrator (persist) | Mensagem a entregar no browser |
stream:outgoing-ig | engine, core, ig-api | ig-api (send) | Mensagem a enviar via Instagram |
stream:status | meta-api, ig-api, webchat-gateway | orchestrator (status), core (core-status, core-campaigns-status) | Status de entrega (sent, delivered, read, failed) |
stream:wa-flow-logs | meta-api | core (core-wa-flow-logs, core-wa-flow-deferred) | Logs de webhook de WhatsApp Flows |
stream:templates | meta-api | core (core-templates) | Eventos WABA-level de status/quality de templates |
stream:analytics | engine | core (core-analytics) | Eventos de recordEvent — persistidos em event_occurrences |
stream:tool-executions | engine | core (core-tool-executions) | Execuções de Tool — persistidas em tool_execution_log |
stream:agent | engine, agent, orchestrator, core | agent (agent) | Início/retomada de sessão de agente de IA e handoff manual |
stream:agent-turns | agent | core (core-agent-turns) | Turnos do agente de IA (AI Debugger) — persistidos em agent_turns |
stream:llm-usage | agent | core (core-llm-usage) | Uso de tokens/custo — persistido em llm_usage |
stream:summary | engine, agent, core | core (core-summary) | Pedidos de sumarização de conversa via LLM |
stream:ig-comments | ig-api | ig-api (ig-comments) | Comentários de post/live do Instagram (moderação) |
stream:ig-comment-activations | ig-api | core (core-ig-comment-activations) | Disparos de gatilho de comentário — persistidos em instagram_comment_activations |
Nota:
stream:outgoingé legado/inativo — a constanteOUTGOINGexiste emconstants.ts, mas não está noSTREAM_GROUP_MAPe não tem produtor nem consumidor. Os três streams de saída vivos sãostream:outgoing-meta,stream:outgoing-chatestream:outgoing-ig.
Nota: o grupo
persistestá registrado emstream:outgoing-ig(STREAM_GROUP_MAP), mas nenhum consumidor o lê. Consequência: as mensagens de saída do Instagram são enviadas, porém não são persistidas no Postgres pelo orchestrator.
Caches Redis (chaves principais)
Sessão e estado (escritos pelos serviços de runtime):
| Chave | Quem escreve | Quem lê | Conteúdo |
|---|---|---|---|
session:{sessionId} | orchestrator, engine, agent | orchestrator, engine, agent, core | SessionState com TTL = expiryTimeout |
sessions:active:{routerId} | orchestrator, core | core | Sorted set de sessões ativas (score = lastInteractionAt) |
sessions:frozen | engine, core | core | Sorted set de sessões congeladas por erro |
Config e credenciais (flow-ai-core é o único escritor):
| Chave | Quem lê | Conteúdo |
|---|---|---|
wa:router:{phoneNumberId} | orchestrator | { routerId, initialFlowId } |
wa:credentials:{phoneNumberId} | meta-api | { accessToken, displayName, wabaId } |
wa:webhook:{phoneNumberId} / wa:webhook:waba:{wabaId} | meta-api | { routerId, appSecret } |
wa:encryption:{phoneNumberId} | meta-api | { privateKeyPem, passphrase } |
meta:verify-token:{token} | meta-api | { routerId } |
wa:flow:{metaFlowId} | meta-api | { endpointConfig, phoneNumberId, ... } |
wc:channel:{channelId} | webchat-gateway, orchestrator | { routerId, initialFlowId, appearance } (TTL 3600s) |
ig:credentials:{igUserId} | ig-api | Credenciais do canal Instagram |
ig:router:{igUserId} | orchestrator | { routerId, initialFlowId } do Instagram |
ig:webhook:{igUserId} | ig-api | Config de assinatura do webhook IG |
ig:comment-triggers:{igUserId} | ig-api | Gatilhos (régua) de moderação de comentário |
vars:router:{routerId} | engine | Record<string, string> (variáveis decifradas) |
vars:flow:{flowId} | engine | Record<string, string> (variáveis decifradas) |
tool:{toolId} | engine, agent | ToolDefinition publicada |
integration:server:{serverId} | engine, agent | Config de servidor de integração |
agent:{agentId} | agent | Config do agente de IA (AgentConfig) |
llm:credential:{credentialId} | agent, core | Credencial LLM decifrada |
observability:config | todos os serviços | Config de telemetria (singleton, dirigida pela UI) |
Chaves de moderação de comentário IG (escritas fora do core):
| Chave | Quem escreve | Conteúdo |
|---|---|---|
ig:comment-handled:{igUserId}:{commentId} | ig-api | Dedupe atômico (SET NX EX) por comentário |
ig:comment-intent / ig:comment-destination:{igUserId}:{from} | ig-api | Intent/destino pendente do gatilho (consumo GETDEL) |
ig:pending-comment-reply:{sessionId} | orchestrator | commentId para a 1ª resposta ir como Private Reply |
Princípio: o flow-ai-core é o único escritor dos caches de config e credenciais (centraliza a fronteira de criptografia e simplifica a invalidação). Os caches de sessão/estado são escritos pelos serviços de runtime, e algumas chaves efêmeras de Instagram são escritas pelo flow-ai-ig-api e pelo orchestrator.
Banco de dados (PostgreSQL)
77 modelos (+ 10 enums) organizados em grupos lógicos:
- Identidade / RBAC:
User,Group(permissões em JSON +routerIdsde escopo),ApiClient(token M2M SHA-256),UserDevice,PasswordRecovery - Conversação:
Contact,Chat,Message,ContactOptIn - Helpdesk:
Ticket(kindhuman/ai, lineagepreviousTicketId),TicketTag,Tag,Queue,QueueAgent,QueueDistributionRule - Flows / Tools:
Flow,PublishedFlow,FlowVariable,FlowTag,ScriptTemplate,Tool,PublishedTool,ToolExecutionLog,IntegrationServer,IntegrationEndpoint,ImportDraft,SessionRuntimeError - Configuração / Router:
Router,RouterVariable,WhatsAppNumber,LlmCredential,ObservabilitySettings(singleton),Integration,RouterIntegration,MetaDataDeletionRequest - WhatsApp Flows:
WhatsAppFlow,WhatsAppFlowPublishLog,WhatsAppFlowWebhookLog,WhatsAppFlowDeferredJob,MessageTemplate - WebChat:
WebChatChannel,WebChatIdentity - Instagram:
InstagramAccount,InstagramCommentTrigger,InstagramCommentActivation - Multicanal:
ChannelTypeMapping(conversão de conteúdo WA → IG/WebChat) - Agentes de IA:
AiAgent,AiAgentRoute,AiAgentTool,SystemPrompt,AgentSystemPrompt,Guardrail,AgentRagDocument,AgentRagChunk(pgvector),LlmUsage,AgentTurn - Atendimento:
Department,DepartmentAgent,MessageGroup,DepartmentMessageGroup,QuickReply,Agent,AgentGroup - DeskActions:
DeskActionCategory,DeskAction,DeskActionAgentGroup,DeskActionDepartment - Eventos / Campanhas / Auditoria:
EventDefinition,EventOccurrence,Report,Campaign,CampaignRecipient,AuditLog - Chat-Test:
FlowGuide,FlowGuideStep,TestRun,TestRunStep
Ver o guia Modelo de dados para o diagrama completo com relações.
Infraestrutura de desenvolvimento
Monorepo: pnpm workspaces + Turborepo
Node: ≥ 20
Banco: PostgreSQL 16
Cache: Redis 7
bash
pnpm dev # inicia todos os serviços em paralelo
pnpm dev:local # sem flow-ai-meta-api (teste local sem Meta)
pnpm typecheck # validação TypeScript em todo o monorepo
pnpm lint # ESLint em todo o monorepoMigrações:
bash
cd packages/flow-ai-database
pnpm prisma migrate dev # aplica migrações pendentes
pnpm prisma generate # regenera o Prisma Client