Flymmatik: flows de IA que se leem como receitas — a DSL e o porquê do Elixir
O desenho do Flymmatik: uma superfície mnemônica para flows de IA declarativos, os princípios que a seguram, e por que o runtime é Elixir/OTP.

O Flymmatik é um runtime pequeno, em Elixir, para flows de IA declarativos. Um flow é um diretório com um YAML de mesmo nome; o runtime valida a entrada, interpola variáveis, executa os steps em ordem e devolve, como JSON, apenas as chaves de contexto que o flow declarou como saída. Isso é tudo — e o resto deste texto é sobre por que “isso é tudo” foi a decisão de design mais difícil de manter.
O nome vem do tema da mosca: um aceno à Grace Hopper e ao vocabulário cotidiano de “bug” que deu nome ao debugging — a mosca não é a mariposa histórica do log dela, mas mora no mesmo dicionário. A intenção está no tema: sair do procedimento manual para a automação, sem esconder onde a máquina decide.
O problema
Fluxos de IA em produção viram, quase sempre, código imperativo espalhado: um script chama um modelo, cola o resultado num prompt, chama outro, trata erro pela metade, e seis meses depois ninguém sabe onde a IA decide e onde ela apenas formata. As bibliotecas de orquestração atacam isso adicionando abstração — classes, grafos, callbacks — e o fluxo fica mais difícil de ler, não menos.
A aposta do Flymmatik é a oposta: o fluxo é um documento. Uma receita que se lê de cima a baixo, versionável, diffável, auditável — e executável. Se a tese de que documento vem antes de código vale para projetos, vale para o próprio artefato que descreve um processo de IA.
A superfície mnemônica
Todo primitivo do parser é um mnemônico de 3 ou 4 letras: FLOW,
DECL, INPS, BANK, STEP, USE, WITH, RETS, OUTS, e
adiante. Maiúsculas ou minúsculas, tanto faz — o parser é
case-insensitive; a convenção visual é caixa alta, porque em caixa alta
os mnemônicos funcionam como marcas de seção num texto corrido: o olho
acha STEP numa página de YAML como acha um título.
Um trecho — condensado, mas fiel — do bncc-map, o flow que atende o
bncc.click em produção:
FLOW:
DECL: bncc-map
DESC: BNCC Click retrieval flow
INPS:
- briefing
- ano
- disciplina
# ...
STEP:
- DECL: retrieve_habilidades
PAR: 3 # três buscas, em paralelo
STEP:
- DECL: search_nucleo
USE: bncc # a coleção no PaveDB
WITH:
filters: {Bloco_STEAM: "1", Bloco_PERF: "1"}
RETS: nucleo
- DECL: search_transversal
USE: bncc
WITH:
filters: {Disciplina: "!{{disciplina}}"}
RETS: transversal
# ...
# Embedding aproxima habilidades irmãs (EF67LP08 vs EF89LP08);
# o juiz LLM reordena o núcleo contra a aula descrita.
- DECL: rerank_nucleo
USE: rerank
WITH:
from: nucleo
query: "{{briefing}}"
# Curto de propósito: juiz frio/lento -> ERR: skip devolve
# a ordem do embedding em vez de deixar a professora esperando.
timeout_ms: 25000
ERR: skip
RETS: nucleo
Repare no que a página conta sozinha: onde há paralelismo (PAR), onde
a IA decide (rerank) e o que acontece quando ela falha (ERR: skip,
com o porquê no comentário). O fluxo é o documento — inclusive os seus
compromissos operacionais.
Por que mnemônicos, em vez de palavras completas e “amigáveis”? Três motivos práticos:
- Densidade de leitura. Uma receita boa tem ingredientes e passos, não parágrafos. Com mnemônicos curtos, a estrutura ocupa pouco: o grosso da página são os prompts, os campos e as condições, que é o que alguém abre o arquivo para ler.
- Vocabulário fechado. Um conjunto pequeno e estranho o suficiente para não colidir com o inglês corrente força o autor a aprender o vocabulário uma vez — e força o projeto a mantê-lo canônico. O glossário do Flymmatik abre com a regra: se o termo não está lá, não é canônico. O parser rejeita sintaxe legada; a spec é o contrato, não uma sugestão.
- Herança honesta. Mnemônicos de poucas letras são a estética de uma era em que cada token custava — assembly, COBOL, telex. Num runtime cujo tema é a Hopper, a homenagem é funcional: brevidade como disciplina, não como nostalgia.
Princípios que seguram o desenho
- Flow é função pura. Entra um dicionário, sai um JSON com as
chaves selecionadas em
RETS/OUTS. Sem estado escondido entre runs. O que o flow sabe está no contexto; o que o flow entrega está declarado. - IA explícita. LLM aparece somente em nós de decisão (
if/switchdirigidos por modelo, com schema booleano ou enum forçado) e em nós de geração (gen, com JSON schema). Nunca embutida em “mágica” de biblioteca. Quem lê o flow sabe exatamente onde a máquina opina. - Canais são externos. Telegram, WhatsApp, Google Chat, REST, web,
ou uma API compatível com OpenAI — um canal adapta um transporte ao
runtime; o flow não sabe de onde veio a mensagem. Um listener por
protocolo, com despacho por caminho canônico
/<flow>/<adapter>. - Providers plugáveis. Retrieval, memória e backends de LLM são trocáveis. O retrieval de referência é o PaveDB — que grava e reproduz queries, então um flow inteiro pode ser reexecutado com as provas de cada busca.
- Zero dependências onde der. O runtime anda com a biblioteca padrão. Cada dependência é uma decisão, não um reflexo.
- Spec forward-compatible. Um step pode existir “spec only”: declarado na especificação, rejeitado em execução até a implementação chegar. O vocabulário anda na frente do motor, nunca atrás.
Papéis: kernel, callable, channel, resource
Um flow (ou módulo de código) exerce papéis num grafo de invocação: o kernel conduz o caso; callables são chamáveis (subflows inclusive); channels falam com o mundo; resources servem capacidades (modelo, retrieval, memória). Um mesmo flow pode exercer mais de um papel. Essa separação é o que permite trocar um canal de Telegram por web sem tocar no kernel — e o que mantém o caso de uso fora do motor, erro que já enterrou uma geração anterior deste projeto.
O conjunto de steps do v0
Deliberadamente pequeno: gen (geração com schema e usage), extract
(campos tipados → schema estrito), if e switch (determinísticos ou
LLM-driven), each (map com contexto filho), parallel (fan-out/
fan-in de steps independentes), ask (espera resposta de um canal),
template (EEx), emit (evento na trilha do run), rerank
(reordenação por índice, sem reescrever conteúdo), e erro por step com
três políticas: halt, skip, default. Cada run produz um trace
JSON com ids de run e de step. Distribuição em arquivo único .fmtk
(pack/unpack), e um adaptador MCP para plugar o runtime em
ferramentas que falam esse protocolo.
Por que Elixir
A resposta curta: porque o step ask existe.
Um flow conversacional espera. Espera minutos ou dias, no meio do passo 7 de 12, por uma resposta que chega por um canal qualquer. Em quase todo runtime, isso vira uma máquina de estados serializada num banco — o fluxo é fatiado em “antes” e “depois” da espera, e a leitura de cima a baixo morre.
Na BEAM, cada run é um processo. ask é um processo em espera
seletiva: o flow simplesmente para na linha em que está até a
mensagem chegar, e o texto da receita continua correspondendo à
execução. Milhares de conversas paradas custam milhares de processos —
ou seja, quase nada.
O resto do OTP vem junto e é tudo coisa que um runtime de flows precisa e normalmente reimplementa mal: supervisão (um run que morre, morre sozinho; o supervisor reergue servidores e canais sem derrubar vizinhos), isolamento (um flow em loop não trava o runtime), registro de conversas (o despacho canal→run é uma tabela de processos, não um banco), e pattern matching, que faz parser e dispatch de mnemônicos ficarem do tamanho que merecem. A biblioteca padrão rica sustenta a política de zero dependências.
Elixir não foi escolhido por moda — fluxo conversacional de longa duração é literalmente o problema que a plataforma de telecom que originou a BEAM resolvia: muitas sessões simultâneas, longas, que não podem derrubar umas às outras.
Direção
O próximo movimento da spec é fazer os steps declararem classes de capacidade (extrair, redigir, julgar) em vez de modelos, e carregarem critérios de aceitação como campo de primeira classe — schema e checagens sobre a saída. Com contrato tipado e aceitação declarada, o runtime pode provar que um step foi bem atendido, e a escolha de modelo vira detalhe de execução, não decisão do autor. A superfície de autoria não cresce com isso: quem escreve receita continua escrevendo receita.
O Flymmatik é AGPL — leia, audite, rode o seu. A versão gerenciada entra no Flowlexi Cloud, ao lado do PaveDB, quando sair do preview. Receitas que se leem como receitas, rodando como relógio — com as provas guardadas.
Follow the work
Essays and field notes, as they’re published.
Read new posts through RSS, or follow the shorter notes on LinkedIn.