Tutorial Promptfoo: um guia prático de avaliação de LLM
Crie apps de IA confiáveis mais rápido ao transformar checagens ad hoc de prompts em avaliações estruturadas de LLM com o Promptfoo — do teste local ao CI automatizado.
A maioria dos recursos com LLM é testada do mesmo jeito: tenta alguns inputs, dá uma olhada no resultado e coloca no ar.
Você fez isso com um gerador de e-mails. Cinco inputs na janela do chat, os outputs pareceram ok. Uma semana depois, metade dos e-mails com tom casual soa como se tivessem sido escritos por um advogado corporativo. Ninguém percebeu porque não havia como perceber. Os prompts mudaram, mas aqueles cinco testes manuais não foram com você.
Promptfoo é um CLI open source que substitui esse processo por avaliações estruturadas e repetíveis. Você define o que é um bom output, escolhe os modelos e roda todas as combinações automaticamente.
A OpenAI adquiriu o projeto em março de 2026, e ele continua sob licença MIT com suporte a dezenas de provedores. Mais de 350 mil desenvolvedores usam a ferramenta, incluindo times em mais de 25% das empresas da Fortune 500.
Este tutorial mostra como configurar o Promptfoo e criar sua primeira suíte de avaliações do zero. Vamos usar um redator de e-mails como exemplo, testando no GPT-5 e no Claude Sonnet 4.6, e integrando tudo ao GitHub Actions no final.
Avaliação de LLM em 60 segundos
Antes de falar da ferramenta, vale entender como funciona o teste de LLM. É diferente de testar código comum.
Se você já testou uma função, conhece o padrão: dá um input e confere se o output bate com o esperado. Com LLMs não é assim. O mesmo prompt pode gerar textos diferentes a cada execução, então você não consegue verificar por igualdade exata.
Em vez disso, você verifica propriedades do output:
Ele traz as informações certas?
Acerta o tom adequado?
Respondeu rápido o suficiente?
É isso que um eval de LLM faz. Ele roda seu prompt contra um conjunto de inputs e verifica cada output com base em regras que você define. Pense como uma suíte de testes para prompts, não para código.
Test case é um input pareado com o comportamento esperado no output.
Assertion é uma única regra que o output precisa cumprir, como "contém a palavra sexta-feira" ou "responde em menos de 30 segundos".
Rubric é uma instrução em linguagem natural para outro LLM quando a checagem é subjetiva demais para casar strings — por exemplo, se um e-mail realmente soa casual.
Sem assertions, você volta ao "pra mim está bom". Com elas, você tem uma definição de "correto" que roda igual sempre.
O que é o Promptfoo?
Agora que você sabe o que é um eval, aqui vai o que o Promptfoo faz com ele.
Você entrega três coisas ao Promptfoo:
Seus templates de prompt
Os modelos que quer testar
Seus casos de teste com assertions
Ele roda cada prompt em cada modelo para cada caso de teste e pontua os resultados. Um comando, promptfoo eval, executa tudo.
Suponha que você tenha um prompt de e-mail, dois modelos (GPT-5 e Claude Sonnet 4) e três casos (casual, formal, urgente). O Promptfoo roda as seis combinações e informa o que passou e o que falhou. Chega de testar um por um na mão.
Todo o eval vive em um único arquivo YAML chamado promptfooconfig.yaml que você versiona junto com o código. O Promptfoo roda na sua máquina: seu config, resultados e cache ficam locais. As únicas chamadas externas são para as APIs de modelo como OpenAI ou Anthropic — que você faria com ou sem o Promptfoo.
Há outras ferramentas nesse espaço, como DeepEval (nativa em Python, estilo pytest), LangSmith (monitoramento em produção para LangChain) e Braintrust (dashboards para times). O Promptfoo é o melhor ponto de partida porque é gratuito, roda localmente e leva você do zero a um eval funcionando mais rápido que todos eles.
Configurando seu ambiente com o Promptfoo
Instale o Promptfoo globalmente e inicialize um novo projeto:
npminstall-g promptfoo
mkdir email-writer-eval
cd email-writer-eval
promptfoo initTecnologia
O comando init guia você por uma configuração interativa. Ele pergunta o que você quer fazer (escolha "Not sure yet") e qual provider de modelo usar (escolha "[OpenAI] GPT 5, GPT 4.1, ...").
Ao finalizar, você terá dois arquivos: um promptfooconfig.yaml de exemplo e um README.md.
Em seguida, defina suas chaves de API. Você precisa de pelo menos uma para rodar os evals (tenha as duas para acompanhar este tutorial):
Se você definir apenas ANTHROPIC_API_KEY e pular a OpenAI, o Promptfoo usa automaticamente o Claude como provedor de avaliação para assertions assistidas por modelo como llm-rubric.
Abra o promptfooconfig.yaml gerado. Todo config do Promptfoo tem três blocos básicos:
prompts é onde entram seus templates de prompt. Espaços reservados com chaves duplas como {{variable}} são preenchidos por cada caso de teste.
providers lista os modelos contra os quais você quer testar.
tests define os inputs e as assertions que decidem se cada output passa ou falha.
Esse config de exemplo é só um ponto de partida. Na próxima seção, vamos substituí-lo por uma avaliação real e explicar a estrutura do YAML em detalhes.
Criando sua primeira avaliação
A tarefa é: dado um conjunto de bullet points e um tom (casual, formal ou urgente), escrever um e-mail. Você vai criar um eval que testa isso em dois modelos.
O config completo está disponível como um Gist se quiser ver tudo de uma vez. Vamos montar parte por parte abaixo.
Prompt e providers
Apague o exemplo gerado e crie um novo promptfooconfig.yaml. Comece pelo prompt e pelos providers:
description: \"Email writer evaluation\"
prompts:-|
Draft an email based on these bullet points.
Match the specified tone throughout the email.Bullet points:{{bullet_points}}Tone:{{tone}}providers:-id: openai:chat:gpt-5label: \"GPT-5\"
-id: anthropic:messages:claude-sonnet-4-6label: \"Claude Sonnet 4.6\"Tecnologia
O template de prompt tem dois placeholders: {{bullet_points}} e {{tone}}. Cada caso de teste preenche esses campos com valores diferentes. O campo label em cada provider gera cabeçalhos de coluna legíveis nos resultados, em vez de IDs crus de modelo.
Bloco defaultTest
Agora, adicione um bloco defaultTest. As assertions dentro de defaultTest valem automaticamente para todos os casos de teste, evitando repetição:
Isso reprova qualquer resposta mais lenta que 30 segundos. Modelos de fronteira como o GPT-5 podem levar 10–20 segundos por requisição devido a tokens de raciocínio, então o limite precisa de folga. Você define uma vez e cobre todos os testes.
Casos de teste
Agora adicione os casos de teste. Cada um traz inputs diferentes e suas próprias assertions:
tests:-vars:bullet_points:|
- Recap of the design review decisions
- Next steps: finalize mockups by Thursday
- Ask if anyone has questionstone: \"casual\"
assert:-type: icontains
value: \"mockups\"
-type: llm-rubric
value: \"The email uses a casual tone with contractions and short sentences\"
-vars:bullet_points:|
- Q1 revenue exceeded targets by 12%
- New enterprise client onboarded
- Hiring plan for Q2 approvedtone: \"formal\"
assert:-type: icontains
value: \"Q1\"
-type: llm-rubric
value: \"The email maintains a formal, professional tone throughout\"
-vars:bullet_points:|
- API migration deadline is Friday at 5pm
- Three endpoints still need updating
- Downtime window is Saturday 2-6amtone: \"urgent\"
assert:-type: icontains
value: \"Friday\"
-type: llm-rubric
value: \"The email conveys urgency with direct language and clear action items\"Tecnologia
Cada caso de teste combina dois tipos de assertions.
icontains é uma checagem simples de string: o output incluiu "mockups", sem diferenciar maiúsculas/minúsculas? É rápido, gratuito e não chama API.
llm-rubric envia o output para outro LLM e pede que ele faça a avaliação com base na sua rubrica. Custa tokens, mas pega coisas que matching de string não pega, como se um e-mail realmente soa casual.
A interface web mostra os providers como colunas e os casos de teste como linhas. Cada célula exibe o status de aprovação/reprovação de cada assertion, e você pode clicar para ver o output completo e os detalhes da avaliação.
Por padrão, o Promptfoo faz cache das respostas de API em disco (TTL de 14 dias), então reexecutar o mesmo eval não custa nada. Use --no-cache quando quiser respostas novas.
Escrevendo assertions
No primeiro eval usamos icontains e llm-rubric. Esses são dois entre muitos tipos de assertions compatíveis com o Promptfoo. Nesta seção, vamos passar pelas principais categorias e continuar adicionando assertions ao config do redator de e-mails.
Assertions determinísticas
Elas rodam localmente, não custam nada e retornam resultados instantaneamente.
Tipo
O que verifica
contains / icontains
Se o output inclui um substring (com ou sem diferenciar maiúsculas)
regex
Se o output casa um padrão (capture {{placeholders}} não preenchidos com \\{.*?\\})
not-contains
Se o output exclui algo (artefatos de template, recusas, texto de placeholder)
latency
Se a resposta chegou em menos de N milissegundos
cost
Se a resposta custou menos de US$ X
Você já usou o icontains e latency. Vamos adicionar not-contains ao caso casual. Se o modelo tender ao formal, pode começar com "Dear" em vez de algo casual como "Hey". Para pegar isso, basta uma linha:
Adicione isso à lista assert do teste casual e rode de novo. Ambos os modelos devem passar: o GPT-5 costuma abrir com "Hey team," e o Claude Sonnet 4 também. Se algum tivesse começado com "Dear Colleagues", essa assertion sinalizaria na hora.
Todo tipo de assertion também aceita o prefixo not-: not-regex, not-equals etc.
Assertions assistidas por modelo
Elas consomem tokens, mas conseguem julgar o que matching de string não consegue. Você já usou llm-rubric no primeiro eval. A rubrica que você escreve é decisiva.
Uma rubrica vaga como "O e-mail soa profissional" não ajuda o avaliador. Uma específica dá o que medir:
-type: llm-rubric
value:\"The email uses a casual tone: contractions like 'we'll' and 'don't',
sentences under 20 words, no corporate jargon like 'synergy' or 'circle back',
and opens with a greeting like 'Hey' or 'Hi team'\"Tecnologia
Quando você reexecuta o eval com essa rubrica, a saída do avaliador também fica específica:
Claude Sonnet 4.6: "O e-mail adota um tom casual ('Hey team,' 'shoot over'), usa contrações ('we're,' 'let's,' 'don't')."
Quanto mais específica a rubrica, mais consistente e útil fica a avaliação.
O Promptfoo também traz answer-relevance (o output respondeu à pergunta?) e similar (similaridade de cosseno contra uma referência via embeddings), ambos úteis para RAG e aplicações de busca.
Assertions personalizadas em Python
Quando os tipos nativos não cobrem sua lógica, escreva a sua. No redator de e-mails, você pode querer verificar se o output fica dentro de um tamanho razoável. Aqui vai uma assertion inline que aprova se o e-mail tem entre 50 e 200 palavras:
Adicione isso ao caso casual e rode. No meu teste, ambos os modelos passaram: o GPT-5 ficou em 54 palavras e o Claude Sonnet 4 em 187. Mas guarde essa assertion para a próxima seção, porque ela não passou em todos os lugares.
Para checagens mais complexas, coloque a lógica em um arquivo separado:
Referencie no seu config com type: python e value: file://assert_length.py. O campo reason aparece na UI de resultados para você ver exatamente por que um teste passou ou falhou.
Quando um teste deve falhar
Veja como é uma falha real.
Adicione a assertion em Python de contagem de palavras aos três casos e rode o eval completo nos dois modelos. No meu caso, cinco das seis combinações passaram. O Claude Sonnet 4.6 não passou no caso de urgência (isso pode ou não acontecer aí, dado o comportamento não determinístico dos LLMs).
Nessa execução, o output ficou com 207 palavras, sete acima do limite. O llm-rubric passou, confirmando que o e-mail usou "linguagem urgente e direta" com "ações claras". O icontains também passou, já que "Friday" estava no output.
Mas a assertion em Python de contagem de palavras falhou. O Claude colocou um preâmbulo ("Here is a draft email based on your bullet points with an urgent tone:") antes do e-mail em si, empurrando a contagem acima de 200.
Esse é o tipo de coisa que você não pegaria só batendo o olho. O e-mail em si parecia ótimo: tom certo, conteúdo certo. Mas o output ficou longo demais porque o modelo adicionou um texto que não fazia parte do e-mail. Uma assertion pegou isso e o eval sinalizou o caso inteiro.
A correção pode seguir dois caminhos: ajustar o prompt para pedir que o modelo não inclua preâmbulo, ou aumentar o limite de palavras. De qualquer forma, você reexecuta o eval e confere se passou.
Pontuação ponderada
Neste ponto, o caso casual tem quatro assertions: icontains, not-contains, llm-rubric e a de contagem de palavras em Python. Nem todas têm o mesmo peso. O campo weight permite expressar isso:
assert:-type: icontains
value: \"mockups\"
weight:1-type: not-contains
value: \"Dear\"
weight:1-type: llm-rubric
value: \"The email uses a casual tone with contractions and short sentences\"
weight:2-type: python
value: \"50 <= len(output.split()) <= 200\"
weight:0.5threshold:0.7Tecnologia
A pontuação de cada assertion é multiplicada pelo seu peso, e o teste calcula uma média ponderada. O threshold define a nota mínima para passar. Aqui, a rubrica de tom vale o dobro dos checks por palavra-chave, e a contagem de palavras vale metade.
Reexecutando o teste casual com esses pesos, os dois modelos fazem 1,0 e passam. Mas se o llm-rubric falhasse (peso 2) enquanto a contagem de palavras passasse (peso 0,5), a pontuação ponderada cairia abaixo de 0,7, e o teste falharia. Os pesos permitem dizer ao Promptfoo o que mais importa para você.
Comparando modelos lado a lado
O config já tem dois providers, então promptfoo eval testou GPT-5 e Claude Sonnet 4 em uma única passada. Abra promptfoo view e você os verá como colunas separadas nos resultados, com cada caso de teste pontuado independentemente por modelo.
Na minha execução, o GPT-5 passou nas seis assertions dos três casos. O Claude Sonnet 4 passou em cinco e falhou na contagem de palavras do e-mail urgente.
Esse é o tipo de diferença que você não notaria testando alguns prompts manualmente, mas que aparece na hora na grade. Você está comparando notas contra as mesmas assertions nos mesmos inputs — não sua memória de qual modelo "pareceu melhor" da última vez.
Mas outputs de LLMs não são determinísticos. O mesmo prompt pode gerar resultados diferentes em execuções consecutivas, e uma passada só não diz se um modelo é confiável ou apenas deu sorte. A flag --repeat ajuda nisso:
Isso roda cada caso de teste três vezes por provider. Se um modelo passa a assertion de tom duas vezes e falha na terceira, é um sinal de confiabilidade que passaria batido numa passada única.
Do teste local ao CI/CD
Rodar evals localmente funciona no desenvolvimento, mas depende de quem alterou o prompt lembrar de rodá-los. O Promptfoo tem uma GitHub Action oficial que elimina essa dependência ao executar sua suíte de evals em todo pull request e publicar os resultados como comentário.
Para configurar, crie um workflow no seu repositório:
Antes de funcionar, você precisa adicionar sua OPENAI_API_KEY (e opcionalmente ANTHROPIC_API_KEY) como secrets do repositório no seu GitHub, em Settings > Secrets and variables > Actions.
O filtro paths faz a action disparar apenas quando alguém altera arquivos em prompts/. O checkout é necessário porque a action usa git internamente para fazer diff dos arquivos de prompt entre branches. Ela roda a suíte inteira e publica um comentário no PR com os resultados e um link para o visualizador web.
Se quiser mais controle sobre a lógica de aprovação/reprovação, você pode analisar o JSON diretamente:
Mudanças de prompt recebem o mesmo tratamento de testar-antes-de-fazer-merge que mudanças de código.
Conclusão
O redator de e-mails do início ainda tem problema de tom, mas agora existe um teste que pega isso antes dos usuários. Você começou com um YAML em branco e terminou com uma suíte de evals rodando em dois modelos no CI.
A mesma abordagem vale para qualquer recurso com LLM que você construir — chatbot, sumarizador, pipeline de classificação. Se você consegue escrever uma assertion do que é "um bom output", dá para testar automaticamente em vez de só bater o olho.
Red teaming: promptfoo redteam run verifica injeção de prompt e jailbreaks em dezenas de plugins de ataque
Providers personalizados em Python: Encapsule qualquer modelo interno ou endpoint fine-tunado com file://my_provider.py
Dados de teste em CSV: Escalone sua suíte com file://tests.csv quando o YAML inline ficar pesado
Se quiser melhorar os prompts que você está testando, nosso curso Prompt Engineering with the OpenAI API cobre várias técnicas importantes aplicáveis a qualquer desenvolvimento em IA.
FAQs sobre o Promptfoo
O que é o Promptfoo e que problema ele resolve?⌄
Promptfoo é um CLI open source para testar outputs de LLM antes de chegarem aos usuários. Em vez de testar manualmente alguns inputs e "julgar no olho", você define assertions sobre o que é um bom output, escolhe os modelos e roda todas as combinações automaticamente. Ele substitui o teste do tipo "pra mim está bom" por avaliações estruturadas e repetíveis.
Como configurar e rodar sua primeira avaliação no Promptfoo?⌄
Instale o Promptfoo com npm install -g promptfoo, rode promptfoo init para criar o projeto, defina suas chaves de API (por exemplo, OPENAI_API_KEY ou ANTHROPIC_API_KEY) e então escreva um promptfooconfig.yaml com seus prompts, providers e casos de teste. Rode promptfoo eval para executar e promptfoo view para ver os resultados na interface do navegador.
Quais tipos de assertions o Promptfoo suporta e quando usar cada um?⌄
O Promptfoo tem três níveis de assertions. As determinísticas (por exemplo, contains, regex, not-contains, latency, cost) são gratuitas e instantâneas. As assertions assistidas por modelo, como llm-rubric, enviam o output para outro LLM para julgar qualidades subjetivas como tom. Assertions personalizadas em Python permitem escrever qualquer checagem como código inline ou em arquivo separado. Use primeiro as determinísticas, adicione as assistidas para o que é subjetivo e Python para lógica específica do domínio.
Como comparar vários modelos na mesma suíte de testes?⌄
Liste vários providers no seu promptfooconfig.yaml e rode promptfoo eval uma vez. O Promptfoo testa cada modelo contra cada caso de teste e mostra como colunas separadas na matriz de resultados. Use a flag --repeat (por exemplo, promptfoo eval --repeat 3) para rodar cada teste múltiplas vezes por provider e capturar inconsistências de outputs não determinísticos.
Como o Promptfoo se encaixa em um pipeline de CI/CD?⌄
O Promptfoo tem uma GitHub Action oficial (promptfoo/promptfoo-action) que roda sua suíte de evals em todo pull request que altera arquivos de prompt. Ela publica os resultados como comentário no PR com link para o visualizador web. Você adiciona suas chaves de API como secrets do repositório e configura um filtro de paths para que os evals disparem apenas quando prompts mudarem. Assim, mudanças de prompt recebem o mesmo teste obrigatório antes do merge que o código.
Sou criador de conteúdo em ciência de dados há mais de 2 anos e um dos perfis com maior alcance no Medium. Gosto de escrever artigos detalhados sobre IA e ML, com uma pitada de sarcasmo — porque alguém precisa deixar o assunto menos monótono. Já publiquei mais de 130 artigos e um curso na DataCamp, com outro em andamento. Meu conteúdo já alcançou mais de 5 milhões de visualizações, e 20 mil pessoas passaram a me seguir no Medium e no LinkedIn.
Saiba como avaliar modelos de linguagem grandes (LLMs) usando métricas importantes, metodologias e práticas recomendadas para tomar decisões informadas.
Descubra 13 ideias de projetos LLM com guias e códigos fáceis de seguir. Crie sistemas RAG, aplicativos de IA e agentes autônomos usando DeepSeek, LangGraph e OpenAI.
O ajuste fino dos grandes modelos de linguagem (LLMs, Large Language Models) revolucionou o processamento de linguagem natural (PLN), oferecendo recursos sem precedentes em tarefas como tradução de idiomas, análise de sentimentos e geração de textos. Essa abordagem transformadora aproveita modelos pré-treinados como o GPT-2, aprimorando seu desempenho em domínios específicos pelo processo de ajuste fino.
Explore o potencial inexplorado dos modelos de linguagem grandes com o LangChain, uma estrutura Python de código aberto para criar aplicativos avançados de IA.
Saiba como realizar a validação de dados e modelos para garantir um desempenho robusto de machine learning usando nosso guia passo a passo para automatizar testes com o DeepChecks.
A maioria dos recursos com LLM é testada do mesmo jeito: tenta alguns inputs, dá uma olhada no resultado e coloca no ar.
Você fez isso com um gerador de e-mails. Cinco inputs na janela do chat, os outputs pareceram ok. Uma semana depois, metade dos e-mails com tom casual soa como se tivessem sido escritos por um advogado corporativo. Ninguém percebeu porque não havia como perceber. Os prompts mudaram, mas aqueles cinco testes manuais não foram com você.
Promptfoo é um CLI open source que substitui esse processo por avaliações estruturadas e repetíveis. Você define o que é um bom output, escolhe os modelos e roda todas as combinações automaticamente.
A OpenAI adquiriu o projeto em março de 2026, e ele continua sob licença MIT com suporte a dezenas de provedores. Mais de 350 mil desenvolvedores usam a ferramenta, incluindo times em mais de 25% das empresas da Fortune 500.
Este tutorial mostra como configurar o Promptfoo e criar sua primeira suíte de avaliações do zero. Vamos usar um redator de e-mails como exemplo, testando no GPT-5 e no Claude Sonnet 4.6, e integrando tudo ao GitHub Actions no final.
Avaliação de LLM em 60 segundos
Antes de falar da ferramenta, vale entender como funciona o teste de LLM. É diferente de testar código comum.
Se você já testou uma função, conhece o padrão: dá um input e confere se o output bate com o esperado. Com LLMs não é assim. O mesmo prompt pode gerar textos diferentes a cada execução, então você não consegue verificar por igualdade exata.
Em vez disso, você verifica propriedades do output:
É isso que um eval de LLM faz. Ele roda seu prompt contra um conjunto de inputs e verifica cada output com base em regras que você define. Pense como uma suíte de testes para prompts, não para código.
Quatro termos vão aparecer ao longo do artigo:
Sem assertions, você volta ao "pra mim está bom". Com elas, você tem uma definição de "correto" que roda igual sempre.
O que é o Promptfoo?
Agora que você sabe o que é um eval, aqui vai o que o Promptfoo faz com ele.
Você entrega três coisas ao Promptfoo:
Ele roda cada prompt em cada modelo para cada caso de teste e pontua os resultados. Um comando,
promptfoo eval, executa tudo.Suponha que você tenha um prompt de e-mail, dois modelos (GPT-5 e Claude Sonnet 4) e três casos (casual, formal, urgente). O Promptfoo roda as seis combinações e informa o que passou e o que falhou. Chega de testar um por um na mão.
Todo o eval vive em um único arquivo YAML chamado
promptfooconfig.yamlque você versiona junto com o código. O Promptfoo roda na sua máquina: seu config, resultados e cache ficam locais. As únicas chamadas externas são para as APIs de modelo como OpenAI ou Anthropic — que você faria com ou sem o Promptfoo.Há outras ferramentas nesse espaço, como DeepEval (nativa em Python, estilo pytest), LangSmith (monitoramento em produção para LangChain) e Braintrust (dashboards para times). O Promptfoo é o melhor ponto de partida porque é gratuito, roda localmente e leva você do zero a um eval funcionando mais rápido que todos eles.
Configurando seu ambiente com o Promptfoo
Instale o Promptfoo globalmente e inicialize um novo projeto:
O comando
initguia você por uma configuração interativa. Ele pergunta o que você quer fazer (escolha "Not sure yet") e qual provider de modelo usar (escolha "[OpenAI] GPT 5, GPT 4.1, ...").Ao finalizar, você terá dois arquivos: um
promptfooconfig.yamlde exemplo e umREADME.md.Em seguida, defina suas chaves de API. Você precisa de pelo menos uma para rodar os evals (tenha as duas para acompanhar este tutorial):
Se você definir apenas
ANTHROPIC_API_KEYe pular a OpenAI, o Promptfoo usa automaticamente o Claude como provedor de avaliação para assertions assistidas por modelo comollm-rubric.Abra o
promptfooconfig.yamlgerado. Todo config do Promptfoo tem três blocos básicos:promptsé onde entram seus templates de prompt. Espaços reservados com chaves duplas como{{variable}}são preenchidos por cada caso de teste.providerslista os modelos contra os quais você quer testar.testsdefine os inputs e as assertions que decidem se cada output passa ou falha.Esse config de exemplo é só um ponto de partida. Na próxima seção, vamos substituí-lo por uma avaliação real e explicar a estrutura do YAML em detalhes.
Criando sua primeira avaliação
A tarefa é: dado um conjunto de bullet points e um tom (casual, formal ou urgente), escrever um e-mail. Você vai criar um eval que testa isso em dois modelos.
O config completo está disponível como um Gist se quiser ver tudo de uma vez. Vamos montar parte por parte abaixo.
Prompt e providers
Apague o exemplo gerado e crie um novo
promptfooconfig.yaml. Comece pelo prompt e pelos providers:O template de prompt tem dois placeholders:
{{bullet_points}}e{{tone}}. Cada caso de teste preenche esses campos com valores diferentes. O campolabelem cada provider gera cabeçalhos de coluna legíveis nos resultados, em vez de IDs crus de modelo.Bloco defaultTest
Agora, adicione um bloco
defaultTest. As assertions dentro dedefaultTestvalem automaticamente para todos os casos de teste, evitando repetição:Isso reprova qualquer resposta mais lenta que 30 segundos. Modelos de fronteira como o GPT-5 podem levar 10–20 segundos por requisição devido a tokens de raciocínio, então o limite precisa de folga. Você define uma vez e cobre todos os testes.
Casos de teste
Agora adicione os casos de teste. Cada um traz inputs diferentes e suas próprias assertions:
Cada caso de teste combina dois tipos de assertions.
icontainsé uma checagem simples de string: o output incluiu "mockups", sem diferenciar maiúsculas/minúsculas? É rápido, gratuito e não chama API.llm-rubricenvia o output para outro LLM e pede que ele faça a avaliação com base na sua rubrica. Custa tokens, mas pega coisas que matching de string não pega, como se um e-mail realmente soa casual.Avaliação
Rode a avaliação:
Depois, abra os resultados no navegador:
A interface web mostra os providers como colunas e os casos de teste como linhas. Cada célula exibe o status de aprovação/reprovação de cada assertion, e você pode clicar para ver o output completo e os detalhes da avaliação.
Por padrão, o Promptfoo faz cache das respostas de API em disco (TTL de 14 dias), então reexecutar o mesmo eval não custa nada. Use
--no-cachequando quiser respostas novas.Escrevendo assertions
No primeiro eval usamos
icontainsellm-rubric. Esses são dois entre muitos tipos de assertions compatíveis com o Promptfoo. Nesta seção, vamos passar pelas principais categorias e continuar adicionando assertions ao config do redator de e-mails.Assertions determinísticas
Elas rodam localmente, não custam nada e retornam resultados instantaneamente.
Tipo
O que verifica
contains/icontainsSe o output inclui um substring (com ou sem diferenciar maiúsculas)
regexSe o output casa um padrão (capture
{{placeholders}}não preenchidos com\\{.*?\\})not-containsSe o output exclui algo (artefatos de template, recusas, texto de placeholder)
latencySe a resposta chegou em menos de N milissegundos
costSe a resposta custou menos de US$ X
Você já usou o
icontainselatency. Vamos adicionarnot-containsao caso casual. Se o modelo tender ao formal, pode começar com "Dear" em vez de algo casual como "Hey". Para pegar isso, basta uma linha:Adicione isso à lista
assertdo teste casual e rode de novo. Ambos os modelos devem passar: o GPT-5 costuma abrir com "Hey team," e o Claude Sonnet 4 também. Se algum tivesse começado com "Dear Colleagues", essa assertion sinalizaria na hora.Todo tipo de assertion também aceita o prefixo
not-:not-regex,not-equalsetc.Assertions assistidas por modelo
Elas consomem tokens, mas conseguem julgar o que matching de string não consegue. Você já usou
llm-rubricno primeiro eval. A rubrica que você escreve é decisiva.Uma rubrica vaga como "O e-mail soa profissional" não ajuda o avaliador. Uma específica dá o que medir:
Quando você reexecuta o eval com essa rubrica, a saída do avaliador também fica específica:
Quanto mais específica a rubrica, mais consistente e útil fica a avaliação.
O Promptfoo também traz
answer-relevance(o output respondeu à pergunta?) esimilar(similaridade de cosseno contra uma referência via embeddings), ambos úteis para RAG e aplicações de busca.Assertions personalizadas em Python
Quando os tipos nativos não cobrem sua lógica, escreva a sua. No redator de e-mails, você pode querer verificar se o output fica dentro de um tamanho razoável. Aqui vai uma assertion inline que aprova se o e-mail tem entre 50 e 200 palavras:
Adicione isso ao caso casual e rode. No meu teste, ambos os modelos passaram: o GPT-5 ficou em 54 palavras e o Claude Sonnet 4 em 187. Mas guarde essa assertion para a próxima seção, porque ela não passou em todos os lugares.
Para checagens mais complexas, coloque a lógica em um arquivo separado:
Referencie no seu config com
type: pythonevalue: file://assert_length.py. O camporeasonaparece na UI de resultados para você ver exatamente por que um teste passou ou falhou.Quando um teste deve falhar
Veja como é uma falha real.
Adicione a assertion em Python de contagem de palavras aos três casos e rode o eval completo nos dois modelos. No meu caso, cinco das seis combinações passaram. O Claude Sonnet 4.6 não passou no caso de urgência (isso pode ou não acontecer aí, dado o comportamento não determinístico dos LLMs).
Nessa execução, o output ficou com 207 palavras, sete acima do limite. O
llm-rubricpassou, confirmando que o e-mail usou "linguagem urgente e direta" com "ações claras". Oicontainstambém passou, já que "Friday" estava no output.Mas a assertion em Python de contagem de palavras falhou. O Claude colocou um preâmbulo ("Here is a draft email based on your bullet points with an urgent tone:") antes do e-mail em si, empurrando a contagem acima de 200.
Esse é o tipo de coisa que você não pegaria só batendo o olho. O e-mail em si parecia ótimo: tom certo, conteúdo certo. Mas o output ficou longo demais porque o modelo adicionou um texto que não fazia parte do e-mail. Uma assertion pegou isso e o eval sinalizou o caso inteiro.
A correção pode seguir dois caminhos: ajustar o prompt para pedir que o modelo não inclua preâmbulo, ou aumentar o limite de palavras. De qualquer forma, você reexecuta o eval e confere se passou.
Pontuação ponderada
Neste ponto, o caso casual tem quatro assertions:
icontains,not-contains,llm-rubrice a de contagem de palavras em Python. Nem todas têm o mesmo peso. O campoweightpermite expressar isso:A pontuação de cada assertion é multiplicada pelo seu peso, e o teste calcula uma média ponderada. O
thresholddefine a nota mínima para passar. Aqui, a rubrica de tom vale o dobro dos checks por palavra-chave, e a contagem de palavras vale metade.Reexecutando o teste casual com esses pesos, os dois modelos fazem 1,0 e passam. Mas se o
llm-rubricfalhasse (peso 2) enquanto a contagem de palavras passasse (peso 0,5), a pontuação ponderada cairia abaixo de 0,7, e o teste falharia. Os pesos permitem dizer ao Promptfoo o que mais importa para você.Comparando modelos lado a lado
O config já tem dois providers, então
promptfoo evaltestou GPT-5 e Claude Sonnet 4 em uma única passada. Abrapromptfoo viewe você os verá como colunas separadas nos resultados, com cada caso de teste pontuado independentemente por modelo.Na minha execução, o GPT-5 passou nas seis assertions dos três casos. O Claude Sonnet 4 passou em cinco e falhou na contagem de palavras do e-mail urgente.
Esse é o tipo de diferença que você não notaria testando alguns prompts manualmente, mas que aparece na hora na grade. Você está comparando notas contra as mesmas assertions nos mesmos inputs — não sua memória de qual modelo "pareceu melhor" da última vez.
Mas outputs de LLMs não são determinísticos. O mesmo prompt pode gerar resultados diferentes em execuções consecutivas, e uma passada só não diz se um modelo é confiável ou apenas deu sorte. A flag
--repeatajuda nisso:Isso roda cada caso de teste três vezes por provider. Se um modelo passa a assertion de tom duas vezes e falha na terceira, é um sinal de confiabilidade que passaria batido numa passada única.
Do teste local ao CI/CD
Rodar evals localmente funciona no desenvolvimento, mas depende de quem alterou o prompt lembrar de rodá-los. O Promptfoo tem uma GitHub Action oficial que elimina essa dependência ao executar sua suíte de evals em todo pull request e publicar os resultados como comentário.
Para configurar, crie um workflow no seu repositório:
Depois crie
.github/workflows/prompt-eval.ymlcom o conteúdo a seguir:Antes de funcionar, você precisa adicionar sua
OPENAI_API_KEY(e opcionalmenteANTHROPIC_API_KEY) como secrets do repositório no seu GitHub, em Settings > Secrets and variables > Actions.O filtro
pathsfaz a action disparar apenas quando alguém altera arquivos emprompts/. O checkout é necessário porque a action usa git internamente para fazer diff dos arquivos de prompt entre branches. Ela roda a suíte inteira e publica um comentário no PR com os resultados e um link para o visualizador web.Se quiser mais controle sobre a lógica de aprovação/reprovação, você pode analisar o JSON diretamente:
O fluxo a partir daqui:
Mudanças de prompt recebem o mesmo tratamento de testar-antes-de-fazer-merge que mudanças de código.
Conclusão
O redator de e-mails do início ainda tem problema de tom, mas agora existe um teste que pega isso antes dos usuários. Você começou com um YAML em branco e terminou com uma suíte de evals rodando em dois modelos no CI.
A mesma abordagem vale para qualquer recurso com LLM que você construir — chatbot, sumarizador, pipeline de classificação. Se você consegue escrever uma assertion do que é "um bom output", dá para testar automaticamente em vez de só bater o olho.
Quando quiser ir além, a documentação do Promptfoo cobre áreas que valem a pena explorar:
Red teaming:
promptfoo redteam runverifica injeção de prompt e jailbreaks em dezenas de plugins de ataqueProviders personalizados em Python: Encapsule qualquer modelo interno ou endpoint fine-tunado com
file://my_provider.pyDados de teste em CSV: Escalone sua suíte com
file://tests.csvquando o YAML inline ficar pesadoSe quiser melhorar os prompts que você está testando, nosso curso Prompt Engineering with the OpenAI API cobre várias técnicas importantes aplicáveis a qualquer desenvolvimento em IA.