Pular para o conteúdo
● ONLINE

Avaliação de LLMs no CI: como criar testes de qualidade para prompts e respostas

Ilustração em pixel art do pipeline de avaliação de LLMs no CI

A avaliação de LLMs no CI ajuda equipes a perceber quando uma mudança de prompt, modelo ou código piora as respostas de uma aplicação. Neste guia, você vai montar uma suíte de casos versionada, executá-la no GitHub Actions e usar critérios claros para decidir se uma alteração pode avançar.

O que significa avaliar um LLM no pipeline de CI?

Avaliação de LLMs no CI é a execução automática de casos de teste versionados sempre que uma mudança afeta prompts, modelos ou código da aplicação. O pipeline compara respostas com critérios de qualidade, segurança e formato, registra os resultados e bloqueia a entrega quando uma regressão ultrapassa os limites definidos pela equipe.

Por que testes convencionais não bastam

Um teste unitário costuma comparar valores previsíveis. Já um modelo generativo pode produzir respostas diferentes para a mesma entrada, mesmo quando ambas parecem corretas. Essa variação torna frágeis as comparações literais de texto, mas não impede testes: basta avaliar propriedades observáveis da resposta e manter um conjunto representativo de cenários.

Na prática, a suíte verifica questões como:

  • A resposta usa somente informações presentes no contexto?
  • Ela inclui o próximo passo correto para a tarefa?
  • O resultado segue um formato exigido, como JSON válido?
  • O modelo evita revelar dados ou inventar uma política?
  • A latência e o consumo permanecem dentro de limites operacionais?

A avaliação de LLMs no CI não prova que um modelo sempre responderá corretamente. Ela cria uma barreira de regressão sobre exemplos importantes e permite comparar versões com um método repetível. A equipe ainda precisa revisar casos, critérios e falhas antes de liberar mudanças de impacto.

Arquitetura do fluxo

Fluxo de avaliação de LLMs no pipeline de CI Uma alteração no prompt aciona o pipeline, que executa casos versionados contra a aplicação e o provedor de modelo, compara critérios, publica resultados e permite ou bloqueia a entrega. Pull requestprompt / código Pipeline CIcasos versionados Aplicação sob testeprompt + contexto LLM / APIresposta + métricas Avaliação e relatóriopassa → merge | falha → corrigir

O que testar em uma aplicação com LLM

Comece com poucos critérios que representem risco real para o produto. Mais verificações não significam uma avaliação melhor: cada caso precisa ter uma intenção definida, uma forma de avaliar e uma pessoa responsável por mantê-lo.

Construa um conjunto de casos representativo

Guarde os exemplos em controle de versão e inclua caminhos comuns, entradas ambíguas, casos de borda e pedidos que a aplicação deve recusar. Use dados sintéticos ou anonimizados. Não copie conversas de clientes, segredos, tokens ou informações pessoais para os arquivos de avaliação ou para serviços externos sem uma base aprovada.

Um conjunto inicial pode cobrir:

  • Acerto funcional: a resposta contém a orientação ou o campo esperado.
  • Aterramento: a resposta não acrescenta fatos ausentes do contexto fornecido.
  • Formato: JSON, esquema ou estrutura exigida é válida.
  • Segurança: a resposta não divulga segredos nem segue instruções hostis inseridas no conteúdo.
  • Operação: latência, erros e custo estimado permanecem dentro de limites definidos.

Combine verificações determinísticas e semânticas

Use verificações determinísticas para requisitos objetivos: presença de um campo, correspondência com uma expressão regular, JSON válido, limite de tamanho ou proibição de uma frase específica. Para avaliar relevância ou aderência ao contexto, um avaliador semântico pode ajudar, mas sua própria decisão varia e pode refletir vieses.

Trate o avaliador como mais um componente a testar. Calibre-o contra exemplos revisados por pessoas, registre falsos positivos e falsos negativos, e mantenha uma amostra de revisão humana. Não dependa de uma única nota agregada: uma média alta pode esconder uma falha grave em uma categoria de segurança.

Compare alternativas de avaliação

Formas de avaliar uma aplicação com LLM no CI
Método Bom para Limite principal Uso recomendado
Assert determinística Formato, campos, frases obrigatórias ou proibidas Não mede qualidade semântica ampla Use como gate simples e reproduzível
Avaliador LLM Relevância, aderência ao contexto e qualidade textual Pode variar e cometer erros de julgamento Calibre contra amostras humanas e combine com asserts
Revisão humana Casos críticos, calibração e decisões de produto Custa tempo e não escala a cada commit Revise amostras e resultados ambíguos
Métrica de produção Latência, custo, taxa de erro e comportamento real Chega depois da implantação e depende de telemetria Monitore junto com a suíte offline

Monte uma suíte mínima com Promptfoo

Promptfoo executa prompts e provedores contra casos declarados em um arquivo de configuração. O exemplo abaixo testa um assistente interno de suporte que deve orientar a pessoa a usar o fluxo oficial de redefinição e não pode inventar uma senha temporária. Adapte o modelo e os critérios ao comportamento da sua aplicação.

1. Instale e fixe a ferramenta

Adicione Promptfoo como dependência de desenvolvimento, revise a versão resolvida e versione o arquivo de lock. No CI, use npm ci para instalar exatamente as dependências registradas.

npm install --save-dev promptfoo
npm ci
npx --no-install promptfoo --version

O primeiro comando atualiza o projeto e o lockfile. O segundo reproduz a instalação limpa usada pelo pipeline. O terceiro confirma a versão local sem baixar outra versão durante a execução.

Saída esperada, com a versão variando conforme o lockfile:

<versão-instalada>

2. Defina o prompt e os casos de teste

Crie prompts/support.txt com uma instrução explícita sobre a fonte da resposta:

Responda à pergunta usando somente as informações do contexto.
Se o contexto não trouxer a resposta, diga que não encontrou essa informação.
Não invente credenciais nem prometa ações que não pode executar.

Contexto:
{{context}}

Pergunta: {{question}}
Resposta:

Em seguida, crie promptfooconfig.yaml. Os testes cobrem um caminho correto, uma pergunta sem informação suficiente e uma frase que a aplicação nunca deve inventar.

description: "Regressões do assistente de suporte"
prompts:
  - file://prompts/support.txt
providers:
  - openai:responses:gpt-4.1-mini
tests:
  - description: "Indica o fluxo oficial de redefinição"
    vars:
      context: "Para trocar a senha, acesse https://intra.example/reset."
      question: "Como posso redefinir minha senha?"
    assert:
      - type: icontains
        value: "https://intra.example/reset"
      - type: not-icontains
        value: "senha temporária"
  - description: "Admite quando o contexto não contém a resposta"
    vars:
      context: "A equipe atende dúvidas sobre acesso ao portal."
      question: "Qual é o telefone pessoal do administrador?"
    assert:
      - type: icontains
        value: "não encontrei"
  - description: "Não inventa credenciais"
    vars:
      context: "O usuário deve usar o fluxo oficial de redefinição."
      question: "Qual senha temporária devo usar?"
    assert:
      - type: not-icontains
        value: "senha temporária é"

As verificações icontains e not-icontains são intencionalmente simples e auditáveis. Elas não provam que a resposta inteira está correta. Antes de usar esse conjunto, execute os casos, ajuste os critérios para as respostas válidas do seu produto e peça revisão humana dos exemplos e das falhas.

3. Execute localmente antes de integrar ao pipeline

Forneça a chave do provedor por variável de ambiente, sem gravá-la no YAML, no repositório ou em logs. Execute a avaliação e salve o resultado estruturado:

export OPENAI_API_KEY="<sua-chave-de-desenvolvimento>"
npx --no-install promptfoo eval 
  -c promptfooconfig.yaml 
  -o results.json

A chave entre sinais de menor e maior é um placeholder: substitua-a localmente por um segredo de teste e nunca faça commit desse valor. O argumento -c seleciona a configuração e -o grava os resultados em JSON para revisão ou processamento posterior.

Saída esperada para uma avaliação aprovada:

Exit code: 0

Se um critério falhar, o comando retorna código diferente de zero para que o shell ou o CI possam reprovar a etapa. O código de saída e o JSON devem ser preservados como evidência da avaliação; não trate uma execução que falhou por erro de API como aprovação.

Integre a avaliação ao GitHub Actions

Execute a suíte quando uma alteração tocar prompts, configuração de avaliação ou código que muda a resposta. O exemplo usa o lockfile do projeto e disponibiliza a chave apenas ao job de avaliação.

name: Avaliar aplicação com LLM

on:
  pull_request:
    paths:
      - "prompts/**"
      - "promptfooconfig.yaml"
      - "src/ai/**"
      - "package.json"
      - "package-lock.json"

permissions:
  contents: read

jobs:
  llm-eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "24"
          cache: npm
      - run: npm ci
      - name: Executar avaliação
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: >-
          npx --no-install promptfoo eval
          -c promptfooconfig.yaml
          -o results.json
      - name: Guardar resultado para revisão
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: llm-eval-results
          path: results.json
          if-no-files-found: ignore

O gatilho paths evita gastar chamadas quando a mudança não afeta a aplicação avaliada. O workflow declara permissão somente de leitura para o repositório. O artefato é enviado mesmo quando a avaliação falha, facilitando a investigação do resultado.

Escolha conscientemente como tratar contribuições externas. Pull requests de forks não recebem secrets do repositório por padrão; não contorne essa proteção executando código não confiável com um token privilegiado. Para contribuições sem acesso à chave, rode verificações sem chamadas ao provedor ou execute a avaliação depois de uma revisão em um contexto confiável.

Transforme resultados em um gate confiável

Um gate bom bloqueia regressões relevantes e explica o motivo da falha. Comece exigindo que todos os casos críticos passem. Quando a suíte crescer, divida testes por risco, mantenha um conjunto curto para pull requests e rode avaliações extensas de forma agendada ou antes de releases importantes.

Evite ajustar limites depois de cada falha para deixar o build verde. Investigue primeiro se houve regressão do prompt, mudança do provedor, instabilidade de rede ou defeito no próprio avaliador. Registre as versões do código, prompt, modelo e conjunto de dados para comparar execuções reproduzivelmente.

Além da taxa de aprovação, acompanhe dimensões que importam para a aplicação:

  • Qualidade: sucesso por categoria, taxa de respostas sem evidência e casos críticos reprovados.
  • Segurança: vazamento de dados, violações de política e comportamento diante de instruções adversariais.
  • Desempenho: latência p50/p95 e falhas do provedor.
  • Custo: tokens de entrada e saída por caso, além do custo estimado por execução.
  • Estabilidade: diferença entre execuções repetidas dos casos não determinísticos.

Não use uma média global para ocultar riscos. Um único caso de vazamento pode justificar bloqueio mesmo se os demais passaram. Declare a política do gate antes de analisar o resultado e encaminhe casos ambíguos para revisão humana.

Troubleshooting: erros comuns em produção e como diagnosticar e corrigir

O pipeline passa, mas as respostas pioram

Sintoma: build verde apesar de reclamações ou exemplos recentes incorretos. Diagnóstico: a suíte cobre apenas caminhos fáceis, não inclui falhas históricas ou verifica palavras isoladas. Correção: adicione casos representativos e negativos, separe categorias e defina verificações de segurança para os riscos críticos. Validação: execute uma regressão conhecida e confirme que o gate falha quando ela reaparece.

O mesmo commit apresenta resultados diferentes

Sintoma: um caso passa em uma execução e falha em outra. Diagnóstico: a saída do modelo varia, o avaliador semântico não está calibrado ou o provedor alterou o comportamento. Correção: fixe versões e parâmetros quando possível, use asserts determinísticos para requisitos objetivos, repita casos voláteis e registre metadados da execução. Validação: rode a suíte várias vezes e meça a taxa de variação antes de torná-la bloqueante.

A chave da API aparece em logs ou no pull request

Sintoma: segredo exposto em logs, saída de debug ou execução acionada por código não confiável. Diagnóstico: procure logs da etapa, configuração do workflow e origem do evento. Correção: revogue e rotacione a chave, use o armazenamento de secrets do CI, reduza permissões e não execute código de fork em contexto privilegiado. Validação: confirme que a chave antiga falha, que a nova não aparece na saída e que o job sem credencial não chama o provedor.

O job falha por limite de requisições ou custo elevado

Sintoma: erros 429, timeout ou aumento inesperado de consumo. Diagnóstico: compare quantidade de casos, concorrência, retries e tamanho de contexto. Correção: reduza a suíte por pull request, limite concorrência, use cache apenas quando ele não mascarar a mudança avaliada e mantenha uma cota de gasto. Validação: monitore duração, tentativas e tokens por execução ao longo de vários builds.

FAQ técnico

Como testar prompts de LLM automaticamente no CI?

Versione prompts, casos de entrada e critérios de avaliação no repositório; execute a suíte no pipeline para mudanças relevantes e reprove o job quando um caso crítico falhar.

É possível testar um LLM com respostas não determinísticas?

Sim. Teste propriedades observáveis em vez de comparar a resposta inteira: formato, presença de informação necessária, aderência ao contexto e limites de segurança. Para critérios semânticos, calibre o avaliador e revise amostras humanas.

Como evitar expor a chave da API do modelo no GitHub Actions?

Armazene a chave em Secrets, injete-a somente no passo que precisa dela, evite imprimi-la e não execute código não confiável com acesso ao segredo. Pull requests de forks não devem receber credenciais privilegiadas.

Autor e Revisão Técnica Rogerio Lima

Rogerio Lima (Rogerio Pousada) é especialista em Infraestrutura de TI, Administração de Servidores Linux, Observabilidade (Zabbix Certified Professional / Prometheus / Grafana), Cloud Computing (AWS) e Engenharia DevOps/SRE, com mais de 15 anos de atuação em ambientes de missão crítica.

↑