Plano de Implementação de Avaliação de Agentes - Google ADK¶
Baseado em: Google ADK Evaluation Documentation
Data: 04/01/2026
Projeto: iFriend Agents Platform
📋 Índice¶
- Visão Geral
- Objetivos e Critérios de Sucesso
- O que Avaliar
- Estrutura de Avaliação
- Implementação por Fases
- Roadmap de Implementação
- Métricas e KPIs
🎯 Visão Geral¶
Por que Avaliar Agentes?¶
Diferente de software tradicional onde testes unit/integration retornam pass/fail determinísticos, agentes LLM são probabilísticos e exigem avaliação qualitativa de:
- Trajetória (sequência de ações/tools)
- Resposta Final (qualidade, relevância, correção)
Contexto do Projeto iFriend¶
O iFriend possui um agente complexo com múltiplos estados e ferramentas:
- Estados: DISCOVERY → SELECTION → QUOTE → IDENTIFICATION → BOOKING → PAYMENT → DONE
- ~27 Tools: busca, detalhes, disponibilidade, preço, booking, payment, FAQ, etc.
- Multi-turn conversations: conversas longas com contexto mantido
- Callbacks: load_memories, save_session_to_memory
🎯 Objetivos e Critérios de Sucesso¶
1. Definir Sucesso¶
O que é uma execução bem-sucedida?
- ✅ O agente segue a state machine corretamente
- ✅ Executa tools apropriados em cada estado
- ✅ Não pula estados obrigatórios
- ✅ Faz perguntas claras quando faltam dados
- ✅ Completa fluxo end-to-end de reserva+pagamento
- ✅ Respostas curtas e objetivas (UX Slack)
2. Tarefas Críticas¶
Tarefas essenciais que o agente DEVE executar:
- Busca de Produtos
- Entender destino/data do usuário
- Executar busca_produtos_tool
-
Apresentar resultados formatados
-
Consulta de Disponibilidade
- Verificar calendário (disponibilidade_calendario_tool)
- Verificar horários (disponibilidade_horarios_tool)
-
Informar corretamente disponibilidade
-
Cálculo de Preço
- Executar calcular_preco_tool com parâmetros corretos
- Aplicar cotação de moeda se necessário
-
Apresentar preço final claro
-
Fluxo de Booking
- Buscar/criar usuário (buscar_usuario_tool)
- Buscar/criar agência (buscar_agencia_tool)
- Validar dados (validar_dados_reserva_tool)
-
Emitir reserva (emitir_reserva_*)
-
Processamento de Pagamento
- Verificar parcelas (verificar_parcelas_tool)
- Gerar token cartão (gerar_token_cartao_tool)
-
Processar pagamento (processar_pagamento_tool)
-
Orçamento e Proposta Comercial ⭐ CENÁRIO MAIS FREQUENTE
- Buscar produtos conforme demanda
- Calcular preços para múltiplos produtos/pessoas
- Gerar CSV consolidado (gerar_csv_tool)
- Enviar proposta formatada por email (enviar_email_mailersend_tool)
-
Incluir todas informações relevantes (preços, disponibilidade, detalhes)
-
FAQ e Suporte
- Responder perguntas comuns via faq_tool
- Manter contexto da conversa
3. Métricas Escolhidas¶
Baseadas nas recomendações do Google ADK:
| Métrica | Threshold | Quando Usar |
|---|---|---|
tool_trajectory_avg_score |
1.0 | CI/CD, regression tests (fast, deterministic) |
response_match_score |
0.8 | Similaridade ROUGE-1 com resposta esperada |
final_response_match_v2 |
0.9 | Equivalência semântica via LLM |
rubric_based_tool_use_quality_v1 |
Custom | Validar ordem/lógica de tools |
rubric_based_final_response_quality_v1 |
Custom | Qualidade de resposta (tom, concisão) |
hallucinations_v1 |
0.9 | Respostas baseadas em dados reais |
safety_v1 |
1.0 | Conteúdo seguro e apropriado |
🔍 O que Avaliar¶
1. Trajetória e Uso de Tools¶
Exemplo de trajetória esperada (busca simples):
{
"expected_steps": [
"busca_produtos_tool",
"detalhes_experience",
"calcular_preco_tool"
],
"actual_steps": [
"busca_produtos_tool",
"detalhes_experience",
"calcular_preco_tool"
]
}
Exemplo de trajetória esperada (booking completo):
{
"expected_steps": [
"busca_produtos_tool",
"disponibilidade_calendario_tool",
"calcular_preco_tool",
"buscar_usuario_tool",
"buscar_agencia_tool",
"validar_dados_reserva_tool",
"emitir_reserva_experience_tool",
"verificar_parcelas_tool",
"gerar_token_cartao_tool",
"processar_pagamento_tool"
]
}
2. Resposta Final¶
Critérios de Qualidade:
- ✅ Concisa (Slack UX)
- ✅ Estruturada (listas numeradas)
- ✅ Uma pergunta por vez
- ✅ Pergunta no final
- ✅ Informações corretas
3. Estados e Transições¶
Validar que o agente:
- Nunca pula estados
- Executa tudo possível no estado atual
- Pergunta dados faltantes UMA vez
- Não descreve planos futuros
🏗️ Estrutura de Avaliação¶
Abordagem 1: Test Files (Unit Tests)¶
Ideal para: Desenvolvimento ativo, testes rápidos, CI/CD
Estrutura de diretórios:
ifriend_agent/
├── evaluations/
│ ├── unit/
│ │ ├── test_config.json
│ │ ├── busca_produtos.test.json
│ │ ├── disponibilidade.test.json
│ │ ├── calcular_preco.test.json
│ │ ├── faq.test.json
│ │ └── booking_simple.test.json
│ ├── integration/
│ │ ├── test_config.json
│ │ ├── booking_completo.evalset.json
│ │ ├── multi_turn_conversation.evalset.json
│ │ └── payment_flow.evalset.json
│ └── scenarios/
│ ├── user_simulation_config.json
│ └── complex_scenarios.evalset.json
Abordagem 2: Evalset Files (Integration Tests)¶
Ideal para: Conversas complexas multi-turn, testes menos frequentes
Características:
- Múltiplas sessões por arquivo
- Conversas longas e complexas
- Simulação de usuários reais
- Requer Vertex Gen AI Evaluation Service API (pago)
🚀 Implementação por Fases¶
Fase 1: Setup Inicial e Infraestrutura 🏗️¶
Duração: 1 semana
Prioridade: CRÍTICA
1.1 Estrutura de Diretórios¶
mkdir -p ifriend_agent/evaluations/{unit,integration,scenarios}
mkdir -p ifriend_agent/tests/evaluation
1.2 Configurações Básicas¶
Arquivo: ifriend_agent/evaluations/unit/test_config.json
{
"criteria": {
"tool_trajectory_avg_score": 1.0,
"response_match_score": 0.8
}
}
Arquivo: ifriend_agent/evaluations/integration/test_config.json
{
"criteria": {
"tool_trajectory_avg_score": 0.95,
"response_match_score": 0.75,
"final_response_match_v2": 0.9,
"hallucinations_v1": 0.9
}
}
1.3 Pytest Infrastructure¶
Arquivo: ifriend_agent/tests/evaluation/conftest.py
import pytest
from google.adk.evaluation.agent_evaluator import AgentEvaluator
@pytest.fixture
def evaluator():
"""Fixture para AgentEvaluator."""
return AgentEvaluator
@pytest.fixture
def agent_module_path():
"""Path para o módulo do agente."""
return "ifriend_agent"
Fase 2: Test Cases Básicos (Unit Tests) 🧪¶
Duração: 2 semanas
Prioridade: ALTA
2.1 Template de Test File¶
Arquivo: ifriend_agent/evaluations/unit/busca_produtos.test.json
{
"eval_set_id": "busca_produtos_basic",
"name": "Busca de Produtos - Cenários Básicos",
"description": "Testes de busca de produtos com diferentes parâmetros",
"eval_cases": [
{
"eval_id": "busca_paris_torre_eiffel",
"conversation": [
{
"invocation_id": "test-001",
"user_content": {
"parts": [{"text": "Quero conhecer a Torre Eiffel em Paris"}],
"role": "user"
},
"final_response": {
"parts": [{
"text": "Encontrei experiências em Paris relacionadas à Torre Eiffel:\n\n1. Tour pela Torre Eiffel com Guia\n2. Experiência Noturna na Torre Eiffel\n3. Combo Torre Eiffel + Cruzeiro no Sena\n\nQual dessas opções te interessa mais?"
}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "busca_produtos_tool",
"args": {
"destino": "Paris",
"palavras_chave": "Torre Eiffel"
}
}
],
"intermediate_responses": []
}
}
],
"session_input": {
"app_name": "ifriend_agent",
"user_id": "test_user_001",
"state": {}
}
}
]
}
2.2 Exemplo Completo: Orçamento + Email ⭐¶
Arquivo: ifriend_agent/evaluations/unit/orcamento_email.test.json
{
"eval_set_id": "orcamento_proposta_email",
"name": "Orçamento e Proposta Comercial por Email",
"description": "Cenário comercial mais frequente: criar orçamento e enviar proposta",
"eval_cases": [
{
"eval_id": "orcamento_familia_paris",
"conversation": [
{
"invocation_id": "quote-001",
"user_content": {
"parts": [{
"text": "Preciso de um orçamento para uma família de 4 pessoas visitando Paris em março. Querem conhecer Torre Eiffel e Louvre. Envie para cliente@email.com"
}],
"role": "user"
},
"final_response": {
"parts": [{
"text": "Perfeito! Preparei um orçamento personalizado para 4 pessoas em Paris com Torre Eiffel e Louvre.\n\nEncontrei:\n1. Tour Torre Eiffel - R$ 450/pessoa\n2. Visita Guiada Louvre - R$ 380/pessoa\n\nTotal: R$ 3.320,00 para 4 pessoas\n\nEnviei a proposta detalhada para cliente@email.com com:\n✓ Detalhes completos dos passeios\n✓ Disponibilidade em março\n✓ Preços e condições\n✓ Link para reservar\n\nO email foi enviado com sucesso! 📧"
}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "busca_produtos_tool",
"args": {
"destino": "Paris",
"palavras_chave": "Torre Eiffel Louvre"
}
},
{
"name": "detalhes_experience",
"args": {"product_id": "exp-torre-eiffel"}
},
{
"name": "detalhes_experience",
"args": {"product_id": "exp-louvre"}
},
{
"name": "calcular_preco_tool",
"args": {
"product_id": "exp-torre-eiffel",
"quantidade": 4
}
},
{
"name": "calcular_preco_tool",
"args": {
"product_id": "exp-louvre",
"quantidade": 4
}
},
{
"name": "gerar_csv_tool",
"args": {
"produtos": [
{"nome": "Tour Torre Eiffel", "preco": 1800, "pessoas": 4},
{"nome": "Visita Louvre", "preco": 1520, "pessoas": 4}
]
}
},
{
"name": "enviar_email_mailersend_tool",
"args": {
"destinatario": "cliente@email.com",
"assunto": "Proposta Paris - Torre Eiffel e Louvre para 4 pessoas",
"corpo": "...",
"anexos": ["orcamento.csv"]
}
}
],
"intermediate_responses": []
}
}
],
"session_input": {
"app_name": "ifriend_agent",
"user_id": "comercial_test_001",
"state": {}
}
},
{
"eval_id": "orcamento_grupo_roma",
"conversation": [
{
"invocation_id": "quote-002",
"user_content": {
"parts": [{
"text": "Grupo de 15 pessoas quer fazer Coliseu e Vaticano em Roma. Preciso orçamento urgente para agencia@travel.com"
}],
"role": "user"
},
"final_response": {
"parts": [{
"text": "Orçamento para grupo de 15 pessoas em Roma pronto!\n\n1. Tour Coliseu - R$ 320/pessoa = R$ 4.800\n2. Visita Vaticano - R$ 280/pessoa = R$ 4.200\n\n💰 Total: R$ 9.000,00\n\n✅ Proposta enviada para agencia@travel.com\n\nIncluí:\n- Tabela de preços detalhada\n- Disponibilidade\n- Condições especiais para grupos\n- Contato direto para dúvidas"
}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "busca_produtos_tool",
"args": {"destino": "Roma", "palavras_chave": "Coliseu Vaticano"}
},
{
"name": "calcular_preco_tool",
"args": {"product_id": "exp-coliseu", "quantidade": 15}
},
{
"name": "calcular_preco_tool",
"args": {"product_id": "exp-vaticano", "quantidade": 15}
},
{
"name": "gerar_csv_tool",
"args": {"produtos": [...]}
},
{
"name": "enviar_email_mailersend_tool",
"args": {
"destinatario": "agencia@travel.com",
"assunto": "Orçamento Grupo Roma - 15 pessoas",
"corpo": "..."
}
}
],
"intermediate_responses": []
}
}
],
"session_input": {
"app_name": "ifriend_agent",
"user_id": "comercial_test_002",
"state": {}
}
}
]
}
2.2 Casos de Teste Essenciais¶
Criar test files para:
- ✅
busca_produtos.test.json - Busca por destino
- Busca por palavras-chave
- Busca com datas
-
Busca sem resultados
-
✅
disponibilidade.test.json - Verificar calendário
- Verificar horários
-
Produto sem disponibilidade
-
✅
calcular_preco.test.json - Preço básico
- Preço com variações
-
Preço com cotação de moeda
-
✅
faq.test.json - Perguntas sobre política de cancelamento
- Perguntas sobre pagamento
-
Perguntas gerais
-
✅
detalhes_produto.test.json - Detalhes de experience
- Detalhes de guia
-
Detalhes com variações
-
✅
orcamento_email.test.json⭐ CENÁRIO COMERCIAL PRINCIPAL - Orçamento para 1 produto
- Orçamento para múltiplos produtos
- Orçamento com variações de preço
- Envio de proposta por email
- Formato de email adequado
2.3 Pytest Tests¶
Arquivo: ifriend_agent/tests/evaluation/test_unit_evaluations.py
import pytest
from google.adk.evaluation.agent_evaluator import AgentEvaluator
@pytest.mark.asyncio
async def test_busca_produtos():
"""Testa busca de produtos básica."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/unit/busca_produtos.test.json",
)
@pytest.mark.asyncio
async def test_disponibilidade():
"""Testa verificação de disponibilidade."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/unit/disponibilidade.test.json",
)
@pytest.mark.asyncio
async def test_calcular_preco():
"""Testa cálculo de preço."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/unit/calcular_preco.test.json",
)
@pytest.mark.asyncio
async def test_faq():
"""Testa ferramenta de FAQ."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/unit/faq.test.json",
)
@pytest.mark.asyncio
async def test_orcamento_email():
"""Testa fluxo de orçamento e envio de proposta por email."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/unit/orcamento_email.test.json",
)
@pytest.mark.asyncio
async def test_all_unit_tests():
"""Executa todos os testes unitários de uma vez."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/unit/",
)
Fase 3: Evalsets Complexos (Integration Tests) 🔄¶
Duração: 2 semanas
Prioridade: MÉDIA
3.1 Template de Evalset¶
Arquivo: ifriend_agent/evaluations/integration/booking_completo.evalset.json
{
"eval_set_id": "booking_flow_complete",
"name": "Fluxo Completo de Booking",
"description": "Testes de fluxo end-to-end de reserva com múltiplos turnos",
"eval_cases": [
{
"eval_id": "booking_experience_paris",
"conversation": [
{
"invocation_id": "turn-001",
"user_content": {
"parts": [{"text": "Quero reservar um tour pela Torre Eiffel para 2 pessoas no dia 15/02/2026"}],
"role": "user"
},
"final_response": {
"parts": [{
"text": "Perfeito! Encontrei tours disponíveis para a Torre Eiffel em 15/02/2026.\n\n1. Tour Guiado Manhã (9h) - R$ 450/pessoa\n2. Tour Noturno (19h) - R$ 520/pessoa\n\nQual horário prefere?"
}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "busca_produtos_tool",
"args": {"destino": "Paris", "palavras_chave": "Torre Eiffel"}
},
{
"name": "disponibilidade_calendario_tool",
"args": {"product_id": "exp-123", "data": "2026-02-15"}
},
{
"name": "calcular_preco_tool",
"args": {"product_id": "exp-123", "quantidade": 2}
}
],
"intermediate_responses": []
}
},
{
"invocation_id": "turn-002",
"user_content": {
"parts": [{"text": "Tour da manhã às 9h"}],
"role": "user"
},
"final_response": {
"parts": [{
"text": "Ótimo! Tour Guiado Manhã (9h) para 2 pessoas.\n\nPara confirmar a reserva, preciso de:\n1. Seu nome completo\n2. CPF\n3. Email\n4. Telefone"
}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [],
"intermediate_responses": []
}
},
{
"invocation_id": "turn-003",
"user_content": {
"parts": [{"text": "João Silva, CPF 123.456.789-00, joao@email.com, (11) 98765-4321"}],
"role": "user"
},
"final_response": {
"parts": [{
"text": "Dados recebidos! Estou criando sua conta e emitindo a reserva..."
}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "buscar_usuario_tool",
"args": {"cpf": "12345678900"}
},
{
"name": "criar_conta_viajante_tool",
"args": {
"nome": "João Silva",
"cpf": "12345678900",
"email": "joao@email.com",
"telefone": "11987654321"
}
},
{
"name": "validar_dados_reserva_tool",
"args": {}
},
{
"name": "emitir_reserva_experience_tool",
"args": {}
}
],
"intermediate_responses": []
}
},
{
"invocation_id": "turn-004",
"user_content": {
"parts": [{"text": "Como posso pagar?"}],
"role": "user"
},
"final_response": {
"parts": [{
"text": "Reserva #REV-789 confirmada! Total: R$ 900,00\n\nAceitamos:\n- Cartão de crédito (até 12x)\n- PIX\n\nComo prefere pagar?"
}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "verificar_parcelas_tool",
"args": {"valor": 900.00}
}
],
"intermediate_responses": []
}
}
],
"session_input": {
"app_name": "ifriend_agent",
"user_id": "integration_test_user",
"state": {}
}
}
]
}
3.2 Exemplo: Evalset Comercial Completo ⭐¶
Arquivo: ifriend_agent/evaluations/integration/comercial_orcamentos.evalset.json
{
"eval_set_id": "comercial_orcamentos_complexos",
"name": "Cenários Comerciais Reais - Orçamentos e Propostas",
"description": "Conversas reais de atendimento comercial com múltiplos turnos",
"eval_cases": [
{
"eval_id": "negociacao_grupo_europa",
"conversation": [
{
"invocation_id": "comercial-turn-001",
"user_content": {
"parts": [{"text": "Tenho um grupo de 20 pessoas querendo fazer Europa: Paris, Roma e Barcelona. Preciso de orçamento completo."}],
"role": "user"
},
"final_response": {
"parts": [{"text": "Ótimo! Para montar o orçamento completo preciso de mais algumas informações:\n\n1. Quais as datas aproximadas da viagem?\n2. Em cada cidade, há atrações específicas de interesse?\n3. Qual o perfil do grupo (família, executivo, estudantes)?"}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [],
"intermediate_responses": []
}
},
{
"invocation_id": "comercial-turn-002",
"user_content": {
"parts": [{"text": "Junho de 2026. Em Paris querem Torre Eiffel e Louvre. Roma: Coliseu e Vaticano. Barcelona: Sagrada Família. São idosos, grupo da terceira idade."}],
"role": "user"
},
"final_response": {
"parts": [{"text": "Perfeito! Estou buscando as melhores opções para grupo de terceira idade com acessibilidade...\n\n📍 Paris (2 experiências)\n1. Torre Eiffel com elevador - R$ 450/pessoa\n2. Louvre com guia especializado - R$ 380/pessoa\n\n📍 Roma (2 experiências)\n3. Coliseu com acesso prioritário - R$ 320/pessoa\n4. Vaticano tour acessível - R$ 350/pessoa\n\n📍 Barcelona (1 experiência)\n5. Sagrada Família tour completo - R$ 290/pessoa\n\n💰 Subtotal: R$ 1.790/pessoa × 20 = R$ 35.800\n\n✨ Desconto grupo (20+ pessoas): 10% = R$ 32.220\n\nPosso enviar a proposta detalhada para qual email?"}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{"name": "busca_produtos_tool", "args": {"destino": "Paris", "palavras_chave": "Torre Eiffel Louvre acessível"}},
{"name": "busca_produtos_tool", "args": {"destino": "Roma", "palavras_chave": "Coliseu Vaticano acessível"}},
{"name": "busca_produtos_tool", "args": {"destino": "Barcelona", "palavras_chave": "Sagrada Família"}},
{"name": "calcular_preco_tool", "args": {"product_id": "exp-001", "quantidade": 20}},
{"name": "calcular_preco_tool", "args": {"product_id": "exp-002", "quantidade": 20}},
{"name": "calcular_preco_tool", "args": {"product_id": "exp-003", "quantidade": 20}},
{"name": "calcular_preco_tool", "args": {"product_id": "exp-004", "quantidade": 20}},
{"name": "calcular_preco_tool", "args": {"product_id": "exp-005", "quantidade": 20}}
],
"intermediate_responses": []
}
},
{
"invocation_id": "comercial-turn-003",
"user_content": {
"parts": [{"text": "comercial@agenciaviagens.com.br - mas o valor ficou acima do orçamento deles. Tem como fechar em R$ 30.000?"}],
"role": "user"
},
"final_response": {
"parts": [{"text": "Entendo! Vou ajustar a proposta para ficar no orçamento de R$ 30.000.\n\nOpções:\n\n🎯 Opção 1: Remover Sagrada Família\n= R$ 29.400 (dentro do budget)\n\n🎯 Opção 2: Trocar por experiências econômicas\n- Manter as 5 atrações mas com tours básicos\n= R$ 28.800\n\nQual opção prefere que eu envie?"}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{"name": "calcular_preco_tool", "args": {"produtos_alternativos": true}}
],
"intermediate_responses": []
}
},
{
"invocation_id": "comercial-turn-004",
"user_content": {
"parts": [{"text": "Opção 2, mantendo todas as atrações"}],
"role": "user"
},
"final_response": {
"parts": [{"text": "✅ Proposta ajustada enviada para comercial@agenciaviagens.com.br!\n\n📧 Email inclui:\n- 5 experiências (Paris, Roma, Barcelona)\n- Preço: R$ 28.800 (R$ 1.440/pessoa)\n- Detalhes de cada passeio\n- Datas disponíveis em junho/2026\n- Condições de pagamento\n- Planilha CSV em anexo\n\nPrecisa de mais alguma informação?"}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{"name": "gerar_csv_tool", "args": {"produtos": [...]}},
{
"name": "enviar_email_mailersend_tool",
"args": {
"destinatario": "comercial@agenciaviagens.com.br",
"assunto": "Proposta Grupo Europa - 20 pessoas (R$ 28.800)",
"corpo": "...",
"anexos": ["orcamento_europa.csv"]
}
}
],
"intermediate_responses": []
}
}
],
"session_input": {
"app_name": "ifriend_agent",
"user_id": "comercial_real_001",
"state": {}
}
}
]
}
3.2 Casos de Teste Integration¶
Criar evalsets para:
- ✅
booking_completo.evalset.json - Fluxo completo discovery → payment
- Múltiplos produtos
-
Variações de produtos
-
✅
multi_turn_conversation.evalset.json - Conversas longas (10+ turnos)
- Mudança de contexto
-
Retomada de conversa anterior (memórias)
-
✅
payment_flow.evalset.json - Diferentes formas de pagamento
- Parcelamento
-
Falhas de pagamento
-
✅
edge_cases.evalset.json - Produtos sem disponibilidade
- Dados inválidos
-
Erros de API
-
✅
comercial_orcamentos.evalset.json⭐ CENÁRIO REAL FREQUENTE - Orçamentos simples (1-2 produtos)
- Orçamentos complexos (5+ produtos, múltiplos destinos)
- Negociação de preços e alternativas
- Follow-up de propostas enviadas
- Múltiplos emails para mesma proposta
3.3 Pytest Integration Tests¶
Arquivo: ifriend_agent/tests/evaluation/test_integration_evaluations.py
import pytest
from google.adk.evaluation.agent_evaluator import AgentEvaluator
@pytest.mark.asyncio
@pytest.mark.slow
async def test_booking_completo():
"""Testa fluxo completo de booking end-to-end."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/integration/booking_completo.evalset.json",
)
@pytest.mark.asyncio
@pytest.mark.slow
async def test_multi_turn_conversation():
"""Testa conversas longas e complexas."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/integration/multi_turn_conversation.evalset.json",
)
@pytest.mark.asyncio
@pytest.mark.slow
async def test_payment_flow():
"""Testa fluxo de pagamento completo."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/integration/payment_flow.evalset.json",
)
@pytest.mark.asyncio
@pytest.mark.slow
async def test_comercial_orcamentos():
"""Testa cenários comerciais reais de orçamento e negociação."""
await AgentEvaluator.evaluate(
agent_module="ifriend_agent",
eval_dataset_file_path_or_dir="ifriend_agent/evaluations/integration/comercial_orcamentos.evalset.json",
)
Fase 4: Métricas Avançadas e Rubrics 📊¶
Duração: 1 semana
Prioridade: MÉDIA
4.1 Rubric-Based Evaluation¶
Arquivo: ifriend_agent/evaluations/integration/test_config_advanced.json
{
"criteria": {
"tool_trajectory_avg_score": 0.95,
"response_match_score": 0.75,
"final_response_match_v2": 0.9,
"rubric_based_final_response_quality_v1": {
"rubrics": [
"A resposta é concisa e direta (máximo 3 parágrafos)",
"Usa listas numeradas quando apresenta múltiplas opções",
"Faz no máximo UMA pergunta por resposta",
"A pergunta está no final da resposta",
"Não usa jargão técnico desnecessário",
"Mantém tom amigável e profissional"
],
"threshold": 0.85
},
"rubric_based_tool_use_quality_v1": {
"rubrics": [
"Executa busca_produtos_tool ANTES de pedir detalhes",
"Verifica disponibilidade ANTES de calcular preço",
"Busca/cria usuário ANTES de emitir reserva",
"Valida dados ANTES de processar pagamento",
"Não executa tools desnecessários",
"Segue a ordem lógica da state machine"
],
"threshold": 0.9
},
"rubric_based_comercial_quality_v1": {
"rubrics": [
"Calcula preço para TODOS os produtos mencionados",
"Gera CSV quando há múltiplos produtos (3+)",
"Envia email SOMENTE quando usuário fornece destinatário",
"Email inclui informações completas (preços, detalhes, disponibilidade)",
"Apresenta total consolidado antes de enviar proposta",
"Sugere alternativas quando produto está indisponível ou fora do orçamento"
],
"threshold": 0.9
},
"hallucinations_v1": 0.95,
"safety_v1": 1.0
}
}
4.2 Custom Validators¶
Arquivo: ifriend_agent/tests/evaluation/custom_validators.py
from typing import List, Dict, Any
def validate_state_machine(tool_trajectory: List[Dict[str, Any]]) -> bool:
"""
Valida se o agente seguiu a state machine corretamente.
DISCOVERY → SELECTION → QUOTE → IDENTIFICATION → BOOKING → PAYMENT
"""
state_transitions = {
"DISCOVERY": ["busca_produtos_tool"],
"SELECTION": ["detalhes_experience", "detalhes_guia", "tem_variacao_tool", "listar_variacoes_tool"],
"QUOTE": ["disponibilidade_calendario_tool", "disponibilidade_horarios_tool", "calcular_preco_tool"],
"IDENTIFICATION": ["buscar_usuario_tool", "buscar_agencia_tool", "criar_conta_viajante_tool", "criar_conta_agencia_tool"],
"BOOKING": ["validar_dados_reserva_tool", "emitir_reserva_experience_tool", "emitir_reserva_guia_tool"],
"PAYMENT": ["verificar_parcelas_tool", "gerar_token_cartao_tool", "processar_pagamento_tool"]
}
current_state = "DISCOVERY"
state_order = ["DISCOVERY", "SELECTION", "QUOTE", "IDENTIFICATION", "BOOKING", "PAYMENT"]
for tool_call in tool_trajectory:
tool_name = tool_call["name"]
# Encontra em qual estado estamos
for state, allowed_tools in state_transitions.items():
if tool_name in allowed_tools:
# Verifica se não pulamos estados
current_idx = state_order.index(current_state)
new_idx = state_order.index(state)
if new_idx < current_idx:
# Retrocedeu estado (pode ser válido em alguns casos)
pass
elif new_idx > current_idx + 1:
# Pulou estado! INVÁLIDO
return False
current_state = state
break
return True
def validate_slack_ux(response_text: str) -> Dict[str, bool]:
"""
Valida se a resposta segue as diretrizes de UX Slack.
"""
lines = response_text.split('\n')
return {
"is_concise": len(lines) <= 10, # Máximo 10 linhas
"has_numbered_lists": any(line.strip().startswith(tuple('123456789')) for line in lines),
"single_question": response_text.count('?') <= 1,
"question_at_end": response_text.strip().endswith('?') if '?' in response_text else True,
"no_long_blocks": all(len(line) <= 100 for line in lines)
}
Fase 5: CI/CD Integration 🔄¶
Duração: 1 semana
Prioridade: ALTA
5.1 GitHub Actions Workflow¶
Arquivo: .github/workflows/agent-evaluation.yml
name: Agent Evaluation Tests
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
schedule:
# Roda testes completos toda segunda às 8h
- cron: '0 8 * * 1'
jobs:
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-asyncio
- name: Run Unit Evaluation Tests
env:
GOOGLE_CLOUD_PROJECT: ${{ secrets.GCP_PROJECT_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GCP_SA_KEY }}
run: |
pytest ifriend_agent/tests/evaluation/test_unit_evaluations.py -v --tb=short
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: unit-test-results
path: pytest-results/
integration-tests:
runs-on: ubuntu-latest
timeout-minutes: 60
needs: unit-tests
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-asyncio
- name: Run Integration Evaluation Tests
env:
GOOGLE_CLOUD_PROJECT: ${{ secrets.GCP_PROJECT_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GCP_SA_KEY }}
run: |
pytest ifriend_agent/tests/evaluation/test_integration_evaluations.py -v --tb=short -m slow
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-test-results
path: pytest-results/
5.2 Pytest Configuration¶
Arquivo: pytest.ini
[pytest]
markers =
slow: marks tests as slow (integration tests)
unit: marks tests as unit tests
evaluation: marks tests as evaluation tests
asyncio_mode = auto
testpaths =
ifriend_agent/tests
tests
python_files =
test_*.py
python_classes =
Test*
python_functions =
test_*
addopts =
-v
--strict-markers
--tb=short
--junitxml=pytest-results/junit.xml
Fase 6: Web UI e Monitoramento 🖥️¶
Duração: 1 semana
Prioridade: BAIXA
6.1 Uso do adk web¶
Comandos:
# Iniciar servidor web
adk web ifriend_agent
# Acessar: http://localhost:8080
Workflow:
- Criar sessão interativa
- Conversar com o agente
-
Testar diferentes cenários
-
Salvar como Eval Case
- Ir para aba "Eval"
- Criar/selecionar eval set
-
Adicionar sessão atual
-
Editar Test Case
- Modificar respostas esperadas
- Ajustar trajetória de tools
-
Deletar mensagens desnecessárias
-
Executar Avaliação
- Selecionar test cases
- Configurar métricas (sliders)
-
Iniciar avaliação
-
Analisar Resultados
- Ver Pass/Fail
- Comparar Actual vs Expected
- Usar Trace View para debug
6.2 CLI Evaluation¶
Comandos:
# Executar avaliação específica
adk eval \
ifriend_agent \
ifriend_agent/evaluations/integration/booking_completo.evalset.json \
--config_file_path=ifriend_agent/evaluations/integration/test_config.json \
--print_detailed_results
# Executar apenas evals específicos
adk eval \
ifriend_agent \
ifriend_agent/evaluations/integration/booking_completo.evalset.json:eval_1,eval_3 \
--print_detailed_results
6.3 Dashboard de Métricas¶
Arquivo: ifriend_agent/evaluations/dashboard.py
import json
from pathlib import Path
from datetime import datetime
from typing import List, Dict
class EvaluationDashboard:
"""Dashboard para visualizar resultados de avaliações."""
def __init__(self, results_dir: str = "evaluation_results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def save_result(self, eval_name: str, results: Dict):
"""Salva resultado de avaliação."""
timestamp = datetime.now().isoformat()
filename = f"{eval_name}_{timestamp}.json"
filepath = self.results_dir / filename
with open(filepath, 'w') as f:
json.dump(results, f, indent=2)
def get_history(self, eval_name: str) -> List[Dict]:
"""Retorna histórico de execuções."""
pattern = f"{eval_name}_*.json"
files = sorted(self.results_dir.glob(pattern))
history = []
for file in files:
with open(file) as f:
history.append(json.load(f))
return history
def generate_report(self) -> str:
"""Gera relatório consolidado."""
all_files = list(self.results_dir.glob("*.json"))
total_evals = len(all_files)
passed = sum(1 for f in all_files if self._check_passed(f))
failed = total_evals - passed
report = f"""
# Evaluation Report
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
## Summary
- Total Evaluations: {total_evals}
- ✅ Passed: {passed} ({passed/total_evals*100:.1f}%)
- ❌ Failed: {failed} ({failed/total_evals*100:.1f}%)
## Details
"""
for file in sorted(all_files, reverse=True)[:10]:
with open(file) as f:
data = json.load(f)
status = "✅ PASS" if data.get("passed", False) else "❌ FAIL"
report += f"\n- {file.stem}: {status}"
return report
def _check_passed(self, filepath: Path) -> bool:
"""Verifica se avaliação passou."""
with open(filepath) as f:
data = json.load(f)
return data.get("passed", False)
📅 Roadmap de Implementação¶
Sprint 1 (Semana 1-2)¶
- ✅ Setup de diretórios e configurações
- ✅ Criar primeiros 5 test files (unit)
- ✅ Implementar pytest infrastructure
- ✅ Primeiro CI/CD workflow
Entregáveis: - Estrutura de pastas completa - 5 test files funcionais - Pytest rodando localmente - GitHub Actions básico
Sprint 2 (Semana 3-4)¶
- ✅ Completar todos unit tests (15-20 arquivos)
- ✅ Criar primeiro evalset (booking_completo)
- ✅ Implementar custom validators
- ✅ Melhorar CI/CD
Entregáveis: - 15-20 test files cobrindo todas tools - 1 evalset complexo - Validadores customizados - CI/CD rodando em PRs
Sprint 3 (Semana 5-6)¶
- ✅ Criar mais 3-4 evalsets
- ✅ Implementar rubric-based metrics
- ✅ Testes com adk web UI
- ✅ Dashboard de métricas
Entregáveis: - 4-5 evalsets integration - Métricas avançadas configuradas - Dashboard funcional - Documentação completa
Sprint 4 (Semana 7-8)¶
- ✅ User Simulation scenarios
- ✅ Fine-tuning de thresholds
- ✅ Otimização de performance
- ✅ Treinamento da equipe
Entregáveis: - Simulação de usuários - Thresholds calibrados - Testes rodando < 30min - Equipe treinada
📊 Métricas e KPIs¶
Cobertura de Testes¶
Meta: 90% das tools cobertas
| Tool Category | Tools | Test Coverage | Target |
|---|---|---|---|
| Search | 2 | 0% → 100% | 100% |
| Details | 2 | 0% → 100% | 100% |
| Availability | 2 | 0% → 100% | 100% |
| Pricing | 3 | 0% → 100% | 100% |
| Booking | 8 | 0% → 100% | 100% |
| Payment | 3 | 0% → 100% | 100% |
| Utils | 7 | 0% → 80% | 80% |
| Comercial (Orçamento+Email) | 3 | 0% → 100% | 100% |
Performance Metrics¶
Meta: Testes rápidos e confiáveis
| Metric | Current | Target |
|---|---|---|
| Unit Test Runtime | N/A | < 5min |
| Integration Test Runtime | N/A | < 30min |
| CI/CD Total Time | N/A | < 45min |
| Test Success Rate | N/A | > 95% |
Quality Metrics¶
Meta: Alta qualidade de respostas
| Metric | Threshold | Description |
|---|---|---|
| Tool Trajectory Score | ≥ 0.95 | Precisão na sequência de tools |
| Response Match Score | ≥ 0.8 | Similaridade com resposta esperada |
| Final Response Quality | ≥ 0.85 | Qualidade geral da resposta |
| Hallucination Score | ≥ 0.95 | Respostas baseadas em dados reais |
| Safety Score | 1.0 | Conteúdo 100% seguro |
🎓 Recomendações e Boas Práticas¶
1. Começar Simples¶
- ✅ Comece com test files (unit tests)
- ✅ Use métricas default primeiro
- ✅ Foque em happy paths
- ❌ Não tente criar todos os testes de uma vez
2. Iterar Rapidamente¶
- ✅ Adicione 2-3 test files por dia
- ✅ Rode testes localmente com
pytest - ✅ Use
adk webpara criar casos interativamente - ✅ Ajuste thresholds baseado em resultados
3. Automatizar Cedo¶
- ✅ Integre ao CI/CD desde Sprint 1
- ✅ Rode unit tests em todos PRs
- ✅ Rode integration tests em merges
- ✅ Agende testes completos semanalmente
4. Documentar Tudo¶
- ✅ Documente cada test case (why, what, expected)
- ✅ Mantenha README atualizado
- ✅ Compartilhe resultados com time
- ✅ Crie runbook de troubleshooting
5. Monitorar Produção¶
- ✅ Use mesmas métricas em prod
- ✅ Crie alertas para degradação
- ✅ Analise falhas reais
- ✅ Adicione casos de falha como testes
🔧 Ferramentas e Recursos¶
Google ADK¶
Pytest¶
CI/CD¶
Vertex AI¶
📝 Próximos Passos Imediatos¶
Ação 1: Setup Inicial (Hoje)¶
# Criar estrutura de diretórios
mkdir -p ifriend_agent/evaluations/{unit,integration,scenarios}
mkdir -p ifriend_agent/tests/evaluation
mkdir -p evaluation_results
# Criar arquivos de configuração
touch ifriend_agent/evaluations/unit/test_config.json
touch ifriend_agent/evaluations/integration/test_config.json
touch ifriend_agent/tests/evaluation/conftest.py
touch pytest.ini
Ação 2: Primeiro Test File (Amanhã)¶
# Criar primeiro test case
touch ifriend_agent/evaluations/unit/busca_produtos.test.json
# Criar teste pytest
touch ifriend_agent/tests/evaluation/test_unit_evaluations.py
# Rodar localmente
pytest ifriend_agent/tests/evaluation/test_unit_evaluations.py -v
Ação 3: CI/CD (Próxima Semana)¶
# Criar workflow
mkdir -p .github/workflows
touch .github/workflows/agent-evaluation.yml
# Testar localmente
act pull_request # Requer 'act' instalado
✅ Checklist de Conclusão¶
Fase 1: Setup ✅¶
- [ ] Diretórios criados
- [ ] Configurações básicas
- [ ] Pytest configurado
- [ ] README atualizado
Fase 2: Unit Tests ✅¶
- [ ] 15-20 test files criados
- [ ] Todos tools cobertos
- [ ] Pytest tests passando
- [ ] CI/CD básico funcionando
Fase 3: Integration ✅¶
- [ ] 4-5 evalsets criados
- [ ] Conversas multi-turn testadas
- [ ] Edge cases cobertos
- [ ] Vertex AI configurado
Fase 4: Advanced ✅¶
- [ ] Rubric-based metrics
- [ ] Custom validators
- [ ] Dashboard implementado
- [ ] Métricas calibradas
Fase 5: Production ✅¶
- [ ] CI/CD completo
- [ ] Testes em PRs
- [ ] Monitoring configurado
- [ ] Equipe treinada
📚 Referências¶
- Google ADK Evaluation Docs: https://google.github.io/adk-docs/evaluate/
- Evaluation Criteria: https://google.github.io/adk-docs/evaluate/criteria/
- User Simulation: https://google.github.io/adk-docs/evaluate/user-sim/
- ADK Python GitHub: https://github.com/google/adk-python
- Vertex AI Evaluation: https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/evaluation
Última Atualização: 04/01/2026
Responsável: Time iFriend Platform
Revisão: Trimestral