aicert.study
Trilha de Estudo/A Messages API é uma máquina de estados

A Messages API é uma máquina de estados

90 min

Recomendamos ver antes: Gaste capacidade onde o erro sai caro, Coloque cada fato no tipo certo de contexto

Objetivos da lição

  • Tratar stop_reason como sinal de controle, não como texto livre
  • Preservar os blocos de conteúdo necessários entre requisições
  • Lidar com erros, limites e cancelamento como estados de primeira classe

O problema

Um desenvolvedor implementa um loop de chamadas para a Messages API lendo o campo content da resposta e procurando, com uma expressão regular, algo que pareça um pedido de ferramenta em texto livre. Funciona nos testes manuais. Em produção, o comportamento fica instável: às vezes o loop continua quando deveria parar, às vezes para antes de terminar. O bug não está na regex — está em tratar a API como um gerador de texto livre quando ela na verdade é uma máquina de estados com sinais explícitos, e esses sinais estavam sendo ignorados.

stop_reason é sinal de controle, não texto para interpretar

Toda resposta da Messages API vem com um campo stop_reason que diz exatamente por que o modelo parou de gerar. Esse campo não é um detalhe cosmético — é a instrução de controle que decide o que sua aplicação deve fazer a seguir:

  • tool_use: o modelo quer executar uma ou mais ferramentas. A aplicação deve executá-las e devolver os resultados antes de continuar. Isso nunca deveria ser inferido lendo o texto da resposta em busca de um "pedido" — o SDK já entrega blocos de conteúdo estruturados do tipo tool_use, com nome da ferramenta e input já parseado.
  • end_turn: o modelo terminou seu turno normalmente. É o caso feliz, mas não é o único caso de parada válido.
  • Outros valores existem para limite de tokens atingido, sequência de parada customizada encontrada, entre outros. Uma implementação de produção precisa tratar todos eles como estados distintos — não só os dois mais comuns — porque cada um exige uma resposta diferente da aplicação (continuar, parar, truncar, alertar).

Tratar stop_reason como um sinal de controle explícito, em vez de tentar adivinhar a intenção do modelo a partir do texto solto, é a diferença entre um loop confiável e um loop que funciona "na maior parte das vezes".

Preservar o estado da conversa entre requisições

A Messages API não guarda estado entre chamadas — cada requisição carrega o histórico completo da conversa até aquele ponto. Isso significa que a aplicação é responsável por preservar os blocos de conteúdo necessários para a próxima requisição fazer sentido: a mensagem do assistente que pediu uma ferramenta, e o resultado dessa ferramenta, precisam aparecer na próxima requisição na ordem certa e associados ao tool_use_id correto.

Um erro comum é "resumir" a resposta anterior em texto livre antes de reenviá-la, achando que isso economiza tokens. Isso quebra a estrutura que o modelo espera — ele não recebe mais um histórico de conversa estruturado, recebe um resumo de terceira pessoa do que aconteceu, o que degrada a qualidade da continuação. Se o objetivo é reduzir tokens, a ferramenta certa é resumir fatos duráveis para fora da conversa (ver a lição sobre contexto e memória), não comprimir o formato estrutural que a API exige.

Erros, limites e cancelamento são estados de primeira classe

Uma implementação que só trata o caminho feliz — resposta chega, stop_reason é end_turn ou tool_use, tudo funciona — não está pronta para produção. Pelo menos três categorias de estado adicional precisam de tratamento explícito:

  • Erros de rede ou da API: timeout, erro 5xx, rate limit. A resposta correta depende do tipo de erro — um rate limit pede retry com backoff; um erro de validação de input não deveria ser reenviado sem correção, porque vai falhar de novo do mesmo jeito.
  • Limites atingidos: a resposta pode ser truncada por atingir o limite de tokens de saída antes de terminar o pensamento. Isso aparece no stop_reason, e ignorar esse sinal significa tratar uma resposta incompleta como se fosse completa.
  • Cancelamento: uma aplicação de produção real precisa de um caminho para interromper uma chamada em andamento (o usuário fechou a aba, uma operação foi cancelada por outro motivo). Isso não é um detalhe de UX — é parte do ciclo de vida da aplicação que precisa ser desenhado, não algo que "não vai acontecer".

Onde a intuição erra

  • "Posso detectar um pedido de ferramenta lendo o texto da resposta." O SDK já entrega isso estruturado via stop_reason: tool_use e blocos de conteúdo tipados — reimplementar isso com parsing de texto é reinventar, pior, algo que já existe.
  • "Resumir o histórico anterior em texto livre economiza tokens sem custo." Quebra a estrutura de blocos de conteúdo que a próxima requisição precisa, degradando a qualidade da continuação.
  • "Só preciso tratar end_turn e tool_use, o resto é raro." Erros de rede, limites de token e cancelamento acontecem em qualquer sistema de produção com volume real — tratar só o caminho feliz é adiar o problema, não eliminá-lo.

Coloque em prática

No laboratório desta lição você vai implementar uma máquina de estados simplificada que recebe uma sequência de respostas simuladas da Messages API (incluindo casos de tool_use, end_turn, limite atingido e erro) e decide a ação correta para cada uma — com testes que confirmam que nenhum estado é tratado como um caso genérico de "continuar".

Laboratório prático

Clone o repositório e rode localmente:

git clone https://github.com/aicertstudy/labs
cd labs/ccar-f/lessons/08-messages-api-and-application-lifecycle
Ver pasta no GitHub

Pronto pra testar de verdade?

Faça o simulado completo do CCAR-F, no mesmo formato da prova oficial.

Ver simulados

Checkpoint da lição

Carregando quiz...