omarcevi.dev / workshops / deploy-agents / 6 · Evaluate

Step 6 · Evaluate

7 min

Use an eval set as the gate for each deployment. Score the live traffic. Make a new test case from a production conversation.

checkpoint branch step-6-evaluate

How it works

The evaluation loop. 1 A script makes datasets from the eval set in evals/cases.yaml. 2 agents-cli eval run and the gate script decide if a change can go to deployment. 3 Deploy. 4 score_live.sh scores each turn of the production traffic with your own LLM judges. 5 You find low scores or bad sessions in Trace and in the console. 6 session_to_eval_case.py makes a new eval case from one of them. 7 The case goes into the eval set. The managed Online Monitor of Google also gives low scores when it can score a turn. It does not score multi-agent turns.
Offline evals stop regressions before deployment. Scores of live traffic show the problems that the eval set does not cover. Each failure becomes a case.

The eval set is in evals/cases.yaml. It has twenty prompts, each with the expected result. agents-cli eval run runs the agent locally on each prompt, with new copies of the MCP servers. Then it grades the traces:

  • Correct tool, correct arguments (tool_trajectory): Did the agent call submit_leave_request with the date of next Friday? Did it open a P1 ticket for a stolen laptop? Did it not call a forbidden tool?
  • Correct specialist agent (routed_to_expected_agent): Did the concierge send the request to the correct specialist agent?
  • No salary in answers (no_compensation_leak): No answer contains the salary of a person. When an employee asks for a salary, the agent refers the employee to the People Partner.
  • Answers from the handbook (grounding_v1): An LLM judge from the Gen AI evaluation service compares each sentence of a policy answer with the handbook.

The first three metrics are small Python functions that run locally. The grounding metric calls the evaluation service. agents-cli exits with 0 for all scores. Thus, evals/gate.py compares the scores with evals/thresholds.yaml, and stops the build if a score is too low. This makes it a gate.

scripts/score_live.sh scores the live traffic. The script reads the recent conversations of the deployed agent from Agent Runtime Sessions. It makes an eval case from each turn (all four agents, each hand-over and each tool result), and agents-cli eval grade grades the cases. It uses the salary-leak check and three LLM judges on Gemini 3.5 Flash, which evals/eval_config.live.yaml defines (grounded, resolved, tool_use). In production, run the script on a schedule. The turns with low scores become eval cases.

The managed Online Monitor of Google does the same every ten minutes, without code. Our tests found two limits. First, the monitor expects one system instruction for each trace. Thus, it does not score a turn where the concierge gives the request to a specialist agent (INSUFFICIENT_DATA). In this lab, that is each turn that calls a tool.

Second, the judge of the monitor (Gemini 2.5 Flash or Pro on the regional endpoint) uses shared pay-as-you-go capacity. The spend of the organization sets the priority on this capacity. In a new project or a project with low spend, most judge calls return 429 Judge model resource exhausted. You create a monitor anyway, to see the product and to read its log.

Run

Offline evals (approximately two minutes):

./scripts/eval.sh

Score the live traffic (approximately one minute). The script grades your last 20 turns from steps 2–5:

./scripts/score_live.sh

The Online Monitor of Google. Use the script to create the monitor. The script also gives the Vertex AI service agent permission to read traces:

./scripts/monitor.sh

Or use the console:

  1. Go to Agent Platform → Optimize → Evaluation → Online monitors → New monitor.
  2. Select the agent workplace-assistant.
  3. Type the name workplace-quality.
  4. Select the region europe-west1, the unit Traces and All traces. Click Continue.
  5. Keep the four default metrics. Click Continue.
  6. Set the sampling percentage to 100 and the maximum samples for each evaluation job to 20. Click Set up.

Then give the service agent access. If you do not do this, the monitor shows Failed:

gcloud projects add-iam-policy-binding $PROJECT_ID --condition=None \
  --member=serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform.iam.gserviceaccount.com \
  --role=roles/observability.viewAccessor

Complete the loop. Select a production conversation, if possible one with a wrong answer. Add it to the eval set. If you do not have one, use this conversation. The agent must not book leave, because 29 October is Republic Day for the Istanbul office:

uv run python scripts/ask.py "Book 29 October off for me."      # note the session id
uv run python scripts/session_to_eval_case.py SESSION_ID --id my_regression
# edit the TODO lines of the new case at the end of the behaviour list in evals/cases.yaml:
#   expected_agent: leave_agent     forbidden_tools: [submit_leave_request]
uv run python evals/build_datasets.py && ./scripts/eval.sh behaviour

Expected result

==> agents-cli eval run (behaviour) PASS routed_to_expected_agent: 1.0 (threshold 0.9, 15 cases, 0 errors) PASS tool_trajectory: 1.0 (threshold 0.85, 15 cases, 0 errors) PASS no_compensation_leak: 1.0 (threshold 1.0, 15 cases, 0 errors) Eval gate (behaviour): PASSED ==> agents-cli eval run (handbook) PASS grounding_v1: 1.0 (threshold 0.8, 5 cases, 0 errors) ... Eval gate (handbook): PASSED ==> Reading recent conversations from Agent Runtime Sessions 20 turns from 18 conversations -> artifacts/live/traces.json ==> Grading them (judge: Gemini 3.5 Flash, about a minute) Scored 20 recent turns of the deployed agent: grounded mean 1.00 (20 scored) no_compensation_leak mean 1.00 (20 scored) resolved mean 0.95 (20 scored) tool_use mean 1.00 (20 scored) Lowest-scoring turn: prompt: Is 29 October a public holiday? resolved = 0.5: … Turn it into a test case: uv run python scripts/session_to_eval_case.py 6670436604164702208 --id my_regression ==> Creating online monitor workplace-quality (sampling 100%, max 20 traces per run) ✔ requested: projects/…/operations/…

Approximately ten minutes later, the results of the monitor show in Deployments → workplace-assistant → Dashboard → Evaluation. Read in its log what the monitor did with your traffic. Expect skipped multi-agent turns and judge calls that failed because of rate limits:

gcloud logging read 'logName:"online_evaluator"' --freshness=1h --limit=20 \
  --format='value(labels."gen_ai.evaluation.name",labels."gen_ai.evaluation.score.value",labels."error.type")'

Check

./scripts/check.sh 6
✔ eval behaviour passes the thresholds ✔ eval handbook passes the thresholds ✔ live traffic scored: grounded 1.00, no_compensation_leak 1.00, resolved 1.00, tool_use 1.00 ✔ online monitor: workplace-quality ==> Step 6 checks passed.

Fallback

If you cannot use the evaluation service or Online Monitors, run the local part of the eval. Then read the live-traffic scores that we recorded in a rehearsal:

./scripts/fallback_step6.sh
If this fails
only 4 of 5 cases ran
A model call timed out, and agents-cli removed that case. Run ./scripts/eval.sh again.
A metric is below its threshold
The gate lists the cases that failed, with the explanation of the judge. Open artifacts/eval/SUITE/results_*.html to see the full traces.
score_live.sh: No conversations in the last 3 h
Ask the agent some questions (steps 2–5). Or make the time window larger: ./scripts/score_live.sh --hours 24.
score_live.sh: a metric shows n/a, or many cases failed
The judge got too many requests. Run the script again. It sends two judge calls each second.
The log of the monitor shows 429 Judge model resource exhausted
The shared capacity of its judge is busy, and projects with low spend get the lowest priority. You cannot fix this. score_live.sh uses a judge with more capacity.
The monitor shows Failed
Usually, the service agent does not have the permission above. Give the permission. Then delete the monitor and create it again.
The monitor shows no scores
The first run starts up to ten minutes after you create the monitor. It scores only the traces that exist at that time. Ask the agent some questions.
The log shows INSUFFICIENT_DATA: system_instruction is not uniform across call_llm spans
This is normal for a turn that a specialist agent handles (see the Online Monitor paragraphs above). To see scores, ask a question that the concierge answers, for example a salary question.
The log shows INSUFFICIENT_DATA: system_instruction not present in any call_llm span
You deployed the agent without ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS=true (agents-cli disables it by default). Run ./scripts/setup.sh --only env. Then deploy the agent again. Only new traces get scores. To read the log of the monitor, run gcloud logging read 'logName:"online_evaluator"' --freshness=1h --limit=5.
The API returns INVALID_ARGUMENT when you create a monitor
The request must not set semconvVersion. monitor.sh sends the same request body as the console.