CodeGym /Cursos /ChatGPT Apps /Contexto de Voice/Realtime: comportamento do App durante ...

Contexto de Voice/Realtime: comportamento do App durante a conversa por voz

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

1. Contexto: o que “modo de voz” significa para o ChatGPT App

Para começar, é importante entender que, dentro do ChatGPT Apps SDK, você não escreve seu próprio cliente de áudio, não controla o microfone e não faz o streaming do áudio por conta própria. Quem faz isso é o cliente do ChatGPT (aplicativo web ou móvel).

Pressupomos que você já tenha uma noção básica do widget, de callTool e do GiftGenius dos módulos anteriores — aqui observamos esses mesmos elementos pela perspectiva do modo de voz.

Do seu ponto de vista como desenvolvedor do App, tudo se parece com isto:

  • O usuário fala no microfone. O cliente do ChatGPT faz o reconhecimento de fala e envia texto ao modelo.
  • No stream, você “vê” o mesmo que veria se o usuário estivesse digitando, só que as mensagens chegam mais rápido e de forma mais “conversacional”.
  • O modelo responde com texto, que o cliente transforma em fala.
  • Ao mesmo tempo, o modelo pode chamar suas ferramentas (callTool), mudar o displayMode do widget, atualizar o widgetState e propor follow-ups — como no modo de texto.

A diferença crucial é que o usuário pode quase não olhar para a tela, ou apenas dar olhadas rápidas no telefone. Ou seja, seu UI deixa de ser o principal canal de interação e vira um complemento à voz, e não o contrário.

Disso decorrem duas consequências:

  • Tudo o que realmente importa deve ser compreensível “pelo ouvido”, por meio das falas do GPT.
  • O widget deve ser “glanceable” no bom sentido: com um olhar rápido já é possível ver o status e as opções principais, sem ler texto miúdo.

Para o nosso GiftGenius, isso já dá uma dica: o cenário “estou dirigindo, escolha um presente para minha mãe” não é apenas um chat de texto. É um diálogo multimodal, em que a voz conduz e o UI dá suporte.

2. Como o cenário de voice difere do de texto

Para não cair na armadilha de “é tudo a mesma coisa, só que o usuário fala”, vale comparar os modos de texto e de voz em alguns eixos.

Aspecto Modo de texto Modo de voz
Atenção do usuário Olha para a tela, lê, rola Pode nem olhar para a tela (hands‑free)
Forma das solicitações Mais estruturadas; a pessoa edita Conversacional, frases truncadas, “aham”, “manda mais”
Tolerância a pausas 1–2 segundos de silêncio são normais Silêncio prolongado incomoda
Papel do UI Principal portador de detalhes Auxiliar, um “painel” com apoio visual breve
Erros de entrada Erros de digitação, mas o texto é visível Fala indistinta, ruído, “sim/não” falsos

Daqui saem algumas conclusões importantes.

  • Não dá para pressupor que o usuário “vai ler no cartão”. O que é crítico deve ser falado: o que você entendeu, o que pretende fazer, qual resultado obteve.
  • O UI deve aguentar o cenário “olhar de 1 segundo”. Status, progresso, escolha principal — tudo deve estar visível em fonte grande. Detalhes são secundários.
  • É preciso preencher as pausas. Enquanto seu servidor MCP processa uma solicitação pesada, o modelo deve dizer o que está acontecendo e o widget deve mostrar progresso, para não dar a sensação de um assistente travado.

Você pode pensar no modo de voz como um audiolivro com ilustrações: há um narrador por voz (GPT) e há as “figuras” (o widget). É preciso sincronizá-los de modo que se complementem, e não que se repitam ou entrem em conflito.

3. Papel do widget no modo de voz: de “painel de controle” a “painel informativo”

No cenário de texto, o widget muitas vezes atua como uma interface completa: formulários, tabelas, carrosséis com filtros, botões de ação. No modo de voz, seu papel muda. As recomendações para interfaces multimodais e VUI mostram que, em cenários de voz, o UI se torna mais um painel informativo (glanceable UI): serve para verificações e confirmações rápidas, e não para um trabalho visual intenso.

Para o GiftGenius, isso significa o seguinte.

Quando o usuário percorre um assistente por voz, na tela (em um widget inline ou em fullscreen) mostramos:

  • Status grande: “Etapa 2 de 3: orçamento e tipo de presente”.
  • Mínimo de texto, mas rótulos claros: “Orçamento até 50 dólares”, “Preferência por presente digital”.
  • Alguns botões de CTA grandes, caso o cenário de voz permita cliques: “Alterar orçamento”, “Continuar”.
  • Uma única barra de progresso simples ou um stepper, e não dez indicadores pequenos.

Exemplo de “painel” simples em um widget inline para um cenário de voz (TypeScript + React, bastante simplificado):

type VoiceUiMode = "default" | "voiceGlance";

interface GiftStepProps {
  step: number;
  totalSteps: number;
  summary: string; // breve descrição do que já foi coletado
  uiMode: VoiceUiMode;
}

export function GiftVoiceStep(props: GiftStepProps) {
  const fontSize = props.uiMode === "voiceGlance" ? "text-lg" : "text-sm";

  return (
    <div className="rounded-xl border p-3 flex flex-col gap-2">
      <div className={`${fontSize} font-semibold`}>
        Etapa {props.step} de {props.totalSteps}
      </div>
      <div className={`${fontSize} text-muted-foreground`}>
        {props.summary}
      </div>
    </div>
  );
}

Não há nada de “voz” em si aqui, mas a ideia é clara: quando uiMode === "voiceGlance", deixamos tudo maior e mais simples. O sinal de que o modo de voz está ativo pode vir de diferentes lugares: de indícios indiretos até um flag explícito que o modelo define em widgetState ou na resposta de uma ferramenta.

4. Sincronização de modalidades: o que o GPT fala e o que o App mostra

O princípio-chave do voice‑UX para Apps é a sincronização de modalidades: a voz e o UI visual devem contar a mesma história, mas em níveis diferentes de detalhe.

Um erro comum é quando o desenvolvedor faz o modelo ler em voz alta tudo o que está no widget: listas longas de presentes, estruturas JSON com filtros etc. Isso vira uma tortura. As recomendações são: a voz dá um resumo curto e o UI mostra os detalhes.

Exemplo de sincronização adequada para o GiftGenius.

Usuário: “Escolha um presente para minha mãe; ela gosta de jardinagem, orçamento até 50 dólares.”

Modelo (em voz): “Encontrei algumas opções. A melhor, na minha opinião, é um kit de ferramentas para jardinagem por 45 dólares. Na tela, mostrei mais duas opções semelhantes. Quer que eu detalhe ou vamos direto à escolha?”

Widget (inline): mostra três cartões com presentes, uma descrição breve e botões de CTA “Selecionar” / “Mostrar semelhantes”.

Representação em pseudo‑JSON de uma etapa do diálogo (não é um protocolo real, apenas uma ilustração do raciocínio):

{
  "user": "Escolha um presente para minha mãe...",
  "assistant_text": "Encontrei algumas opções...",
  "widget": {
    "displayMode": "inline",
    "state": {
      "view": "gift_list",
      "items": [
        { "id": "g1", "title": "Kit de ferramentas de jardinagem", "price": 45 },
        { "id": "g2", "title": "Avental de jardinagem", "price": 30 },
        { "id": "g3", "title": "Kit de sementes de flores", "price": 20 }
      ]
    }
  }
}

Um detalhe importante: no system‑prompt você pode especificar explicitamente como o modelo deve falar sobre o UI, para não “ler JSON”: “Se você estiver mostrando uma lista de opções no widget, não leia cada item por completo. Descreva brevemente a melhor opção e diga que as demais estão visíveis na tela”.

No futuro, quando você trabalhar com o Realtime API e seus próprios clientes de voz, o princípio continuará o mesmo: o UI e o stream de áudio devem estar alinhados. A diferença é que lá você terá controle direto sobre o streaming.

5. Realtime e latência: como evitar silêncio constrangedor

Tecnicamente, os tool_calls no modo de voz são iguais aos do texto: o modelo decide chamar sua ferramenta, você retorna a resposta, o widget é atualizado. Mas, na voz, surge um novo problema de UX — a latência. Enquanto seu servidor MCP chama APIs externas ou calcula um relatório complexo, o usuário ouve… nada. E isso é percebido muito pior do que apenas esperar texto no chat.

Há duas camadas de proteção: a vocal e a visual.

  • No nível vocal, o system‑prompt deve permitir (e estimular) que o modelo diga “estou trabalhando” e faça perguntas adicionais enquanto a tool ainda está processando. Por exemplo: “Agora vou selecionar alguns presentes, isso deve levar alguns segundos. Enquanto isso, conte se há mais restrições”.
  • No nível visual, seu widget deve mostrar o progresso de forma muito clara: loader, status “Procurando opções…”, etapa atual. Sem isso, o usuário vai achar que tudo travou e começará a falar de novo, atrapalhando o fluxo de voz.

Na prática, isso é conveniente de resolver por meio de uma tarefa adiada: a ferramenta retorna imediatamente o status "pending" e um jobId, enquanto a seleção acontece em segundo plano. O widget, ao ver "pending", mostra o progresso, e a voz informa que está “trabalhando”.

Um esquema simples de uma ferramenta server‑side que devolve um “placeholder” com o job‑id, em vez de bloquear até o resultado final, pode ser assim:

// Pseudocódigo da ferramenta server-side do GiftGenius
export async function startGiftSearch(params: SearchParams) {
  const jobId = await createBackgroundJob(params); // coloca a tarefa na fila

  return {
    status: "pending",
    jobId,
    message: "Busca de presentes iniciada"
  };
}

Ao ver status: "pending", o widget pode alternar o UI para o modo de progresso:

if (toolOutput.status === "pending") {
  return (
    <div className="p-4 rounded-xl border flex items-center gap-3">
      <Spinner />
      <div className="text-base">
        Estou procurando presentes… Isso deve levar alguns segundos.
      </div>
    </div>
  );
}

E o modelo, em resposta a esse mesmo tool‑output, de acordo com as instruções, dirá em voz algo parecido e, possivelmente, fará uma pergunta adicional. Mais tarde, quando a tarefa em segundo plano terminar e, digamos, via uma MCP‑notification chegar um job.completed, o widget é atualizado para a lista de presentes e a voz anuncia o resumo.

Assim, obtemos um comportamento o mais próximo possível de realtime, mesmo que o backend não seja instantâneo.

6. Segurança e confirmações na voz

Interfaces de voz são traiçoeiras quando se trata de ações críticas: pagamento, exclusão de dados, alteração de configurações. O reconhecimento de fala não é perfeito, os usuários falam “em movimento” e um “aham” pode facilmente virar “sim, compre”. Por isso, em cenários de voz, os confirmation flows são especialmente importantes.

Há dois padrões básicos.

  • Confirmação de voz explícita (Explicit Voice Confirmation). Para ações perigosas, você exige uma frase específica. Por exemplo: “Para confirmar a compra, diga: ‘Confirmo a compra’” — e, no system‑prompt, você proíbe concluir o pagamento com “aham”, “ok”, “vamos lá” e outras expressões vagas.
  • Apenas confirmação visual (Visual Confirmation Only). O modelo conduz o usuário por voz até a ação (“Preparei o pedido; na tela estão o total e o conteúdo do carrinho”), mas o gatilho de fato é o clique no botão “Pagar” no widget. Isso é especialmente pertinente em cenários de comércio, e ainda voltaremos a isso no módulo 14.

Para o GiftGenius, isso pode ser assim.

Modelo: “Encontrei um ótimo kit para jardinagem por 45 dólares. Posso concluir a compra pelo ChatGPT. Na tela estão o preço final e o endereço de entrega. Para confirmar por voz, diga ‘Confirmo a compra’ ou toque no botão ‘Pagar’ na tela.”

Widget (fullscreen): mostra o pedido final, destaca em negrito o valor e o endereço e dois botões visíveis: “Pagar” e “Cancelar”.

No widget, você pode refletir o status da confirmação:

type CheckoutState = "review" | "waiting_voice_confirm" | "confirmed";

if (state.phase === "waiting_voice_confirm") {
  return (
    <div className="space-y-3">
      <h2 className="text-xl font-semibold">Quase pronto</h2>
      <p className="text-base">
        Confirme a compra por voz com a frase
        “Confirmo a compra” ou toque no botão “Pagar”.
      </p>
      <Button variant="primary">Pagar</Button>
      <Button variant="ghost">Cancelar</Button>
    </div>
  );
}

Dessa forma, se o modelo interpretar algo de forma incorreta na voz, o usuário ainda terá uma camada visual de “segurança”.

7. Comandos de voz simples e design de ferramentas

O usuário de voz não vai formular comandos exatamente como as variáveis da sua ferramenta. Ele dirá “escolha o primeiro”, “mostre mais baratos”, “sem coisas de tecnologia”. Sua tarefa é projetar ferramentas e o system‑prompt para que o modelo consiga mapear facilmente essas frases para chamadas das suas ferramentas (callTool).

Para o GiftGenius, você pode prever, por exemplo, estas ações:

  • Escolher uma das opções mostradas pelo índice ou id.
  • Ajustar o orçamento: “mais barato”, “até 30 dólares”.
  • Filtrar por tipo: “apenas presentes digitais”, “nada que precise de entrega via correio”.

Isso pode ser expresso de forma conveniente com uma ferramenta que tenha um parâmetro enum simples action e campos adicionais:

// Pseudoschema da ferramenta em TypeScript
type VoiceActionInput =
  | { action: "select_item"; itemId: string }
  | { action: "refine_budget"; maxPrice: number }
  | { action: "filter_type"; type: "digital" | "physical" };

export function handleVoiceAction(input: VoiceActionInput) {
  switch (input.action) {
    case "select_item":
      // marcar o presente como selecionado
      break;
    case "refine_budget":
      // recalcular a seleção para o novo orçamento
      break;
    case "filter_type":
      // filtrar a lista existente
      break;
  }
}

No system‑prompt, você descreve como essas ações se relacionam com os comandos de voz: “Se o usuário disser ‘escolha a primeira opção’, chame a ferramenta gift.voiceAction com action="select_item" e o identificador do primeiro presente na tela” etc.

Do ponto de vista de UX, isso reduz a carga cognitiva: o usuário não precisa inventar formulações exatas como “Ajuste os filtros para que sejam apenas presentes digitais até 30 dólares”. Ele fala de forma natural e o modelo traduz isso para uma estrutura de dados.

8. Cenário de voz do GiftGenius: três etapas

Vamos juntar tudo e projetar um cenário completo de voz para o GiftGenius, sem mergulhar no Realtime API de baixo nível por enquanto.

Imagine o usuário: ele está dirigindo e ativa o modo de voz do ChatGPT. Ele diz: “Por favor, escolha um presente para minha mãe; ela gosta de jardinagem, orçamento até 50 dólares”.

Etapa 1. Coleta de informações por voz

Modelo: “Perfeito, vamos escolher um presente. Vou esclarecer algumas coisas: para quando é o presente — para os próximos dias ou mais tarde? E há restrições, por exemplo, nada pesado ou volumoso?”

Widget (inline): por enquanto, apenas um pequeno painel com o status “Selecionando presente para: mãe, jardinagem, até 50 dólares”. As fontes ficam um pouco maiores do que o usual, para serem percebidas com um único olhar.

O estado do widget pode ser algo assim:

interface GiftSessionState {
  mode: "voice" | "text";
  step: 1 | 2 | 3;
  recipientSummary: string;
  budget?: number;
}

const [state, setState] = useState<GiftSessionState>({
  mode: "voice",
  step: 1,
  recipientSummary: "Mãe, gosta de jardinagem"
});

O backend, à medida que recebe as respostas do usuário, atualiza recipientSummary e budget, e o widget reage.

Etapa 2. Busca e espera

Depois que o modelo coletar informação suficiente, ele chama sua ferramenta de busca de presentes. Essa ferramenta, por sua vez, pode iniciar uma tarefa em segundo plano, caso a seleção seja complexa, e retornar status: "pending". Enquanto o plano de fundo trabalha, o modelo diz: “Agora vou procurar opções adequadas; isso deve levar alguns segundos. Enquanto isso, você pode dizer se ela prefere presentes físicos ou se valem certificados digitais”.

O widget alterna para um modo “semelhante a PiP”, se o usuário navega para outra parte da interface, ou permanece inline com progresso: “Procurando presentes…” e um pequeno indicador.

Etapa 3. Resultados e escolha

Quando os resultados estiverem prontos, o modelo: “Encontrei três opções. A primeira é um kit de ferramentas para jardinagem por 45 dólares. A segunda é um avental de jardinagem por 30 dólares. Mostrei na tela. Diga ‘escolha o primeiro’ ou ‘mostre mais baratos’”.

O widget mostra três cartões grandes com preços e descrições curtas. Cada cartão tem os CTAs “Selecionar” e “Semelhantes”. Além disso, há um botão separado “Mostrar mais opções”.

Se o usuário disser: “Escolha o segundo”, o modelo chama sua ferramenta voiceAction com action="select_item" e o id do segundo presente. O widget o destaca como selecionado, e o modelo diz: “Perfeito, escolhemos o avental de jardinagem por 30 dólares”.

Etapa opcional 4. Finalização

Se o App estiver integrado a pagamentos (no futuro, no módulo 14), começa o checkout. O modelo fala as condições e pede confirmação por voz ou botão. O widget muda para um assistente em fullscreen com as etapas “Revisão do pedido” → “Endereço de entrega” → “Confirmação”.

É importante que, em cada etapa, tudo o que é chave seja dito por voz, e o widget sirva de apoio visual, especialmente se o usuário já parou e está olhando para a tela.

9. Notas práticas de implementação e limites do Apps SDK

Todas as etapas descritas para o GiftGenius são implementadas dentro de um ChatGPT App comum — sem cliente de áudio próprio e sem WebRTC. Aqui é importante lembrar os limites da stack.

É muito fácil “cair” nos temas do Realtime API, WebRTC, streaming de áudio e começar a imaginar sua própria plataforma de voz. Há um módulo 20 do curso só para isso. Nesta aula, é importante lembrar os limites do ChatGPT App dentro do cliente do ChatGPT.

Na arquitetura atual:

  • O fluxo de áudio é gerenciado pelo cliente do ChatGPT. Você não envia nem recebe bytes de áudio no widget.
  • No backend, você ainda vê chamadas de ferramenta e mensagens de texto comuns, mas o modelo pode estar no modo de voz, e suas respostas serão narradas.
  • A plataforma pode fornecer indícios indiretos de que o modo de voz está ativo (via user-agent ou campos de ambiente). Mas não se deve criar uma dependência rígida disso: a API pode mudar e seu App deve continuar útil também no modo apenas de texto.

Portanto, uma boa estratégia de implementação é: primeiro projetar um UX que funcione bem tanto para texto quanto para voz — status curtos, CTAs claros, estágios de progresso compreensíveis. Depois, adicionar alguns aprimoramentos para voz: fontes um pouco maiores no modo "voiceGlance", progresso mais evidente, foco em status como “Etapa 2 de 3” e estados óbvios como “Aguardando confirmação”.

Além disso, no system‑prompt você descreve o comportamento de voz do modelo: como ele comenta o estado do widget, que frases usa para confirmações, quais palavras evita (por exemplo, não ler JSON, não narrar cada detalhe da lista).

Se mais tarde você fizer seu próprio Custom Voice Client com o Realtime API, todas essas decisões de UX migrarão sem problemas. A diferença estará apenas no nível de acesso a eventos e ao streaming, não nos princípios.

10. Erros típicos ao trabalhar com o contexto Voice/Realtime

Erro nº 1: “Ler o UI em voz alta” em vez de um sumário.
Às vezes, desenvolvedores criam ferramentas de modo que o modelo passe a ler em voz alta todo o conteúdo da resposta em JSON ou a lista completa de cartões. No modo de voz, isso destrói o UX: o usuário perde o fio da conversa e você gasta tokens. É melhor que a voz forneça um resumo curto e foque em uma ou duas opções, deixando o restante na tela.

Erro nº 2: Ausência total de feedback visual na voz.
É tentador pensar: “Se o usuário está falando, então ele está ouvindo; o UI é desnecessário”. Na prática, o usuário muitas vezes dá uma olhada na tela ou volta a ela depois de um minuto. Se nesse momento não houver status, progresso ou um resultado claro, ele achará que o App travou ou não fez nada. Mostre “Estou pensando”, “Etapa 2 de 3”, “Resultados prontos” etc.

Erro nº 3: Ações perigosas sem confirmação forte.
No modo de texto, já é arriscado fazer “Pagar” com um clique; no modo de voz, é ainda mais arriscado concluir uma compra com um “aham” vago. Ignorar fluxos explícitos de confirmação (vocais e/ou visuais) leva a compras incorretas e a problemas de confiança no App. Planeje quais ações exigem dupla confirmação e descreva isso explicitamente no system‑prompt e no UI.

Erro nº 4: Focar apenas no olho, e não no ouvido.
Às vezes, o App é projetado como se o usuário sempre estivesse lendo: frases muito complexas, botões longos, descrições carregadas. No modo de voz, tudo isso também precisa ser falado — vira uma “sopa de palavras”. Procure condensar o sentido principal em frases curtas e simples, fáceis de compreender pelo ouvido.

Erro nº 5: Confundir o Apps SDK com um cliente de voz próprio.
Alguns alunos começam a procurar no Apps SDK eventos de microfone, streaming de áudio, WebRTC etc., como no Realtime API, e se frustram por “nada disso existir”. É crucial entender: o ChatGPT App vive dentro do cliente do ChatGPT, e a voz é gerenciada pela plataforma. Você trabalha com texto, chamadas de ferramentas e estado do widget, e projeta o UX para que o modo de voz “simplesmente funcione bem”. Se você precisa de controle total sobre a voz, é outro projeto, mais complexo, com o Realtime API.

Erro nº 6: Falta de estratégia para lidar com latência.
Se você não planejar o que o modelo fala e o que o widget mostra durante operações longas, o usuário irá interromper, fazer novas perguntas e quebrar seu fluxo. A latência na voz é sentida mais fortemente do que no texto. Use status intermediários, processamento em segundo plano e falas do tipo “estou pensando; enquanto isso, conte…” para que o silêncio não vire um bug.

1
Pesquisa/teste
UX e interfaces, nível 8, lição 4
Indisponível
UX e interfaces
UX e interfaces (Inline, Fullscreen, Voice)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION