Guia de Criação de Flows com IA
Este recipe orienta agentes de IA (ou humanos auxiliados por IA) a produzir suites YAML completas para o Flow Test Engine com consistência, riqueza funcional e reaproveitamento máximo dos recursos já implementados no repositório.
1. Missão do Flow
- Entender o objetivo do cenário: qual API está sendo exercitada? Quais etapas de negócio precisam ser encadeadas? Há pré-condições ou dependências?
- Limite de escopo: priorize suites únicas com até três flows (caso precise dividir responsabilidades) e garanta que cada flow seja autoexplicativo nas descrições.
- Referência oficial: use o exemplo consolidado no final deste guia como checklist vivo dos recursos disponíveis.
2. Checklist Inicial Antes de Escrever YAML
- Confirmar
node_id,suite_name,descriptionemetadata(priority + tags) coerentes com o propósito. - Definir
base_urlevariablesnecessários, incluindo anchors quando o fluxo for compartilhar cabeçalhos dentro do mesmo documento. - Mapear dados dinâmicos que exigem
{{$env...}},{{$faker...}}ou{{$js...}}. - Planejar quais etapas precisam de
inputinterativo (incluindo selects encadeados, como no cenário de propostas). - Identificar quais valores precisarão ser compartilhados com outros flows (
exportseexports_optional).
3. Estrutura Recomendada de Suite
node_id: "my_flow_id"
suite_name: "Descrição amigável"
description: "Propósito do fluxo"
metadata:
priority: "high"
tags: ["categoria", "subcategoria"]
base_url: "{{$js.return env.API_BASE_URL || 'https://api.example.local'}}"
variables:
# Variáveis locais + anchors
exports: ["variavel_importante"]
steps:
- name: "Etapa X"
metadata: { ... }
request: {...}
assert: {...}
capture: {...}
input: {...}
continue_on_failure: false
3.1 Variáveis e Interpolação
- Env:
{{$env.NOME}}para valores obrigatórios do ambiente. - Faker:
{{$faker.internet.email}},{{$faker.person.fullName}},{{$faker.string.uuid}},{{$faker.commerce.productName}}. Priorize métodos estáveis (person, internet, string, commerce, number). - JavaScript:
{{$js.return ...}}ou expressões inline para composições, filtros e conversões. - Anchors YAML: use
&common_headers/<<: *common_headersdentro do mesmo documento e exporte o objeto viacapturese precisar reutilizá-lo em outro flow.
3.2 Headers Compartilhados Entre Flows
- No flow “setup”, capture o objeto de cabeçalhos e exporte:
capture: common_headers: "body.resolved_headers" exports: ["common_headers", ...] - Em flows dependentes, reconstrua manualmente usando as chaves exportadas:
variables: shared_common_headers: &shared_common_headers Accept: "{{shared_setup.common_headers.Accept}}" Content-Type: "{{shared_setup.common_headers.\"Content-Type\"}}" x-correlation-id: "{{shared_setup.common_headers.\"x-correlation-id\"}}" request: headers: <<: *shared_common_headers X-Feature-Flag: "{{feature_toggle}}"
4. Padrões de Step
| Situação | Componentes obrigatórios | Extras úteis |
|---|---|---|
| Request + Assert | request, assert.status_code, assert.body, capture | retry, metadata.tags, timeout, continue_on_failure |
| Input puro | input (single ou array) | condition, ci_default, style |
| Request + Input dependente | request, capture (gerando opções), input com selects encadeados | Novo step subsequente refinando dados (ver seção 5) |
| Iteração | iterate.over ou iterate.range + request | scenarios por iteração |
| Cenário condicional | scenarios + then/else | Capturas específicas por branch |
5. Seleções Dependentes e Re-captura (modelo Proposta → Estágio)
Use o blueprint abaixo para construir o padrão Proposta → Estágio dentro de um único flow:
- Request inicial captura opções com JMESPath, incluindo snapshots completos (
proposal_response_snapshot). - Inputs encadeados: o primeiro
selectescolhe a proposta; o segundo usa o resultado anterior na expressãooptions. - Reprocessamento:
capturecom{{$js: ...}}percorre o snapshot para retornar registros filtrados sem outra chamada. - Follow-up request: valida a escolha e captura detalhes adicionais.
# Step 1 — Request + opções + inputs encadeados
steps:
- name: "Seleciona proposta e estágio"
request:
method: GET
url: "/admin/proposals/dashboard"
capture:
proposal_total_count: "length(body.rows)"
proposal_selection_options: "body.rows[?flow.stages && length(flow.stages) > `0`].{value: to_string(id), label: join(' - ', [to_string(client.name), to_string(code), to_string(status)])}"
proposal_response_snapshot: "body"
input:
- prompt: |
Encontradas {{proposal_total_count}} propostas.
Escolha uma opção para continuar:
variable: proposal_choice_id
type: "select"
options: "proposal_selection_options"
required: true
- prompt: |
Proposta {{proposal_choice_id}} selecionada.
Agora escolha o estágio correspondente:
variable: stage_choice_id
type: "select"
options: "proposal_response_snapshot.rows[?id == `{{proposal_choice_id}}`].flow.stages[*].{value: to_string(id), label: join(' - ', [to_string(name), to_string(code)])} | [0]"
required: true
# Step 2 — Reprocessa snapshot localmente
- name: "Deriva dados escolhidos"
capture:
selected_proposal_from_snapshot: |-
{{js: (() => {
const dataset = variables.proposal_response_snapshot || {};
const rows = dataset.rows || [];
return rows.find(row => String(row.id) === String(variables.proposal_choice_id));
})() }}
selected_stage_from_snapshot: |-
{{js: (() => {
const snapshot = variables.selected_proposal_from_snapshot || {};
const stages = snapshot.flow?.stages || [];
return stages.find(stage => String(stage.id) === String(variables.stage_choice_id));
})() }}
6. Boas Práticas para IA ao Gerar Flows
- Narrativa consistente: mantenha descrições (
descriptionemetadata.description) claras e orientadas a negócio. - Nomes autoexplicativos:
node_id,step.namee variáveis devem refletir o contexto (ex.:reservation_id>id1). - Falhas controladas: use
continue_on_failure: trueonde a suíte deve prosseguir (ex.: limpeza), e mantenhafalsequando a etapa for gate crítico. - Retry consciente: adicione
retry.max_attemptsapenas em endpoints potencialmente instáveis. - Capturas úteis: sempre registre IDs, estados, payloads essenciais e snapshots quando a próxima etapa depender do mesmo corpo.
- Exports enxutos: exporte apenas o necessário; se algo for opcional, mova para
exports_optional. - Referência cruzada: documente dependências via
dependspara encadear flows que compartilham variáveis exportadas. - Validação: assegure
assert.bodypara campos críticos (comosuccess: { equals: true }) eassert.headersquando o contrato exigir.
7. Processo de Trabalho Recomendado para a IA
- Receber briefing: coletar requisitos do usuário (APIs envolvidas, dados necessários, entradas manuais, resultados esperados).
- Avaliar assets existentes: revisar o exemplo consolidado na seção “Apêndice A” e reutilizar os trechos aplicáveis.
- Planejar suite: desenhar steps em bloco, garantindo cobertura de setup → execução → validação → cleanup quando aplicável.
- Escrever YAML: produzir documento único com comentários claros, anotações de anchors e seções relevantes.
- Checagem estática: validar mentalmente (ou via lint interno da engine) se as expressões JMESPath e
{{$js}}estão coerentes com a estrutura de resposta esperada. - Orientar o usuário: junto do YAML, fornecer breve resumo das variáveis esperadas (envs, faker), dependências e próximos passos.
8. Snippets Prontos para Reuso
8.1 Cabeçalhos Compartilhados
variables:
shared_headers_template: &shared_headers
Accept: "application/json"
Authorization: "Bearer {{session_token}}"
steps:
- name: "Configura sessão"
...
capture:
shared_headers: "body.headers"
exports: ["shared_headers"]
8.2 Input com fallback de CI
input:
prompt: "Digite o limite de itens"
variable: "page_size"
type: "number"
default: 25
ci_default: 10
validation:
min: 1
max: 100
8.3 Expressão JS para filtrar snapshot
capture:
selected_item: |-
{{js: (() => {
const list = variables.items_snapshot || [];
return list.find(item => item.id === variables.item_id);
})() }}
9. Lista de Verificação Final
- Todos os steps têm
metadata.descriptiondescrevendo o objetivo. - Requests possuem
assert.status_codee validações mínimas de payload. - Inputs retornam variáveis utilizadas posteriormente ou exportadas.
- Variáveis reutilizadas entre flows estão exportadas corretamente.
- Comentários explicam decisões não óbvias (anchors, js complexos, dependências).
- Arquivo está formatado e organizado por blocos de comentários, se necessário.
10. Apêndice A — Exemplo Consolidado
Exemplo completo com três flows, cobrindo setup compartilhado, jornada principal com selects dependentes e cleanup resiliente. Use como referência imediata ao projetar novos artefatos.
---
node_id: "shared_setup"
suite_name: "Flow 1 – Configuração compartilhada"
description: "Constrói cabeçalhos base e variáveis globais"
metadata:
priority: "critical"
tags: ["setup", "shared", "headers"]
base_url: "{{$js.return env.CONFIG_BASE_URL || 'https://config.example.local'}}"
variables:
tenant_id: "{{$js.return env.DEFAULT_TENANT_ID || 'tenant-demo'}}"
store_code: "{{$faker.string.alphanumeric(6)}}"
correlation_id: "{{$js.return `corr-${Math.random().toString(16).slice(2,10)}`}}"
common_headers_template: &common_headers
Accept: "application/json"
Tenant-id: "{{tenant_id}}"
x-store-code: "{{store_code}}"
steps:
- name: "Captura cabeçalhos base"
metadata:
description: "Chama endpoint de configuração para exportar cabeçalhos"
request:
method: POST
url: "/internal/config/build-headers"
headers:
<<: *common_headers
X-Setup-Trace: "shared_setup"
body:
tenantId: "{{tenant_id}}"
storeCode: "{{store_code}}"
correlationId: "{{correlation_id}}"
assert:
status_code: 200
body:
resolved_headers:
type: "object"
notEmpty: true
capture:
common_headers: "body.resolved_headers"
correlation_id: "body.resolved_headers.\"x-correlation-id\""
continue_on_failure: false
exports: ["common_headers", "correlation_id", "tenant_id", "store_code"]
---
node_id: "catalog_and_proposals"
suite_name: "Flow 2 – Jornada principal"
description: "Busca catálogo, seleciona proposta/estágio e cria reserva"
metadata:
priority: "high"
tags: ["journey", "catalog", "proposals"]
base_url: "{{$js.return env.API_BASE_URL || 'https://api.demo.local'}}"
depends:
- node_id: "shared_setup"
required: true
variables:
tenant_id: "{{shared_setup.tenant_id}}"
correlation_id: "{{shared_setup.correlation_id}}"
shared_common_headers: &shared_common_headers
Accept: "{{shared_setup.common_headers.Accept}}"
Tenant-id: "{{shared_setup.common_headers.\"Tenant-id\"}}"
x-store-code: "{{shared_setup.common_headers.\"x-store-code\"}}"
account_token: "{{$env.ACCOUNT_TOKEN}}"
steps:
- name: "Itera catálogo"
metadata:
description: "Percorre páginas do catálogo para capturar itens"
iterate:
range: "1..2"
as: "page_index"
request:
method: GET
url: "/catalog/items"
headers:
<<: *shared_common_headers
X-Page-Index: "{{page_index}}"
params:
tenant: "{{tenant_id}}"
page: "{{page_index}}"
size: 20
assert:
status_code: 200
body:
items:
type: "array"
capture:
catalog_item_id: "body.items[0].id"
owner_ids: "body.items[*].owner_id"
- name: "Seleciona proposta e estágio"
metadata:
description: "Captura opções e executa selects encadeados"
request:
method: GET
url: "/admin/proposals/dashboard"
headers:
<<: *shared_common_headers
Authorization: "Bearer {{account_token}}"
assert:
status_code: 200
body:
rows:
type: "array"
notEmpty: true
capture:
proposal_total_count: "length(body.rows)"
proposal_selection_options: "body.rows[?flow.stages && length(flow.stages) > `0`].{value: to_string(id), label: join(' - ', [to_string(client.name), to_string(code), to_string(status), 'Stages:', to_string(length(flow.stages))])}"
proposal_response_snapshot: "body"
input:
- prompt: |
Encontradas {{proposal_total_count}} propostas. Escolha uma:
variable: proposal_choice_id
type: "select"
options: "proposal_selection_options"
required: true
- prompt: |
Proposta {{proposal_choice_id}} selecionada. Escolha o estágio:
variable: stage_choice_id
type: "select"
options: "proposal_response_snapshot.rows[?id == `{{proposal_choice_id}}`].flow.stages[*].{value: to_string(id), label: join(' - ', [to_string(name), to_string(code)])} | [0]"
required: true
- name: "Deriva dados da escolha"
metadata:
description: "Filtra snapshot localmente e confirma com nova chamada"
capture:
selected_proposal_from_snapshot: |-
{{js: (() => {
const dataset = variables.proposal_response_snapshot || {};
const rows = dataset.rows || [];
return rows.find(row => String(row.id) === String(variables.proposal_choice_id));
})() }}
selected_stage_from_snapshot: |-
{{js: (() => {
const snapshot = variables.selected_proposal_from_snapshot || {};
const stages = snapshot.flow?.stages || [];
return stages.find(stage => String(stage.id) === String(variables.stage_choice_id));
})() }}
request:
method: GET
url: "/admin/proposals/{{proposal_choice_id}}/stages/{{stage_choice_id}}"
headers:
<<: *shared_common_headers
Authorization: "Bearer {{account_token}}"
assert:
status_code: 200
body:
proposal:
type: "object"
capture:
proposal_stage_snapshot: "body"
continue_on_failure: false
- name: "Reserva item do catálogo"
metadata:
description: "Usa dados coletados para criar uma reserva"
request:
method: POST
url: "/catalog/reservations"
headers:
<<: *shared_common_headers
Authorization: "Bearer {{account_token}}"
body:
itemId: "{{catalog_item_id}}"
proposalId: "{{proposal_choice_id}}"
stageId: "{{stage_choice_id}}"
correlationId: "{{correlation_id}}"
assert:
status_code: 201
body:
reservationId:
exists: true
capture:
reservation_id: "body.reservationId"
exports: ["catalog_item_id", "owner_ids", "proposal_choice_id", "stage_choice_id", "reservation_id"]
---
node_id: "cleanup_flow"
suite_name: "Flow 3 – Limpeza"
description: "Remove dados artificiais e gera relatório"
metadata:
priority: "medium"
tags: ["cleanup", "report"]
depends:
- node_id: "shared_setup"
required: true
- node_id: "catalog_and_proposals"
required: true
variables:
cleanup_headers: &cleanup_headers
Accept: "application/json"
Tenant-id: "{{shared_setup.tenant_id}}"
x-store-code: "{{shared_setup.store_code}}"
steps:
- name: "Confirma limpeza"
metadata:
description: "Permite pular cleanup localmente"
input:
prompt: "Executar cleanup final?"
variable: "execute_cleanup"
type: "confirm"
default: true
ci_default: true
- name: "Remove usuários gerados"
metadata:
description: "Percorre IDs capturados na jornada"
iterate:
over: "{{catalog_and_proposals.owner_ids}}"
as: "user_id"
condition: "{{execute_cleanup}} == true"
request:
method: DELETE
url: "/users/{{user_id}}"
headers:
<<: *cleanup_headers
assert:
status_code:
greater_than: 199
less_than: 300
continue_on_failure: true
- name: "Resumo final"
metadata:
description: "Publica relatório consolidado"
request:
method: POST
url: "/reports/summary"
headers:
<<: *cleanup_headers
body:
flows: ["shared_setup", "catalog_and_proposals", "cleanup_flow"]
reservationId: "{{catalog_and_proposals.reservation_id}}"
correlationId: "{{shared_setup.correlation_id}}"
assert:
status_code: 201
Resumindo: a IA deve sempre produzir flows ricos, comentados, tecnicamente válidos e aproveitando ao máximo as capacidades da engine—capturas, inputs condicionais, reuso de variáveis e exportações. Use este guia como checklist para entregar artefatos prontos para execução ou rápida adaptação pelo time.