Teste o prompt antes de publicar: avaliação detecta regressão de 16%

Um prompt mais curto perdeu uma regra. Em 110 mensagens novas, omitiu o número do pedido 18 vezes (16%), contra nenhuma da versão antiga. Um teste de 40 linhas não detectou falhas.

Nossas escolhas

preços em tempo real
Modelo Veredito Preço
GPT-6 Luna A v1 de support-answer informou o número do pedido em 275 de 275 chamadas de teste. A v2, com um prompt de sistema mais curto, fez isso em 92 de 110 na execução definida de antemão. Modelo de ambas as versões em teste desde $0.1/M
Claude Sonnet 5.5 Avaliou se cada resposta aborda a pergunta do cliente, com PASS ou FAIL. Suas chamadas custaram $0.12 dos $0.14 que custaram as seis execuções de teste. Modelo avaliador na demonstração desde $2/M
Conteúdo
  1. O que compõe um teste de prompt?
  2. Como executar um teste de prompt por código ou no CI?
  3. O teste detectou a regressão?
  4. Por que uma execução sem falhas pode esconder uma regressão?
  5. O prompt mais curto causou outros problemas?
  6. Quanto custa um teste de prompt?
  7. Como o teste ajuda na decisão de publicar?
  8. O que ficou de fora?
  9. Perguntas frequentes
  10. Como testar um prompt de LLM antes de colocá-lo em produção?
  11. De quantos casos preciso para um teste de regressão de prompt?
  12. Um avaliador LLM pode substituir verificações por padrão?
  13. Leituras relacionadas

Testar um prompt antes de publicá-lo significa executar a versão nova e a atual com as mesmas entradas fixas e conferir cada resposta com base em regras escritas. Fizemos isso com a v2 de um prompt de suporte: ela encurtou o texto de sistema e removeu a regra “sempre informe o número do pedido”. Essa era a regressão, uma mudança que piora a saída, que o teste deveria detectar. Em 110 mensagens que não haviam sido usadas em nenhuma execução anterior, a v2 omitiu o número do pedido em 18 respostas (16%), e a v1 não omitiu em nenhuma. A diferença ficou muito além do que se esperaria por acaso (teste exato de Fisher, p = 3.6 × 10⁻⁶). Uma execução anterior com 40 mensagens não mostrou nenhuma omissão. Portanto, passar em uma única execução não basta para aprovar uma versão.

TL;DR

  • A v2 de support-answer omitiu o número do pedido em 18 de 110 respostas em um teste definido antes da execução; a v1, em 0 de 110 (p = 3.6 × 10⁻⁶).
  • Em uma das três execuções anteriores com 40 linhas, a v2 passou nas 40. Um dos três conjuntos finais também não teria detectado o problema sozinho.
  • A v1 informou o número do pedido nas 275 chamadas de teste feitas pelo script da demonstração.
  • Seis execuções de teste com 15 a 20 linhas custaram $0.14, dos quais $0.12 foram gastos com o modelo avaliador.

Este guia dá continuidade a Versionamento de prompts, que configura support-answer e suas versões. Tanto a v1 quanto a v2 usam GPT-6 Luna; a única diferença é o texto de sistema. No mesmo diretório de github.com/synthorai-io/use-cases:

cd use-cases/prompt-library
python evals.py                        # v2 against v1 on the orders and general sets, three runs each
python evals.py --set fresh --runs 1   # the test fixed before the run
python evals.py --read RUN_ID          # read back a run started in the console

As execuções desta página são de 19 de outubro de 2026. O modelo avaliador é o Claude Sonnet 5.5, que não é usado por nenhuma das versões em teste.

O que compõe um teste de prompt?

Um conjunto de teste é uma tabela de entradas acompanhada de uma lista de verificações. Cada linha fornece valores para as variáveis do prompt e, opcionalmente, uma mensagem do usuário e uma resposta esperada. A cada execução, o teste chama a versão candidata e uma versão de referência para cada linha e aplica todas as verificações a cada resposta. Os tipos de verificação são:

VerificaçãoA resposta passa quando
contains / not_containscontém ou não contém uma string fixa
regex / not_regexcorresponde ou não corresponde a um padrão (sintaxe RE2 do Go)
equals_expectedé igual à resposta esperada da linha
json / schemaé um JSON válido / segue o esquema de saída da versão
max_latency_ms / max_cost_usdchega dentro do prazo e custa até o limite definido
judgerecebe PASS de um modelo com base em um critério escrito por você

Um conjunto comporta até 50 linhas, e cada verificação se aplica a todas as linhas dele. Isso determina como dividir o teste. “A resposta contém um número de pedido” só faz sentido para mensagens que informam esse número. Por isso, a demonstração mantém essas mensagens em conjuntos separados:

ConjuntoLinhasVerificações
orders (15 linhas, depois 40)mensagens que informam um número de pedido e tratam de entrega, devoluções, itens errados ou danificados e cobrançapresença do número do pedido (regex \bA-\d{5}\b); avaliação por modelo: responde à pergunta do cliente
fresh orders 1 a 3mais 110 mensagens semelhantes, sem repetir nenhuma de outros conjuntos, divididas em 40, 35 e 35as mesmas duas
general (20 linhas)mensagens sem número de pedido, várias sobre prazo de reembolsonot_contains “refund by” e “business days”; latência abaixo de 20 s; avaliação por modelo: responde à pergunta; avaliação por modelo: não promete uma data de reembolso

A verificação por modelo faz uma chamada por resposta e retorna PASS ou FAIL. Ela tem custo e pode errar. Por isso, a demonstração a usa para critérios que um padrão não consegue expressar e apresenta seus resultados separadamente das verificações por padrão.

Como executar um teste de prompt por código ou no CI?

O CI (integração contínua, com verificações executadas a cada alteração antes do merge) pode executar um teste de prompt pela API. Seu código conduz o teste: abre uma execução, chama as duas versões por prompt_version para cada linha, avalia as respostas e grava os resultados na execução. O console passa a exibi-la como qualquer outra. Uma versão resumida do loop da demonstração:

EVAL = {"X-Synthorai-Metadata": '{"source":"eval"}'}   # keeps test calls out of per-version stats

run = admin.post("/prompts/support-answer/eval-runs",
                 json={"set_id": set_id, "candidate": 2, "baseline": 1}).json()["data"]
results = []
for i, row in enumerate(rows, 1):
    entry = {"row": i}
    for side, version in (("candidate", 2), ("baseline", 1)):
        answer = call_by_version("support-answer", row, version, headers=EVAL)   # a Chat Completions call
        entry[side] = {"version": version, "answer": answer.text,
                       "checks": {"order number": bool(ORDER.search(answer.text)),
                                  "answers": judge(ANSWERS, row, answer.text)}}
    results.append(entry)
    admin.put(f"/prompts/support-answer/eval-runs/{run['id']}",
              json={"status": "finished" if i == len(rows) else "running", "done": i, "checked": i,
                    "passed": sum(all(e["candidate"]["checks"].values()) for e in results),
                    "results": {"rows": results}})

call_by_version faz uma chamada pelo identificador com prompt_version definido, como em Versionamento de prompts. Já judge chama o modelo avaliador e pede PASS ou FAIL. O rótulo source: eval importa durante a implantação: os totais por versão na página Prompts excluem chamadas com esse rótulo, para que o teste não distorça a comparação do tráfego de produção.

A demonstração avalia as verificações por padrão em Python. Para avaliá-las exatamente como o console, envie as verificações e as respostas para POST /api/v1/prompt-checks/grade. O endpoint aplica contains, not_contains, regex, not_regex, equals_expected e json no servidor com RE2 e retorna aprovação ou reprovação para cada combinação de verificação e resposta. Um teste rápido retornou o resultado esperado: (?i)refund aplicado a “REFUND by Friday” passou, e not_regex (?i)refund.{0,40}\bby\b aplicado à mesma resposta falhou.

O botão de execução do console funciona da mesma forma, a partir do navegador: chama as duas versões para cada linha e armazena os resultados. Em uma execução pelo console com o conjunto orders de 40 linhas (execução 10 abaixo), apenas a candidata foi avaliada. As respostas da v1 apareceram ao lado, sem verificações.

Linha 37 da execução no console: a resposta da v1 informa o pedido A-50037, mas a da v2 não. A v2 falha na verificação por padrão, embora passe na avaliação por modelo

O teste detectou a regressão?

Sim. Definimos o teste antes de executá-lo: 110 mensagens novas, não usadas em nenhuma execução anterior, uma chamada por versão para cada linha e um único teste exato de Fisher sobre a verificação do número do pedido nas 110 linhas, com limiar de 0.05. O teste exato de Fisher calcula a probabilidade de observar uma diferença pelo menos tão grande caso as duas versões realmente se comportassem da mesma forma. O teste bilateral considera diferenças em qualquer direção. Fizemos o commit das linhas e dessa regra no repositório antes da primeira chamada. O resultado:

Conjuntov2 informa o número do pedidov1 informa o número do pedidop de Fisher para este conjunto isolado
fresh orders 1 (40 linhas)33/4040/400.012
fresh orders 2 (35 linhas)27/3535/350.005
fresh orders 3 (35 linhas)32/3535/350.239
Todas as 110 linhas92/110110/1103.6 × 10⁻⁶

A v2 omite o número do pedido em 16% das respostas. As omissões aparecem em vários assuntos, não em um único tipo de mensagem: 4 de 22 em cada categoria de entrega, devoluções, itens errados e itens danificados, e 2 de 22 em cobrança. Um exemplo típico é a resposta a “O pedido A-50918 aparece com duas cobranças pendentes no aplicativo do meu banco.”: ela explicou que os dois lançamentos poderiam ser autorizações pendentes, e não duas cobranças. O modelo avaliador aprovou a resposta, mas ela não mencionou o pedido.

Os valores de p por conjunto descrevem os três conjuntos; eles não são o teste definido. Dois dos três conjuntos teriam sinalizado o problema na v2 isoladamente; o terceiro, não.

Por que uma execução sem falhas pode esconder uma regressão?

Uma versão que só viola uma regra em parte das respostas pode passar uma execução inteira sem violá-la. Antes do teste acima, a demonstração comparou a v2 com a v1 três vezes em um conjunto de 40 linhas:

Execuçãov2 informa o número do pedidov1
735/4040/40
836/4040/40
940/4040/40
10 (console)39/4040/40 segundo a avaliação das respostas armazenadas feita pela demonstração

A v2 passou nas 40 linhas da execução 9. Uma equipe que rodasse o teste uma única vez e obtivesse esse resultado teria publicado a v2. Nenhuma dessas execuções tem significância estatística isoladamente: contra uma referência que nunca falha, uma execução de 40 linhas ou mais precisa de pelo menos 6 falhas para ficar abaixo de 0.05 (34 de 40 resulta em p = 0.026; 35 de 40, em 0.055). Conjuntos menores precisam de menos falhas: 5 em 20 já resultam em p = 0.047.

Isso dá uma regra para dimensionar o teste: use linhas suficientes para que seja provável observar 6 falhas, não apenas para que essa seja a quantidade esperada. Com uma taxa de falha de 16%, 48 linhas dão 80% de chance de observar pelo menos 6 falhas; 38 linhas dão apenas 58%, embora 6 seja a quantidade esperada. Com 6.25%, a taxa do conjunto de 40 linhas acima (10 falhas em 160 respostas), são necessárias 126 linhas para chegar a 80% de chance. Para uma regra importante, use mais linhas do que cabe em um conjunto, distribua-as em vários conjuntos e decida antes como combinar os resultados. Repetir as mesmas linhas não acrescenta evidência da mesma forma que usar linhas novas: as execuções testam novamente as mesmas mensagens, portanto não são amostras independentes. Na demonstração, três linhas em que a v2 falhou em uma execução também falharam em outra.

O prompt mais curto causou outros problemas?

As outras verificações não detectaram nenhum outro problema. No conjunto general, com três execuções para cada versão:

  • Sem data de reembolso: nenhuma versão escreveu “refund by” ou “business days”. A verificação por modelo reprovou no máximo 2 de 20 respostas por versão em cada execução, sem diferença entre as versões.
  • Latência: a v1 ultrapassou 20 s em 2 linhas de uma execução; a v2 nunca ultrapassou.
  • Responde à pergunta (avaliação por modelo): a v2 teve resultado melhor em todos os conjuntos, com 88 de 110 contra 75 de 110 nos conjuntos fresh. Não definimos essa comparação antecipadamente, então ela indica apenas uma tendência: pela avaliação do modelo, o prompt mais curto responde de forma mais direta.

O avaliador do console e o da demonstração usam prompts diferentes (o do console escreve uma justificativa). Nas mesmas 40 mensagens, com respostas de execuções diferentes, o console aprovou a v2 em 40 de 40, e o avaliador da demonstração, em 32 a 35. Compare resultados apenas dentro do mesmo avaliador.

Quanto custa um teste de prompt?

Cerca de um décimo de centavo de dólar por linha, considerando duas versões e uma verificação por modelo, com os modelos desta demonstração:

ExecuçõesLinhas por execuçãoChamadasCusto
6 execuções dos conjuntos de 15 e 20 linhas15 ou 20respostas e avaliação por modelo$0.1400, dos quais $0.1198 em chamadas ao avaliador
3 execuções do conjunto de 40 linhas40240 respostas + 240 avaliações por modelo$0.115
teste definido antecipadamente110220 respostas + 220 avaliações por modelo$0.1076

O avaliador responde pela maior parte do custo porque cada resposta exige uma chamada própria a um modelo maior. Uma execução de 40 linhas levou 10 minutos com chamadas sequenciais. Chamadas paralelas reduziriam esse tempo, mas consumiriam mais do limite de requisições.

Como o teste ajuda na decisão de publicar?

Ao mover production no console, a caixa de diálogo mostra a última execução de teste da versão ao lado da alteração. O resultado serve de referência para a decisão, mas não bloqueia a mudança. Os conjuntos de teste e as respostas de cada execução são armazenados com criptografia, e as respostas são mantidas por 30 dias.

Caixa de diálogo para mover production para a v2: modelo e parâmetros permanecem iguais, as mensagens mudam, e a última execução de teste da v2, fresh orders 3 com 35 linhas, aprovou 28 de 35

A caixa de diálogo só conta uma linha como aprovada quando ela passa em todas as verificações do conjunto. Portanto, o resultado de 28 de 35 combina a verificação por padrão e a avaliação por modelo.

O que ficou de fora?

  • Verificações por linha. Cada verificação se aplica a todas as linhas do conjunto. Por isso, a demonstração separa as mensagens com e sem número de pedido em conjuntos distintos.
  • Testes executados pelo servidor. Tanto a API quanto o botão de execução do console fazem as chamadas ao modelo a partir do lado de quem inicia o teste. Não há um endpoint que execute o teste inteiro no servidor e aguarde sua conclusão.
  • Precisão do avaliador. Não comparamos o avaliador com avaliações humanas. Seus números indicam tendências, e a conclusão apresentada nesta página se limita à verificação por padrão.

Perguntas frequentes

Como testar um prompt de LLM antes de colocá-lo em produção?

Execute a versão nova e a atual com as mesmas entradas fixas, confira cada resposta com base em regras escritas e compare as quantidades de aprovações com um teste de significância definido antecipadamente. Na demonstração, 110 mensagens novas e um teste exato de Fisher distinguiram uma taxa de omissão de 16% de uma de 0%.

De quantos casos preciso para um teste de regressão de prompt?

Use linhas suficientes para que seja provável observar pelo menos 6 violações da regra caso a regressão exista. Para uma chance de 80%, são 48 linhas com taxa de falha de 16% e 126 linhas com taxa de 6.25%. Na demonstração, uma execução de 40 linhas aprovou uma versão que violou a regra em 16% das respostas do teste principal.

Um avaliador LLM pode substituir verificações por padrão?

Um avaliador não deve substituir uma verificação que possa ser expressa por um padrão. Na demonstração, o console e o script usaram prompts de avaliação diferentes e aprovaram a v2 em 40 de 40 e em 32 a 35 de 40 das mesmas mensagens. Já um padrão sempre produz o mesmo resultado para o mesmo texto.

Leituras relacionadas

← Casos de uso