Criando uma estrutura automatizada de avaliação
Crie um fluxo de avaliação repetível que execute seu sistema RAG completo em um conjunto de teste, calcule todas as métricas e gere um relatório para acompanhar as melhorias ao longo do tempo.
Criando uma estrutura automatizada de avaliação é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.
O que é uma estrutura de avaliação?
Uma estrutura de avaliação é um fluxo de processamento automatizado e repetível que executa todo o seu sistema RAG em um conjunto de testes padronizado, calcula todas as métricas e produz um relatório. A palavra-chave é repetível: sempre que você fizer uma alteração na estratégia de divisão em trechos, no modelo de embeddings, no prompt ou no LLM, executará a mesma estrutura e comparará os resultados com uma linha de base. Isso transforma o desenvolvimento de RAG de ajustes subjetivos em engenharia orientada por dados.
Arquitetura da estrutura de avaliação
Uma estrutura de avaliação bem projetada tem quatro camadas: gerenciamento dos dados de teste (carregar e versionar o conjunto de dados de referência), execução do fluxo de processamento (executar cada pergunta de teste em todo o fluxo de processamento RAG), cálculo das métricas (calcular todas as métricas de recuperação e geração) e geração do relatório (salvar os resultados com informações de versão e produzir uma comparação com a linha de base anterior). Cada camada deve poder ser testada e configurada de forma independente.
class RAGEvaluationHarness:
def __init__(self, retriever, llm_client, config):
self.retriever = retriever
self.llm_client = llm_client
self.config = config # chunk_size, top_k, model, threshold, etc.
self.results = []
def run(self, golden_dataset):
for item in golden_dataset:
result = self._evaluate_single(item)
self.results.append(result)
metrics = self._compute_metrics()
self._save_report(metrics)
return metricsExecutando cada caso de teste
Para cada pergunta do conjunto de dados de referência, execute o fluxo de processamento RAG completo e capture todas as saídas intermediárias: os IDs e as pontuações dos trechos recuperados, o contexto formatado, a resposta gerada e a quantidade de tokens. Armazenar esses valores intermediários é essencial para depurar falhas — quando uma pergunta obtiver uma pontuação baixa, você poderá inspecionar exatamente quais trechos foram recuperados e por que a resposta estava errada, sem executar novamente o fluxo de processamento dispendioso.
import time
def _evaluate_single(self, item):
start = time.perf_counter()
query_vector = embed_query(item['question'])
chunks = self.retriever.retrieve(query_vector, top_k=self.config['top_k'])
filtered_chunks = filter_by_score(chunks, self.config['threshold'])
context = format_context(filtered_chunks)
answer_result = generate_answer(item['question'], context, self.llm_client)
latency_ms = (time.perf_counter() - start) * 1000
return {
'question': item['question'],
'expected_answer': item['answer'],
'generated_answer': answer_result['answer'],
'retrieved_chunk_ids': [c['id'] for c in filtered_chunks],
'retrieved_scores': [c['score'] for c in filtered_chunks],
'relevant_chunk_ids': item['relevant_chunk_ids'],
'context_texts': [c['text'] for c in filtered_chunks],
'tokens_used': answer_result['tokens_used'],
'latency_ms': round(latency_ms)
}Calculando todas as métricas em uma única passagem
Depois de coletar as saídas de todos os casos de teste, calcule o conjunto completo de métricas em uma única passagem pelos resultados. Separe as métricas de recuperação (calculadas a partir dos IDs dos trechos) das métricas de geração (calculadas chamando o LLM avaliador). Agrupe as chamadas ao LLM avaliador para maximizar a eficiência — agrupe as avaliações de fidelidade e envie-as em paralelo com asyncio, em vez de sequencialmente. Registre o progresso, pois as métricas de geração podem levar vários minutos para mais de 100 casos de teste.
def _compute_metrics(self):
# Retrieval metrics (no LLM calls needed)
hit_rates = []
mrr_scores = []
for r in self.results:
retrieved = r['retrieved_chunk_ids']
relevant = set(r['relevant_chunk_ids'])
hit = any(rid in relevant for rid in retrieved)
hit_rates.append(1.0 if hit else 0.0)
for rank, rid in enumerate(retrieved, 1):
if rid in relevant:
mrr_scores.append(1.0 / rank)
break
else:
mrr_scores.append(0.0)
metrics = {
'hit_rate_at_5': sum(hit_rates) / len(hit_rates),
'mrr': sum(mrr_scores) / len(mrr_scores),
'mean_latency_ms': sum(r['latency_ms'] for r in self.results) / len(self.results),
'mean_tokens': sum(r['tokens_used'] for r in self.results) / len(self.results)
}
return metricsSalvando resultados com informações de versão
Cada execução de avaliação deve ser salva com metadados de versão, para que seja possível comparar os resultados entre configurações. Inclua o hash do commit do git, os parâmetros de configuração (modelo de embeddings, tamanho dos trechos, K, limiar, modelo de LLM), o registro de data e hora e uma descrição da execução em linguagem natural. Armazene os resultados em um arquivo JSONL ou em uma tabela de banco de dados. Isso cria um histórico permanente da evolução do sistema.
import json
import subprocess
from datetime import datetime
def _save_report(self, metrics):
git_hash = subprocess.check_output(
['git', 'rev-parse', '--short', 'HEAD']
).decode().strip()
report = {
'run_id': datetime.utcnow().strftime('%Y%m%d_%H%M%S'),
'git_commit': git_hash,
'config': self.config,
'metrics': metrics,
'n_test_cases': len(self.results),
'timestamp': datetime.utcnow().isoformat()
}
with open('eval_history.jsonl', 'a') as f:
f.write(json.dumps(report) + '\n')
print(f'Saved evaluation run: {report["run_id"]}')
print(json.dumps(metrics, indent=2))Comparando com a linha de base
Depois de cada execução, compare automaticamente com a linha de base anterior e sinalize regressões. Uma regressão é qualquer métrica que caia mais do que um limiar (por exemplo, 2 pontos percentuais). Imprima uma tabela de diferenças mostrando as alterações nas métricas. Se alguma métrica regredir significativamente, a execução da avaliação deverá falhar com um código de saída diferente de zero, fazendo com que um fluxo de integração e entrega contínuas bloqueie a implantação dessa alteração.
def compare_to_baseline(current_metrics, baseline_file='best_eval.json'):
import json
from pathlib import Path
if not Path(baseline_file).exists():
print('No baseline yet. Saving current as baseline.')
Path(baseline_file).write_text(json.dumps(current_metrics, indent=2))
return True
baseline = json.loads(Path(baseline_file).read_text())
regressions = []
print('\nMetric comparison (current vs baseline):')
for metric, current_val in current_metrics.items():
baseline_val = baseline.get(metric, 0)
delta = current_val - baseline_val
status = 'OK' if delta >= -0.02 else 'REGRESSION'
print(f' {metric}: {current_val:.3f} vs {baseline_val:.3f} ({delta:+.3f}) {status}')
if status == 'REGRESSION':
regressions.append(metric)
return len(regressions) == 0Integrando à integração e entrega contínuas
A estrutura de avaliação é mais poderosa quando integrada ao seu fluxo de integração e entrega contínuas. Configure-a para ser executada automaticamente em toda solicitação de pull que modifique a lógica de divisão em trechos, a configuração do modelo de embeddings, os modelos de prompt ou os parâmetros de recuperação. O fluxo só será aprovado se todas as métricas atingirem os limiares mínimos e nenhuma métrica regredir em relação à linha de base do ramo principal. Isso impede que regressões acidentais de qualidade sejam enviadas para produção.
# GitHub Actions workflow (eval.yml)
# on:
# pull_request:
# paths:
# - 'rag/**'
# - 'prompts/**'
# - 'config/**'
# jobs:
# evaluate:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Install dependencies
# run: pip install -r requirements.txt
# - name: Run evaluation harness
# run: |
# python eval/run_harness.py \
# --test-set eval/golden_dataset.json \
# --config config/rag_config.yaml \
# --fail-on-regression
# env:
# OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}Gerando relatórios legíveis por pessoas
Além dos arquivos de métricas brutas, gere um relatório HTML ou Markdown legível por pessoas que sua equipe possa revisar nos comentários das solicitações de pull. Inclua uma tabela resumida com todas as métricas, uma lista dos casos de teste que falharam com a pergunta, a resposta esperada, a resposta gerada e os trechos recuperados, além de um gráfico de tendências mostrando as métricas das últimas 10 execuções. Relatórios visuais ajudam as partes interessadas não técnicas a entender se o sistema está melhorando.
def generate_markdown_report(metrics, failed_cases, run_id):
lines = [
f'# RAG Evaluation Report — {run_id}\n',
'## Summary Metrics',
'| Metric | Score | Target |',
'|--------|-------|--------|',
f'| Hit Rate@5 | {metrics["hit_rate_at_5"]:.1%} | > 80% |',
f'| MRR | {metrics["mrr"]:.3f} | > 0.70 |',
f'| Mean Latency | {metrics["mean_latency_ms"]:.0f}ms | < 500ms |',
'',
f'## Failed Cases ({len(failed_cases)} failures)'
]
for case in failed_cases[:10]: # show first 10
lines += [
f'**Q:** {case["question"]}',
f'**Expected:** {case["expected_answer"]}',
f'**Generated:** {case["generated_answer"]}\n'
]
return '\n'.join(lines)Acompanhando o custo por execução de avaliação
As execuções de avaliação custam dinheiro — elas chamam a API de embeddings, a API do LLM e o LLM avaliador. Acompanhe o custo de cada execução de avaliação junto com as métricas de qualidade. Uma avaliação abrangente de 100 casos de teste normalmente custa de US$ 0,50 a US$ 2,00, dependendo dos modelos usados. Use modelos mais baratos para as chamadas do avaliador (GPT-4o-mini para pontuação de fidelidade) e reserve os modelos caros para a geração. Inclua o custo estimado da execução no relatório salvo, para que você possa incluir a avaliação no orçamento do seu ciclo de desenvolvimento.
def estimate_run_cost(results, config):
# Embedding cost
embed_tokens = sum(len(r['question'].split()) * 1.3 for r in results)
embed_cost = (embed_tokens / 1_000_000) * 0.02 # $0.02/1M tokens
# Generation cost
total_gen_tokens = sum(r['tokens_used'] for r in results)
gen_cost = (total_gen_tokens / 1_000_000) * 5.0 # gpt-4o approx
# Judge cost (faithfulness evals)
judge_cost = len(results) * 0.001 # ~$0.001 per eval with gpt-4o-mini
total = embed_cost + gen_cost + judge_cost
print(f'Evaluation cost estimate: ${total:.2f}')
print(f' Embedding: ${embed_cost:.3f}')
print(f' Generation: ${gen_cost:.3f}')
print(f' Judgment: ${judge_cost:.3f}')
return totalAvaliação agendada para monitoramento em produção
Além da avaliação de integração e entrega contínuas em alterações de código, execute a estrutura em um agendamento na produção — diariamente ou semanalmente — testando-a com consultas reais de usuários selecionadas dos seus registros. Isso detecta a deriva dos dados: à medida que o conjunto de documentos evolui e os padrões de consulta dos usuários mudam, a qualidade do sistema pode se deteriorar sem nenhuma alteração no código. Agende execuções semanais de avaliação que selecionem 50 consultas recentes de usuários, avaliem-nas e enviem automaticamente um resumo de qualidade ao canal do Slack da sua equipe.
# Example scheduled evaluation (cron job or scheduled cloud function)
import random
def sample_production_queries(query_log_file, n=50):
with open(query_log_file) as f:
all_queries = [json.loads(line) for line in f]
sample = random.sample(all_queries, min(n, len(all_queries)))
# Convert to golden dataset format (without expected answers — use LLM judge)
return [
{'question': q['user_question'], 'relevant_chunk_ids': []}
for q in sample
]
# Run weekly evaluation against production queries
if __name__ == '__main__':
prod_queries = sample_production_queries('/var/log/rag_queries.jsonl')
harness = RAGEvaluationHarness(retriever, llm_client, config)
metrics = harness.run(prod_queries)
send_slack_digest(metrics)Visualizando tendências das métricas ao longo do tempo
Números brutos em um arquivo JSONL são difíceis de interpretar rapidamente. Crie uma visualização simples de tendências que trace cada métrica das últimas 20 execuções de avaliação em um gráfico de linhas. Use o registro de data e hora da execução como eixo x e a pontuação da métrica como eixo y. Trace uma linha horizontal no limiar mínimo aceitável. Quando uma métrica cair abaixo da linha do limiar, o problema ficará imediatamente visível, sem que seja necessário ler os dados brutos. Ferramentas como Matplotlib ou um painel web simples (Grafana, Streamlit) funcionam bem para isso.
import json
import matplotlib.pyplot as plt
from pathlib import Path
def plot_metric_trends(history_file='eval_history.jsonl', metric='hit_rate_at_5'):
records = [
json.loads(line)
for line in Path(history_file).read_text().strip().split('\n')
]
timestamps = [r['timestamp'][:10] for r in records[-20:]]
scores = [r['metrics'].get(metric, 0) for r in records[-20:]]
plt.figure(figsize=(10, 4))
plt.plot(timestamps, scores, marker='o', label=metric)
plt.axhline(y=0.80, color='r', linestyle='--', label='Min threshold')
plt.title(f'{metric} over last 20 evaluations')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig(f'eval_trend_{metric}.png')
print(f'Saved trend chart for {metric}')Verificação rápida
Teste sua compreensão dos conceitos de Engenharia de IA desta lição.
Recapitulação da lição
Nesta lição, você aprendeu: como estruturar uma estrutura completa de avaliação com gerenciamento dos dados de teste, execução do fluxo de processamento, cálculo das métricas e geração de relatórios; como comparar execuções com uma linha de base e fazer a integração e entrega contínuas falhar em caso de regressões; como gerar relatórios legíveis por pessoas para análise da equipe; e como executar o monitoramento agendado em produção para detectar a deriva dos dados sem alterações no código. Agora você tem uma base completa para criar e avaliar sistemas RAG em produção.
Perguntas Frequentes
A aula “Criando uma estrutura automatizada de avaliação” é grátis?
Sim — o texto completo de “Criando uma estrutura automatizada de avaliação” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.
O que vou aprender em “Criando uma estrutura automatizada de avaliação”?
Crie um fluxo de avaliação repetível que execute seu sistema RAG completo em um conjunto de teste, calcule todas as métricas e gere um relatório para acompanhar as melhorias ao longo do tempo. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Engineering Academy?
Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Criando uma estrutura automatizada de avaliação”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Engineering Academy?
Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Por que a avaliação é importante em RAG
- Métricas de recuperação: taxa de acerto, MRR e NDCG
- Métricas de geração: fidelidade e relevância da resposta
- Criando uma estrutura automatizada de avaliação