Quando eu li sobre Dynamic Workflows, percebi que eu já usava a maior parte desses padrões antes mesmo de nomeá-los. A diferença é que a minha implementação não começa a partir de um script altamente orquestrado. Ela começa a partir de comportamentos semânticos que publicam e assinam eventos.
Em um workflow tradicionalmente orquestrado, existe um controlador central que conhece a sequência inteira:
- classificar
- chamar ferramenta
- esperar
- ramificar
- tentar novamente
- mesclar
- finalizar
Isso é útil quando o sistema precisa de previsibilidade, visibilidade centralizada e aplicação rígida de políticas. Mas, na minha arquitetura, muitos fluxos são naturalmente coreografados. Um QuarkBehavior não precisa conhecer o sistema inteiro. Ele só precisa conhecer o contrato semântico que valida, os eventos que emite e os eventos que assina.
Isso cria um modelo híbrido:
-
IntentGraph / graflowdefine a macrointenção. -
QuarkBehaviordefine a menor unidade semântica. -
AtomicBehaviorcompõe comportamentos semânticos. -
SubAgentsreagem a eventos. -
ProofAgentsverificam corretude. -
HealingAgentsreparam estados inválidos. -
BenchmarkAgentsmedem custo, latência e comportamento de execução. -
Governordecide se o resultado é aceitável.
Nesse modelo, a orquestração é usada para a intenção de alto nível, enquanto a coreografia é usada para adaptação local. O orquestrador diz o que precisa ser alcançado. Os agentes descobrem como continuar reagindo a eventos semânticos.
1. Classify-And-Act
Esse foi o primeiro padrão que usei com IA, antes mesmo de ter um nome formal para ele. O usuário envia uma mensagem, o sistema classifica a intenção e o runtime roteia o payload para o comportamento correto.
Na minha arquitetura, isso não é apenas “classificação de intenção”. É roteamento semântico.
Uma mensagem pode ser classificada como:
CreateEntityIntentUpdateEntityIntentFindEntityIntentSendMessageIntentValidateFieldIntentHealInvalidPayloadIntentGenerateBehaviorIntentVerifyGeneratedCodeIntent
Depois da classificação, o sistema não apenas chama uma função. Ele seleciona um comportamento semântico, valida o payload, extrai valores primitivos, converte tipos quando necessário e emite success ou error.
Versão orquestrada:
IntentResolver -> Router -> Behavior -> Response
Versão coreografada:
received.message -> classified.intent -> requested.behavior -> validated.behavior -> ended.behavior.in.success | ended.behavior.in.error
A versão coreografada é mais alinhada com meu runtime, porque cada etapa pode ser substituída, observada, curada, medida ou formalmente verificada sem alterar o fluxo inteiro.
2. Fanout-And-Synthesize
Esse padrão já está embutido na forma como meu runtime trata funções sub.
Quando um payload chega a um subscriber, o runtime automaticamente chama a função forge do Semantic QuarkBehavior Type correspondente. O comportamento valida a entrada, extrai o valor primitivo, tenta conversão de tipo se necessário e emite seu próprio evento de erro se a validação falhar.
Isso é uma forma de fanout porque um único payload de entrada pode ser testado por vários comportamentos semânticos independentes:
- validador de tipo
- extrator de primitivo
- validador de schema
- validador semântico
- compilador específico de linguagem
- proof checker
- benchmark runner
- self-healing pipeline
A etapa de síntese não é apenas uma mesclagem de texto. É uma redução semântica.
O runtime sintetiza:
- qual valor é válido
- qual conversão funcionou
- qual comportamento falhou
- qual regra de tipo específica da linguagem causou a falha
- qual função de healing deve ser adicionada ou reutilizada
- qual artefato gerado é aceitável
Em um workflow de VibeCoding, isso fica ainda mais forte:
- um agente gera TypeScript
- um agente gera Zig
- um agente gera Haskell
- um agente gera regras Prolog
- um agente gera testes
- um agente verifica equivalência semântica
- um agente mede custo
- um agente sintetiza o comportamento final aceito
Isso não é simplesmente geração paralela. É fanout sobre responsabilidade semântica.
3. Adversarial Verification
Esse é o workflow que eu deveria formalizar de forma mais explícita.
Adversarial Verification significa que, depois que um comportamento, função, tradução da DSL ou implementação gerada é produzida, outro agente recebe esse output com o objetivo explícito de provar que ele está errado.
O agente adversarial não deve se comportar como um revisor tentando ser útil. Ele deve se comportar como:
- atacante
- compilador
- type checker
- property-based tester
- usuário malicioso
- simulador de falha em runtime
Na minha arquitetura, isso pode ser implementado assim:
DSL.pub("generated.candidate")
generated.candidate -> request.adversarialVerification.for.candidate
Depois, múltiplos verificadores assinam esse evento:
TypeAdversaryAgentSecurityAdversaryAgentSemanticEquivalenceAdversaryAgentLinearUsageAdversaryAgentRuntimeCrashAdversaryAgentPayloadCorruptionAdversaryAgentPromptInjectionAdversaryAgent
Cada um emite um destes resultados:
passed.verificationfound.counterexamplefound.securityViolationfound.semanticDriftfound.runtimeFailure
Esse é um workflow naturalmente coreografado. O gerador não precisa chamar cada verificador diretamente. Ele apenas emite um candidato. Qualquer verificador que entende esse evento pode assinar, atacar e tentar quebrar o candidato.
Para o meu sistema, isso é essencial porque eu gero código em várias linguagens a partir de uma DSL semântica. Se implementações em TypeScript, Zig, Haskell e Prolog dizem representar o mesmo comportamento, um verificador adversarial precisa tentar encontrar onde elas divergem.
4. Generate-And-Filter
Generate-And-Filter é uma versão menos agressiva de Adversarial Verification.
Nesse padrão, o sistema gera múltiplas soluções possíveis e depois filtra essas soluções usando gates determinísticos.
Por exemplo, ao gerar um novo QuarkBehavior, o sistema pode produzir vários candidatos:
- implementação mínima
- implementação strict
- implementação rápida
- implementação segura
- implementação linear
- implementação idiomática da linguagem
Depois disso, os filtros removem qualquer coisa que falhe em:
- validação de schema
- validação de tipo
- testes unitários
- testes de propriedade
- testes de equivalência semântica
- compilação
- linting
- benchmark de runtime
- política de segurança
- regra de consumo linear
- regra de ZeroTrust
Isso já é muito próximo de como eu trabalho manualmente durante o VibeCoding. Eu peço variações, comparo, mantenho as partes úteis, descarto o que quebra a arquitetura e converto o padrão vencedor em um comportamento reutilizável.
No meu runtime, o filtro não deve ser subjetivo. Ele deve ser um pipeline de gates executáveis de aceitação:
generated.candidate -> filtered.candidate -> accepted.candidate | rejected.candidate
A ideia principal é que geração é barata, mas aceitação precisa ser rígida.
5. Tournament
Tournament é o padrão que eu devo usar quando não existe uma implementação obviamente melhor.
Em vez de gerar uma solução e validá-la, eu gero várias soluções concorrentes e pontuo todas contra a mesma rubrica.
Para a minha arquitetura, um torneio pode comparar:
- linguagens diferentes
- codificações de tipo diferentes
- formatos de serialização diferentes
- estratégias de healing diferentes
- estratégias de sharding diferentes
- janelas diferentes de agregação de eventos
- planos diferentes de tradução SQL para graflow
- implementações diferentes do mesmo
QuarkBehavior
Um torneio deve produzir um scorecard, não apenas um vencedor.
Exemplos de dimensões:
- passa nos testes
- preserva equivalência semântica
- compila com sucesso
- tem menor custo de CPU
- usa menos memória
- faz menos alocações
- tem menor custo de healing
- tem melhor rastreabilidade
- gera menos código
- é mais fácil de provar
- é mais seguro sob regras ZeroTrust
- não viola consumo linear
O workflow de torneio é naturalmente coreografado:
submitted.candidate -> request.benchmark -> request.proof -> submitted.score -> closed.tournament -> selected.winner
Cada candidato é independente. Cada agente de pontuação é independente. O coordenador do torneio apenas fecha a rodada e seleciona o vencedor depois que pontuações suficientes foram recebidas.
É exatamente assim que eu posso transformar VibeCoding em engenharia. Eu não preciso confiar no primeiro output. Eu posso criar uma competição entre implementações e deixar testes, provas, benchmarks e políticas decidirem.
6. Loop-Until-Done
Loop-Until-Done é a expressão mais clara de Intent-based Healing.
A maioria dos sistemas retorna erro quando um valor não corresponde ao tipo esperado ou à restrição semântica esperada. O meu sistema trata o erro como um novo evento.
Um comportamento que falha emite error.
O mesmo comportamento, ou um agente especializado em healing, assina esse erro.
O pipeline de healing recebe:
- payload original
- último estado do payload
- valor inválido
- comportamento que rejeitou o valor
- trace interno
Depois, ele tenta reparar o payload.
Se tiver sucesso, ele emite success.
Se falhar, ele desembrulha o valor recursivamente até uma representação primitiva, tenta conversão de tipo, tenta conversão semântica e emite um novo erro com um estado mais rico.
O loop não é repetição cega. É um loop de convergência.
O workflow é:
AtomicBehavior.pub("execute.{QuarkBehavior}")
execute.{QuarkBehavior}
-> QuarkBehavior.forge
-> [
Ok:pub("ended.{QuarkBehavior}.in.success"),
Error:pub("failed.{QuarkBehavior}.at.validation")
]
failed.{QuarkBehavior}.at.validation
-> execute.selfHealingPipeline
-> [
Ok:pub("ended.{QuarkBehavior}.in.success"),
Error:pub("failed.{QuarkBehavior}.at.selfHealingPipeline")
]
failed.{QuarkBehavior}.at.selfHealingPipeline
-> unwrapRecursiveToPrimitive
-> [
Ok:pub("ended.{QuarkBehavior}.in.success"),
Error:pub("failed.{QuarkBehavior}.at.unwrapRecursiveToPrimitive")
]
failed.{QuarkBehavior}.at.unwrapRecursiveToPrimitive
-> convertTypePipeline
-> [
Ok:pub("ended.{QuarkBehavior}.in.success"),
Error:pub("failed.{QuarkBehavior}.at.convertTypePipeline")
]
failed.{QuarkBehavior}.at.convertTypePipeline
-> convertSemanticData
-> [
Ok:pub("ended.{QuarkBehavior}.in.success"),
Error:pub("ended.{QuarkBehavior}.in.error")
]
AtomicBehavior.sub("ended.{QuarkBehavior}.in.success")
AtomicBehavior.sub("ended.{QuarkBehavior}.in.error")
A regra importante é que esse loop precisa ter memória. Cada iteração precisa adicionar informação:
- qual conversão falhou
- qual primitivo foi extraído
- qual regra semântica rejeitou o valor
- qual função de healing foi tentada
- qual regra de tipo específica da linguagem esteve envolvida
- qual candidato de reparo foi produzido
- qual reparo nunca deve ser tentado novamente
Sem memória, isso é apenas retry. Com memória, isso vira Intent-based Healing.
Looping VibeCoding Engineering
Looping VibeCoding Engineering é o nome que eu daria para a forma como eu trabalho e para a forma como quero que meus agentes trabalhem.
Não é simplesmente pedir para uma IA programar.
É um loop de:
- classificar a intenção
- gerar implementações candidatas
- fazer fanout para agentes especializados
- filtrar outputs inválidos
- atacar outputs aceitos
- benchmarkar alternativas
- selecionar vencedores
- curar falhas
- transformar o reparo em um comportamento reutilizável
O objetivo final não é evitar erros. O objetivo final é converter cada erro em um novo comportamento semântico que previne que o mesmo erro aconteça novamente.
Foi por isso que essa arquitetura naturalmente se tornou Intent-based Healing. Uma intenção do usuário só deveria falhar quando não existe caminho seguro, válido ou cooperativo para completá-la. Qualquer outra falha é apenas conhecimento ausente esperando para virar um novo comportamento.
Top comments (0)