Um agente atendente de loja de peças automotivas, construído com pydantic-ai, e um conjunto de evals com deepeval (LLM como juiz). O agente conversa com o cliente em português e usa ferramentas para consultar um catálogo simulado (busca, estoque, preço, compatibilidade) e montar um pedido durante a sessão.
agent_eval/
├── src/ # código fonte
│ ├── agents/
│ │ ├── attendant.py # agente, SYSTEM_PROMPT e tools
│ │ ├── models.py # Product, Catalog, Order, AttendantContext
│ │ └── data/catalog.json # catálogo de produtos (editável)
│ ├── core/settings.py # carregamento das variáveis de ambiente
│ └── main.py # loop de chat interativo no terminal
├── evals/ # ferramentas de desenvolvimento (fora do pacote)
│ ├── main.py # runner script: imprime scores
│ ├── test_attendant.py # mesma cobertura via pytest + assert_test
│ ├── metrics.py # SINGLE_TURN_METRICS / MULTI_TURN_METRICS
│ └── goldens.json # cenários e turnos esperados
├── .github/workflows/evals.yml # CI manual (workflow_dispatch) que roda os tests
└── pyproject.toml # define o pacote, dev group e config do pytest
uv sync # carrega as dependências
cp .env.example .env # preencha as chavesVariáveis esperadas no .env:
AGENT_LLM_MODEL="<openai-model-name>"
OPENAI_API_KEY="<openai-api-key>"
JUDGE_MODEL="<openai-model-name>"
EVAL_THRESHOLD="<threshold-value>"EVAL_THRESHOLD é o score mínimo para que cada métrica passe no pytest. Deve ser um valor entre 0 e 1 (ex.: 0.7); valores fora desse intervalo não fazem sentido para os scores normalizados do deepeval.
Conversar com o agente:
uv run src/main.pyDigite sair, quit ou exit (ou Ctrl+C) para encerrar.
Rodar os evals como script (imprime os scores):
uv run evals/main.pyRodar os evals como pytest (pass/fail pelo threshold):
uv run pytest evals/ -v| Tool | O que faz |
|---|---|
search_product |
Busca produtos no catálogo por nome ou parte do nome |
check_stock |
Verifica a quantidade em estoque para um SKU |
check_price |
Retorna o preço unitário de um SKU |
check_compatibility |
Confere se um SKU é compatível com um modelo de veículo |
add_to_order |
Adiciona um item ao pedido da sessão |
order_summary |
Lista os itens do pedido atual com subtotais e total |
Os produtos ficam em src/agents/data/catalog.json e são carregados via Catalog.load(...). Cada produto tem sku, name, price, stock e compatible_models. Para adicionar/editar itens, basta mexer no JSON, não precisa tocar no código.
O agente recebe um AttendantContext (catálogo + pedido) via RunContext do pydantic-ai. O pedido vive apenas em memória durante a sessão.
Os cenários ficam em evals/goldens.json, cada um é um conjunto de turnos com input, expected_output e expected_tools. Eles são carregados como Golden objects do deepeval (evals/main.py) e rodados em dois fluxos:
- Single-turn (end-to-end por turno):
ToolCorrectnessMetric,ArgumentCorrectnessMetrice umGEvalcom rubric chamadoResponseCorrectness. - Multi-turn (cenário inteiro como conversa):
ConversationCompletenessMetriceTurnRelevancyMetric, usandoConversationalTestCase.
As métricas e thresholds vivem em evals/metrics.py. O mesmo conjunto de cenários alimenta o runner script e os testes pytest.
.github/workflows/evals.yml roda pytest evals/ -v em runner Ubuntu, com trigger manual (workflow_dispatch) via aba Actions. Espera:
- Secrets:
OPENAI_API_KEY - Vars:
AGENT_LLM_MODEL,JUDGE_MODEL,EVAL_THRESHOLD