Seu teste passou na segunda-feira. Mesma entrada, mesmo código, temperature=0. Na terça-feira, falhou, e você não mudou nada. A asserção verificava uma string exata, e o modelo retornou a mesma resposta, mas formulada de forma um pouco diferente. O teste está vermelho, o agente está bem, e agora você está depurando sua suíte de testes em vez do seu produto.
Este é o preço a pagar por testar qualquer coisa que chame um modelo de linguagem. A saída se move, mesmo quando você disse para não fazer isso. Defina a temperatura como zero e você ainda não obterá respostas byte a byte idênticas em diferentes execuções. A maioria dos desenvolvedores aprende isso da maneira difícil, uma vez, e então reescreve como eles testam. Este guia mostra como escrever asserções que se mantêm quando o texto subjacente continua mudando. É um mergulho profundo no modo de falha três do nosso guia sobre por que agentes de IA falham em produção.
Por que temperature=0 não significa determinístico
A temperatura controla como o modelo amostra o próximo token. Em zero, ele pega o token mais provável todas as vezes, o que parece que deveria ser reproduzível. Não é, e as razões estão abaixo do modelo.
A matemática de ponto flutuante não é associativa em uma GPU. Adicione os mesmos números em uma ordem diferente e você obterá um resultado ligeiramente diferente na última casa decimal. Essa pequena diferença pode determinar qual token ocupa a posição mais alta, e um token diferente muda tudo depois disso. A ordem dessas adições depende de como o provedor agrupa sua solicitação com outro tráfego, qual hardware a executa e qual versão do kernel é implantada naquele dia. Você não controla nada disso.
Os provedores também mudam as coisas em seu lado. Eles trocam GPUs, atualizam bibliotecas de inferência, requantizam pesos e roteiam sua chamada para uma região diferente. Uma longa discussão no vLLM explica por que uma seed fixa e temperature=0 ainda não são suficientes para a reprodutibilidade bit a bit. A versão curta: o determinismo é uma propriedade de toda a pilha de serviço, não um sinalizador que você define em sua solicitação.
Então, pare de tratar a saída idêntica como a linha de base. O modelo lhe dá uma resposta que significa a mesma coisa, formulada como quer que ela apareça nesta execução. Seus testes precisam aceitar isso.
Asserções de string exata tornam sua suíte instável
Aqui está a armadilha. Você escreve assert response == "Your order total is $42.00." porque foi isso que retornou da primeira vez. Ele passa. Então o modelo retorna “Seu total é de $42.00” e o teste falha em uma resposta correta.
Um teste que falha em uma resposta correta é pior do que nenhum teste. A equipe aprende que essa suíte vive dando alarme falso. As pessoas o executam novamente até que fique verde, então param de ler as falhas e, então, perdem a verdadeira regressão enterrada no ruído. Testes instáveis não apenas desperdiçam tempo, eles corroem a confiança em toda a suíte, e já escrevemos antes sobre o que causa testes instáveis e por que eles se espalham. A saída não determinística é uma das maneiras mais rápidas de produzi-los.
O instinto é fixar a saída com mais força: capturar a string exata, fazer um snapshot, comparar com ela. Isso piora a instabilidade, porque você acoplou seu teste à única coisa que é garantida mudar. Você precisa do movimento oposto.
Afirme a estrutura e o significado, não o texto exato
A saída varia, mas o contrato subjacente não deve. Um agente de suporte pode formular uma confirmação de reembolso de cem maneiras, mas toda resposta válida contém os mesmos fatos: um valor de reembolso, um ID de pedido, um status. Teste os fatos, não a formulação.
Essa é a mudança completa. Pare de perguntar “o modelo disse exatamente isso” e comece a perguntar “a resposta tem o formato certo, os campos certos e valores na faixa correta”. Essas propriedades sobrevivem à reformulação. Uma regressão real, um campo ausente, um número fora dos limites, um payload malformado, ainda aciona a asserção. Aqui estão as estratégias que colocam isso em prática.
Valide a resposta contra um esquema JSON
Se seu agente retornar dados estruturados, defina um esquema JSON para eles e valide cada resposta contra esse esquema. O esquema verifica tipos, campos obrigatórios, enums permitidos e formatos sem se preocupar com valores específicos. Um campo `status` deve ser um de `refunded` (reembolsado), `pending` (pendente) ou `denied` (negado). Um `order_id` deve corresponder ao seu padrão de ID. Um `amount` deve ser um número, não uma string.
Esta é a asserção individual mais forte que você pode escrever contra uma resposta não determinística, porque ela detecta as falhas que causam problemas: o modelo ignorou um campo, aninhou o objeto incorretamente ou retornou prosa onde você esperava JSON. Carregue seu esquema de resposta no Apidog e valide as respostas em tempo real do agente contra ele. Uma incompatibilidade nomeia o campo exato que falhou, e não uma diferença de string de 400 caracteres.
Afirme que a chamada da ferramenta tem o formato e o alvo corretos
Quando seu agente decide chamar uma ferramenta, teste a chamada, não a frase que a levou. Afirme três coisas: ela escolheu a ferramenta certa, ela mirou no alvo certo e o payload corresponde ao esquema da ferramenta. Um agente de reservas que chama `POST /reservations` deve enviar `guests` como um número inteiro e uma `date` válida, qualquer que seja o raciocínio em linguagem natural que produziu essa chamada.
Esta é a mesma disciplina de validar um corpo de resposta, aplicada à requisição de saída. Verifique se os parâmetros obrigatórios existem, se os tipos estão corretos e se nenhum campo inventado foi inserido. O método ponta a ponta para testar chamadas de API de um agente abrange a captura desses esquemas de ferramentas e a asserção contra eles. Um payload de chamada de ferramenta tem um contrato, mesmo quando a redação ao seu redor não tem.
Use intervalos numéricos em vez de valores exatos
Para qualquer número que o modelo produz ou transmite, afirme um intervalo, não um valor. Um agente de carrinho de compras calcula um total. Você não sabe o valor exato em cada execução, carrinho e regra fiscal, mas sabe que ele não pode ser negativo e não pode exceder o valor do carrinho mais o frete máximo e o imposto. Então afirme isso: a resposta contém um `total` entre 0 e esse limite.
Esse único limite captura as falhas que importam: um total negativo, um total dez vezes maior, um total de zero em um carrinho cheio, enquanto ignora variações que você não se importa. Intervalos funcionam para pontuações de confiança, contagens de itens, uso de tokens, orçamentos de latência e qualquer valor derivado. Escolha o limite mais amplo que ainda falha em um bug genuíno.
Verifique se as chaves obrigatórias existem e os campos proibidos estão ausentes
Duas asserções simples carregam muito peso. Primeiro, as chaves das quais você depende estão presentes e não nulas. Segundo, as chaves que nunca devem aparecer estão ausentes. Um agente que lida com um ticket de suporte deve retornar uma `resolution` (resolução), e nunca deve vazar um campo `internal_notes` (notas internas) ou `raw_prompt` (prompt bruto) para o cliente.
As verificações de presença e ausência são imunes à reformulação por design, pois testam o esqueleto da resposta, não seu conteúdo. Elas também são sua guarda mais barata contra toda uma classe de vazamentos de privacidade, onde o modelo, de forma útil, inclui um campo que deveria ter mantido privado.
Use verificações semânticas e de limiar para texto livre
Às vezes o payload é prosa e você ainda precisa testá-lo. A correspondência exata não funcionará, então verifique as propriedades. A resposta contém o número do pedido que você passou? Ela se mantém abaixo de um limite de comprimento? Ela evita uma lista de frases que você nunca quer enviar a um usuário?
Quando você realmente precisa testar o significado, compare por similaridade de incorporação (embedding) contra uma resposta de referência e afirme que a pontuação ultrapassa um limiar, em vez de exigir que as strings correspondam. Trate essas verificações semânticas como um portão grosseiro, não preciso. Elas detectam uma resposta que se desviou do tópico. Elas não detectarão um erro factual sutil, então combine-as com as asserções estruturais acima.
Intervalos de snapshot, não snapshots exatos
Testes de snapshot ainda têm seu lugar, contanto que você faça snapshot das partes estáveis. Congele o formato da resposta, o conjunto de chaves, os tipos, os valores de enum, e deixe os campos de fluxo livre variarem dentro dos limites. Na prática, seu snapshot registra “esta resposta tem as chaves a, b, c, com b neste intervalo e c deste conjunto” em vez de um bloco congelado de texto exato. Quando o snapshot falha, ele falha em uma mudança estrutural que vale a pena revisar, não em um sinônimo.
Estado e memória tornam isso mais difícil
Tudo acima assume uma requisição de entrada, uma resposta de saída. Agentes não funcionam assim. Eles carregam memória entre as interações, e esse estado multiplica as fontes de variação.
A resposta de um agente com estado depende do que ele recuperou, do que armazenou anteriormente e da ordem em que as interações anteriores foram executadas. Duas execuções da mesma conversa podem divergir porque uma etapa de recuperação classificou documentos de forma diferente, ou porque um resumo escrito na interação dois moldou o raciocínio na interação cinco. Agora sua saída varia por duas razões compostas: o não-determinismo do próprio modelo e um estado inicial diferente. Nosso explicador sobre como a memória de agentes de IA funciona detalha onde esse estado reside e como é construído.
Dois hábitos mantêm isso testável. Primeiro, controle o estado que você pode. Inicialize a memória do agente para um ponto de partida conhecido antes de cada teste, para que você esteja variando uma coisa e não duas. Segundo, afirme invariantes que se mantêm independentemente do caminho. Um saldo corrente nunca deve ficar negativo. Uma conversa que reservou um voo deve terminar com exatamente uma reserva, não importa quantas interações levou. Asserções independentes de caminho são aquelas que sobrevivem a um agente com estado e não determinístico.
Simule as dependências para que o teste se repita
Você não pode ensaiar nada disso contra APIs de terceiros em tempo real. Elas aplicam limites de taxa, alteram seus dados e adicionam uma segunda fonte de aleatoriedade além do modelo. Para obter um teste repetível, fixe tudo o que não seja o comportamento que você está testando.
Simule as APIs que o agente chama e programe respostas fixas. Agora, a API de pagamento sempre retorna o mesmo recibo, a API de pesquisa sempre retorna os mesmos três resultados, e a única coisa que resta em movimento é o próprio raciocínio do agente, que é o que você quer observar. Uma dependência simulada também permite que você force os casos extremos que uma API saudável não produziria sob demanda, e então afirme que o agente os trata. Aponte o Apidog para as dependências do agente para configurar essas simulações com corpos estáveis e controláveis, e combine-as com as asserções de esquema acima. Isso se encaixa na prática mais ampla de testes de IA agêntica, onde simulação e asserção trabalham juntas.
Onde o Apidog se encaixa (e onde não)
Seja exato sobre o trabalho da ferramenta. Apidog é uma plataforma de design, teste e simulação de API. Não é um framework de agente, um host de modelo, um ambiente de execução de agente, nem uma plataforma de avaliação e observabilidade. Ele não constrói seu agente, não o executa, não orquestra seus passos, nem avalia seu raciocínio.
O que ele realmente detém é a camada de API com a qual seu agente se comunica, e é onde esses testes residem. Dois encaixes honestos. Você escreve asserções nas respostas da API do agente (validação de esquema, formato da resposta, intervalos numéricos, chaves obrigatórias e proibidas, formato do payload de chamada de ferramenta) que sobrevivem a uma saída não determinística. E você simula as dependências do agente para que um teste seja executado da mesma forma duas vezes. Essa é a lacuna que o Apidog preenche: o contrato nas requisições e respostas, não o modelo que as produz.
Teste o contrato, não a redação
O não-determinismo não é um bug que você pode configurar para eliminar. É uma propriedade de executar um modelo de linguagem, e `temperature=0` não o desliga. As equipes que entregam agentes confiáveis pararam de lutar contra isso. Elas testam as coisas que permanecem constantes: o esquema, o formato, os intervalos, os campos obrigatórios, e deixam a redação mudar. Faça isso e sua suíte ficará tranquila da melhor forma: ela permanece verde enquanto o texto varia, e só fica vermelha quando algo está realmente quebrado.
Escolha uma asserção instável em sua suíte esta semana e reescreva-a como uma verificação de esquema e intervalo. Baixe o Apidog para validar as respostas do seu agente contra um contrato e simular as dependências que tornam os testes repetíveis.
