CodeGym /Cursos /ChatGPT Apps /Análise de UX do workflow: medindo a eficácia das etapas

Análise de UX do workflow: medindo a eficácia das etapas

ChatGPT Apps
Nível 11 , Lição 4
Disponível

1. Por que medir o workflow

Em resumo: sem análise você vive no modo “acho”, e não no modo “eu sei”.

Nas aulas anteriores deste módulo, já dividimos o cenário em etapas, atribuímos papéis ao GPT, ao widget e ao MCP, discutimos tool‑gating e o armazenamento de estado entre as etapas. Agora, veremos a mesma construção pelo olhar da análise: será que tudo funciona como planejado e onde os usuários realmente ficam travados.

Na web tradicional, todos já estão acostumados a funis: quantas pessoas chegaram à landing page, quantas colocaram o produto no carrinho, quantas chegaram ao pagamento. No ChatGPT App é a mesma coisa, só que em vez de páginas você tem etapas do workflow, e em vez de “clique no botão Comprar” — uma combinação de fala do usuário, chamada de ferramenta e interação com o widget.

Quando você constrói um cenário complexo sem métricas, você não vê:

  • em qual etapa as pessoas mais “desistem”;
  • onde elas ficam paradas e leem por um minuto (ou simplesmente foram pegar um café e não voltaram);
  • qual etapa não traz benefício nenhum e só irrita;
  • como mudanças em prompts ou tool gating afetam o comportamento.

O objetivo da análise por etapas é simples: aprender a aumentar a parcela de cenários concluídos, reduzir o tempo até o resultado e diminuir a quantidade de erros e de chamados ao suporte.

A partir de agora, o workflow não é apenas um objeto arquitetural e não só uma missão de UX. É também algo mensurável, com números.

2. O funil do cenário no ChatGPT App

Na web clássica, o funil é linear: Landing → Product → Cart → Checkout. No ChatGPT App o desenho é um pouco mais animado: o usuário pode “pular” uma etapa com palavras, o modelo às vezes pode ignorar uma etapa, e o widget e o texto do diálogo podem ficar dessincronizados.

Ainda assim, a ideia básica é a mesma: temos uma sequência de etapas, e em cada uma delas parte dos usuários segue adiante e parte — não.

Vamos pegar o nosso GiftGenius:

  1. collect_recipient — o ChatGPT e o widget coletam dados básicos sobre o destinatário (sexo, idade, relação, interesses).
  2. collect_budget — ajustamos o orçamento e a moeda.
  3. suggest_ideas — o MCP/agente seleciona ideias e retorna cards de presentes no widget.
  4. review_selection — o usuário curte/oculta ideias e escolhe 1–2 favoritas.
  5. checkout — cria‑se um commerce intent e o pedido é finalizado.

Em forma de funil, dá para desenhar assim:

flowchart TD
    A[Início do workflow] --> B["1\. Destinatário"]
    B --> C["2\. Orçamento"]
    C --> D["3\. Ideias de presente"]
    D --> E["4\. Escolha do presente"]
    E --> F["5\. Checkout"]

Mas é importante lembrar: o usuário pode escrever no chat “Vamos direto para o pagamento” ou “Mostre primeiro as opções caras”, e o modelo pode decidir pular parte das etapas. Portanto, a análise por etapas no ChatGPT App não é só sobre telas de UI, mas também sobre o comportamento do modelo: quais etapas são realmente percorridas, em que ordem e quem iniciou a transição — o usuário, o widget ou o GPT.

3. Métricas básicas por etapa

Vamos começar com o básico da análise de produto e adaptá-lo um pouco para o ChatGPT App.

Para cada workflow, precisamos de pelo menos quatro indicadores básicos.

Para conveniência, vamos reuni-los em uma tabela:

Métrica O que significa Pergunta típica
Start rate Quantos usuários iniciaram o cenário Nosso App está sendo mostrado para alguém?
Completion rate Quantos usuários chegaram ao final Até que ponto o cenário leva ao resultado?
Conversion per step Proporção de usuários que passaram da etapa N para N+1 Em qual etapa exatamente temos um “vazamento”?
Drop-off per step Proporção de usuários que abandonaram na etapa N Em qual etapa as pessoas desistem com mais frequência?

Quase sempre adicionamos métricas de esforço:

  • tempo médio por etapa (onde as pessoas “travam”);
  • quantidade de interações na etapa (quantas mensagens/cliques foram necessários);
  • parcela de etapas que terminaram com erro ou exigiram nova tentativa.

No contexto de cenários com LLM, entram coisas ainda mais específicas, como a precisão na escolha da ferramenta pelo modelo ou a parcela de respostas “alucinatórias” em uma etapa específica, mas isso é assunto avançado, chegaremos lá nos módulos finais.

Para cenários de comércio, sobre as etapas adicionam‑se métricas de negócio:

  • conversão em pagamento a partir do início do workflow;
  • conversão em pagamento a partir de uma etapa específica (por exemplo, de “seleção de ideias”);
  • ticket médio;
  • parcela de cancelamentos/devoluções.

Importante: todos esses números não existem isoladamente; há causalidade entre eles. Uma etapa com grande drop‑off nem sempre é ruim: talvez ela filtre usuários não adequados e só seguem adiante aqueles para quem o cenário é realmente útil. Portanto, análise não é apenas “contar porcentagens”, mas saber contar uma história com dados.

Para que todas essas porcentagens e funis possam ser calculados, precisamos de eventos brutos: quem, quando e qual etapa passou (ou não passou). Na próxima seção, vamos combinar um formato para esses eventos.

4. Como são os eventos de análise

Antes de escrever código, precisamos combinar o formato do “evento” que enviaremos do widget e do backend.

Normalmente, um evento de análise contém:

  • quem: o identificador do usuário ou pelo menos da sessão;
  • qual workflow e qual versão dele;
  • qual etapa;
  • o que aconteceu (tipo do evento);
  • se foi bem‑sucedido, quanto tempo levou;
  • um pouco de metadados (localidade, dispositivo etc.).

Um esquema simplificado de eventos para workflow pode ser descrito assim:

export type WorkflowEventType =
  | "workflow_started"
  | "workflow_finished"
  | "step_started"
  | "step_completed"
  | "step_failed";

export interface WorkflowAnalyticsEvent {
  eventId: string;              // uuid
  timestamp: string;            // string no formato ISO
  userId?: string;              // se for possível desanonimizar
  conversationId?: string;      // id do diálogo do ChatGPT (se disponível)
  workflowId: string;           // nosso identificador interno
  workflowType: "gift_selection";
  workflowVersion: string;      // por exemplo, "1.2.0" ou "1.2.0-A"
  stepName?: string;            // collect_budget, suggest_ideas etc.
  eventType: WorkflowEventType;
  toolName?: string;            // se relacionado a tool-call
  success?: boolean;
  errorCode?: string | null;
  durationMs?: number;
  metadata?: Record<string, unknown>;
}

Alguns nuances:

Primeiro, workflowVersion é muito importante se você pretende fazer testes A/B: sem ela você nunca saberá qual variante do cenário entrega os melhores números.

Segundo, conversationId ou outro correlation ID permite correlacionar eventos: etapas no widget, chamadas de ferramenta no MCP e o diálogo em texto. Em módulos seguintes ainda falaremos de tracing e observabilidade, mas o hábito de pensar desde o início em identificadores “de ponta a ponta” é muito útil.

Terceiro, não precisa colocar no evento tudo o que aparece: textos completos de mensagens, e‑mail, endereços e outros PII é melhor evitar ou anonimizar rigidamente — falaremos mais sobre isso perto do final.

5. Instrumentação no widget (Next.js + Apps SDK)

Agora, a parte mais interessante: como fazer o nosso widget GiftGenius reportar silenciosamente as etapas enquanto o usuário percorre o cenário.

Vamos supor que, nas aulas anteriores, você tenha feito algo como:

// components/GiftWizard.tsx
type StepId = "recipient" | "budget" | "ideas" | "review" | "checkout";

export function GiftWizard() {
  const [currentStep, setCurrentStep] = useState<StepId>("recipient");
  const [workflowId] = useState(() => crypto.randomUUID());

  // ... renderização das diferentes etapas
}

Vamos adicionar uma pequena “camada de análise” na forma de um hook.

Hook useWorkflowAnalytics

Vamos criar um wrapper que conhece workflowId, workflowVersion e sabe enviar um evento para a nossa rota de API do Next.js /api/workflow-analytics.

// lib/useWorkflowAnalytics.ts
import { useCallback } from "react";
import type { WorkflowAnalyticsEvent, WorkflowEventType } from "./types";

const WORKFLOW_VERSION = "1.0.0";

export function useWorkflowAnalytics(
  workflowId: string,
  workflowType: WorkflowAnalyticsEvent["workflowType"] = "gift_selection"
) {
  const sendEvent = useCallback(
    async (payload: Omit<WorkflowAnalyticsEvent, "eventId" | "timestamp" | "workflowType" | "workflowVersion" | "workflowId">) => {
      const event: WorkflowAnalyticsEvent = {
        eventId: crypto.randomUUID(),
        timestamp: new Date().toISOString(),
        workflowId,
        workflowType,
        workflowVersion: WORKFLOW_VERSION,
        ...payload,
      };

      // envio simples para a API; em produção você pode adicionar buffer/debounce
      await fetch("/api/workflow-analytics", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(event),
      });
    },
    [workflowId, workflowType]
  );

  const trackStepEvent = useCallback(
    async (stepName: string, eventType: WorkflowEventType, extra?: Partial<WorkflowAnalyticsEvent>) => {
      await sendEvent({ stepName, eventType, ...extra });
    },
    [sendEvent]
  );

  return { sendEvent, trackStepEvent };
}

O importante aqui é que o hook não depende de uma etapa específica de UI. Ele simplesmente sabe o que é stepName e eventType. Componentes específicos irão dizer a ele: “comecei a etapa”, “terminei a etapa” e assim por diante.

Enviando workflow_started e workflow_finished

No componente GiftWizard, no momento de montagem e desmontagem, podemos registrar o início e a conclusão do cenário:

// components/GiftWizard.tsx
export function GiftWizard() {
  const [currentStep, setCurrentStep] = useState<StepId>("recipient");
  const [workflowId] = useState(() => crypto.randomUUID());
  const { sendEvent } = useWorkflowAnalytics(workflowId);

  useEffect(() => {
    void sendEvent({ eventType: "workflow_started" });
    return () => {
      void sendEvent({ eventType: "workflow_finished" });
    };
  }, [sendEvent]);

  // ...
}

Claro, encerrar no unmount é uma aproximação grosseira: o usuário pode simplesmente minimizar o chat ou ir para outro diálogo. Mas mesmo essa métrica grosseira já dá uma noção de quantos cenários “chegaram a algum lugar”.

Rastreando eventos por etapa

Agora, vamos fazer com que cada etapa relate a si mesma na análise. Para começar, adicionamos um wrapper simples:

interface StepProps {
  stepId: StepId;
  onNext: () => void;
  trackStepEvent: (stepName: string, eventType: WorkflowEventType, extra?: Partial<WorkflowAnalyticsEvent>) => Promise<void>;
}

function StepRecipient({ stepId, onNext, trackStepEvent }: StepProps) {
  useEffect(() => {
    void trackStepEvent(stepId, "step_started");
  }, [stepId, trackStepEvent]);

  const handleSubmit = async () => {
    // ... validação, salvar no widgetState
    await trackStepEvent(stepId, "step_completed");
    onNext();
  };

  return (
    <div>
      {/* campos do formulário do destinatário */}
      <button onClick={handleSubmit}>Avançar</button>
    </div>
  );
}

Em GiftWizard, passamos o trackStepEvent:

export function GiftWizard() {
  // ...
  const { trackStepEvent } = useWorkflowAnalytics(workflowId);

  const goToNext = () => {
    setCurrentStep((prev) => NEXT_STEP[prev]);
  };

  if (currentStep === "recipient") {
    return (
      <StepRecipient
        stepId="recipient"
        onNext={goToNext}
        trackStepEvent={trackStepEvent}
      />
    );
  }

  // demais etapas...
}

Da mesma forma, nas etapas onde há possíveis erros (por exemplo, uma chamada a API externa em suggest_ideas), você pode, em caso de falha, enviar "step_failed" com errorCode, e em caso de carregamento bem‑sucedido das opções — "step_completed".

Assim, obtemos:

  • uma lista clara de eventos: quando as etapas começam e terminam;
  • a possibilidade de calcular o tempo da etapa: a diferença entre "step_started" e "step_completed";
  • visibilidade de quais etapas mais frequentemente terminam em "step_failed".

6. Instrumentação no backend / MCP

Análise no cliente é bom, mas o widget vive em um mundo bastante frágil: navegador do usuário, iframe, limitações de sandbox e tudo que vem junto. Por isso, em paralelo, vale registrar eventos no lado do servidor — nas ferramentas MCP ou no backend‑API do seu App.

Por exemplo, você tem a ferramenta suggest_gifts, que realmente faz o trabalho pesado: acessa o product feed, aplica filtros e retorna presentes. Dentro de uma ferramenta assim, você pode registrar tanto a lógica de negócio quanto os eventos de análise.

Um handler hipotético de MCP‑tool em TypeScript pode ser assim:

// mcp/tools/suggestGifts.ts
import type { SuggestGiftsArgs } from "../schemas";
import { logWorkflowEvent } from "../analytics/log";

export async function handleSuggestGifts(args: SuggestGiftsArgs, context: { workflowId: string; stepName: string }) {
  const startedAt = Date.now();

  try {
    // ... lógica principal de seleção de ideias

    await logWorkflowEvent({
      workflowId: context.workflowId,
      workflowType: "gift_selection",
      workflowVersion: "1.0.0",
      stepName: context.stepName,
      eventType: "step_completed",
      toolName: "suggest_gifts",
      success: true,
      durationMs: Date.now() - startedAt,
    });

    return {
      content: [{ type: "text", text: "Encontrei 5 ideias de presentes." }],
      _meta: {
        // dados brutos para o widget
      },
    };
  } catch (e) {
    await logWorkflowEvent({
      workflowId: context.workflowId,
      workflowType: "gift_selection",
      workflowVersion: "1.0.0",
      stepName: context.stepName,
      eventType: "step_failed",
      toolName: "suggest_gifts",
      success: false,
      errorCode: "SUGGEST_FAILED",
      durationMs: Date.now() - startedAt,
    });
    throw e;
  }
}

E o logWorkflowEvent pode escrever na mesma tabela/armazenamento onde ficam os eventos do front, apenas com a marca "source": "backend".

Por que a análise no servidor é mais confiável

Primeiro, a chamada de ferramenta ou aconteceu, ou não — é um fato claro, não uma heurística do tipo “o usuário aparentemente clicou no botão”.

Segundo, no servidor é mais fácil agregar dados: você pode contabilizar quantas vezes cada ferramenta foi chamada, qual o durationMs médio e qual a parcela de chamadas que termina em erro.

Terceiro, assim você vê a diferença entre um problema de UX (o usuário não chega à etapa onde a ferramenta é chamada) e um problema técnico (chegam à etapa, mas a ferramenta falha com frequência).

7. Como ler os dados: encontrando gargalos

Suponha que você já tenha implementado o envio de eventos "workflow_started", "step_started", "step_completed" e "step_failed" do widget e do MCP, e que no armazenamento já existam dados suficientes. Vamos imaginar que você já coletou uma certa quantidade de dados do GiftGenius e obteve estatísticas resumidas por etapa. A tabela abaixo traz números hipotéticos para 1000 workflows iniciados:

Etapa Inícios da etapa Etapa concluída Drop‑off na etapa Tempo médio (s)
recipient 1000 950 5 % 12
budget 950 700 26 % 35
ideas 700 680 3 % 8
review 680 500 26 % 40
checkout 500 420 16 % 20

O que vale observar aqui:

Primeiro, a etapa budget é um gargalo óbvio. Alto drop‑off (26 %) e tempo médio visivelmente maior. Talvez você esteja perguntando demais sobre moedas/impostos, as formulações não sejam claras, ou as pessoas simplesmente não tenham certeza do orçamento. É um bom candidato para simplificar a etapa, dividi‑la em dois subpassos ou mudar a formulação das perguntas.

Segundo, review também causa bastante abandono. Talvez o UI dos cards de presente esteja sobrecarregado, ou o usuário não entenda o que significa “curtir” um presente. Pode ser que o modelo retorne opções demais e o widget pareça uma lista interminável. Aqui vale olhar não só os números, mas também screenshots/gravações de sessão (se você as faz), ou ao menos percorrer o cenário manualmente como um usuário.

Terceiro, checkout perde 16 % — para um cenário de comércio, isso é muito dinheiro. Mas é preciso entender onde se perde: no fechamento do pedido, em erros do provedor de pagamento, ou porque o usuário simplesmente desistiu. Isso já não é uma questão puramente de UX, mas a soma de UX + restrições de negócio.

É importante saber distinguir problemas de UI e de modelo.

  • Se os usuários voltam com frequência à etapa anterior e mudam respostas — é um sinal de pergunta confusa ou mal formulada.
  • Se a etapa termina rapidamente, mas a chamada de ferramenta nela falha com frequência — é problema do backend/MCP.
  • Se a etapa dura muito e ao mesmo tempo não há erros nem retornos, talvez o usuário esteja apenas lendo um textão que não é muito necessário.

8. Experimentos e testes A/B de workflow

Números, por si só, não melhoram nada. Para que a análise seja útil, é preciso saber conduzir experimentos: mudar etapas e comparar se ficou melhor.

No contexto do ChatGPT App, o experimento típico é comparar duas versões de uma etapa ou da sequência de etapas:

  • um wizard longo com várias telas simples versus um formulário único e complexo;
  • formulações diferentes das perguntas;
  • ordem diferente das etapas (por exemplo, perguntar o orçamento antes ou depois);
  • estratégias diferentes de tool gating (menos tools na primeira etapa, mais na segunda).

Um bom hábito é fixar a versão do cenário em workflowVersion e adicionar o identificador do experimento, por exemplo "1.3.0-A" e "1.3.0-B".

Split A/B mais simples no widget

Claro que, em produção, você vai querer um assignment estável no nível do usuário ou da sessão (via backend), mas para um exemplo didático basta escolher a variante aleatoriamente.

// lib/useWorkflowVariant.ts
import { useMemo } from "react";

export type WorkflowVariant = "A" | "B";

export function useWorkflowVariant(): WorkflowVariant {
  return useMemo(() => {
    return Math.random() < 0.5 ? "A" : "B";
  }, []);
}

No GiftWizard, definimos a variante e a repassamos à análise:

export function GiftWizard() {
  const [workflowId] = useState(() => crypto.randomUUID());
  const variant = useWorkflowVariant();
  const { sendEvent, trackStepEvent } = useWorkflowAnalytics(
    workflowId,
    "gift_selection"
  );

  useEffect(() => {
    void sendEvent({
      eventType: "workflow_started",
      metadata: { variant },
    });
  }, [sendEvent, variant]);

  // depois, você pode mudar textos/estrutura das etapas conforme a variant
}

No servidor, você pode substituir o rígido "1.0.0" por algo como "1.1.0-A" e "1.1.0-B", ou simplesmente registrar metadata.variant e, na análise, agrupar por ele.

O principal sentido de um teste A/B: escolher a métrica objetivo com antecedência. Por exemplo: “queremos aumentar o completion rate do cenário de 42% para 50%” ou “reduzir o tempo na etapa budget em 20%”. Sem uma métrica objetivo, qualquer reestruturação do workflow vai parecer uma reforma do tipo “mudamos o armário de lugar, parece que ficou mais bonito”.

9. Privacidade e ética de dados

Já mencionamos de passagem que, em metadata e nos eventos de análise, é melhor não enviar PII de forma direta. Enquanto discutimos métricas, é fácil se empolgar e começar a registrar tudo. Mas lembre‑se de que você trabalha dentro do ChatGPT e o usuário pode razoavelmente esperar que suas mensagens pessoais não sejam enviadas, em bruto, para uma análise externa.

Algumas regras simples, que valem a pena seguir desde já, antes dos módulos sobre segurança e Store:

  • Primeiro, não registre os textos completos das mensagens do usuário. Em vez disso, você pode salvar o comprimento da mensagem, o tipo de resposta (número, “sim/não”, escolha em lista) ou sinais anonimizados como “resposta vazia/incompleta/alterada”.
  • Segundo, não registre informações claramente identificáveis (PII) se elas não forem necessárias para a lógica de negócio: e‑mail, telefones, endereços, nomes completos. Se for inevitável, armazene em outro domínio protegido e restrinja rigidamente o acesso.
  • Terceiro, trate o contexto do diálogo com cuidado. Se você salva o conversationId, garanta que, na análise, você não tente “juntar” diálogos em super‑perfis sem motivo legítimo e base legal.
  • Quarto, atenção às políticas da OpenAI e aos requisitos do Store (vamos tratar disso em detalhes nos módulos sobre publicação e segurança), que explicitam quais dados podem ou não ser levados para fora do ChatGPT. Na fase de projeto da análise, é útil já prever anonimização e minimização de dados para não ter que reescrever meio sistema depois.

E, por fim, lembre‑se de que análise de UX não é vigilância total e certamente não é surveillance ao estilo Big Brother. O objetivo é melhorar cenários e reduzir a frustração do usuário, não criar um painel Big Brother do tipo “quem às 2:37 da madrugada não chegou ao checkout”.

10. Erros comuns na análise de UX de workflow

Erro nº 1: “Ainda não estamos em produção, as métricas ficam para depois”.
Muitas vezes, os desenvolvedores começam a pensar na análise quando o App já está sendo usado por pessoas reais. Como resultado, os eventos são introduzidos “depois do fato”, os dados ficam fragmentados e comparar versões antiga e nova do cenário se torna quase impossível. É melhor planejar um funil mínimo ("workflow_started", "step_started", "step_completed", "workflow_finished") desde o início, enquanto o código ainda é relativamente simples.

Erro nº 2: Logar apenas sucessos e ignorar falhas.
Às vezes, nos logs há apenas "step_completed", e ninguém registra "step_failed" porque “isso não deveria falhar”. Como resultado, você vê que pouca gente chega a certa etapa, mas não entende se saíram por vontade própria ou se foram expulsos por erros. Sempre registre tanto a conclusão bem‑sucedida quanto a malsucedida, com pelo menos um errorCode grosseiro.

Erro nº 3: Ausência total de vínculo à versão do workflow.
Você muda textos, a ordem das etapas, introduz tool gating, e nos eventos a workflowVersion segue sendo "1.0.0". Um mês depois você olha os gráficos e não consegue entender o que foi antes das mudanças e o que foi depois. Fixar a versão do cenário e, se necessário, a variante A/B — é um elemento obrigatório da análise.

Erro nº 4: Análise detalhada demais sem motivo.
O extremo oposto — tentar construir desde o início o “esquema perfeito” de eventos com 50 campos, registrar cada clique em cada pixel e cada caractere digitado. Primeiro, isso pode violar a privacidade. Segundo, esses dados são difíceis de analisar e você vai se afogar em ruído. É melhor começar com um conjunto pequeno de eventos e métricas que realmente respondam a perguntas de produto específicas, e depois evoluir o sistema conforme a necessidade.

Erro nº 5: Inconsistência nos nomes das etapas e dos cenários.
Às vezes, no código a etapa se chama budget, na análise — collect_budget, e no relatório — “etapa com a pergunta sobre dinheiro”. Em poucas semanas, ninguém lembra o que é o quê. Na fase de projeto do workflow, é útil combinar identificadores estáveis das etapas (stepName) e usá‑los tanto no UI quanto nos logs e nos relatórios.

Erro nº 6: Ter métricas que ninguém usa.
A história mais triste: você coletou cuidadosamente um monte de dados, configurou o envio de eventos do widget e do MCP, mas ninguém abre os dashboards nem toma decisões. Análise por análise não serve; sempre se pergunte: “Que decisão eu consigo tomar com base nesta métrica?”. Se não houver resposta — você ainda não precisa dessa métrica.

1
Pesquisa/teste
Workflow, nível 11, lição 4
Indisponível
Workflow
Workflow e revelação gradual de ferramentas
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION