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 tipotool_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_usee 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_turnetool_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".