Scoring ADK Agents Locally with an Evalset — and Choosing a Deployment Target from Official Docs

In Google Cloud’s Professional Agentic Architect, §4 of the exam guide, “Evaluating and deploying agentic workflows”, accounts for about 22% of the exam (English exam guide, October 2026). The Agent Development Kit (ADK), which Google publishes under Apache 2.0, is named as an example evaluation tool. So we ran ADK’s evaluation on our own PC with a local model to see what it catches and what it misses. We have not taken this exam.
This article contains affiliate links. As an Amazon Associate, we earn from qualifying purchases.
The subject is the agent from Run ADK Agents Locally with Ollama, which has three tools (filament stock, printer status and reservation). We wrote a five-question evaluation set for it — a JSON file with expected tool calls and reference answers, called an evalset in ADK — and evaluated it with the CLI of ADK Python v2.11.0. We also built three deliberately broken agents: one that reserves without checking, one that always makes an extra stock call, and one with the status tool removed. They serve as negative controls to see whether the evaluation catches them.
The results first. In this run only EXACT, the default matching method, caught the extra calls. IN_ORDER and ANY_ORDER, which allow extra calls, gave a perfect 1.0 to the greeting — where calling no tool is correct — even with an extra call. The agent without the status tool passed EXACT on two of the three questions that use it, because the call names and arguments matched expectations: the matching does not check whether a call succeeded. And when we fed fixed sentence pairs into the response match, a sentence with the opposite meaning scored 0.772 and a paraphrase scored 0.464, so the opposite sentence scored higher.
The second half sorts out §4.2 — choosing a deployment target (Agent Runtime, Cloud Run, GKE) and isolating failures — from official materials. We did not deploy anything, and local practice is not a substitute for the exam’s labs. For the exam overview and plan, see What the Google Cloud Professional Agentic Architect Certifies.
- What §4 asks for — four evaluation items and three deployment items
- How ADK evaluation works — evalsets, a config file and four ways to run
- Metrics fall into three groups — local only, judge LLM, and cloud evaluation service
- Matching tool calls — EXACT, IN_ORDER and ANY_ORDER treat extra calls differently
- Test conditions — five questions for a three-tool agent, and three ways to break it
- Results — whether broken agents were caught depended on the matching mode
- A question that passed with the tool removed — matching does not check whether a call succeeded
- Response matching counts overlapping characters, not correctness of meaning
- Efficiency metrics and duration — read a jump in inference calls as a sign of looping
- Production evaluation lives on Agent Platform — continuous evaluation and drift
- Choosing a deployment target — Agent Runtime, Cloud Run and GKE from official materials
- Isolating failures and monitoring — what you can see locally and what you see in the cloud
- What this run cannot tell us
- Summary — ADK evaluation starts with choosing the matching mode
- Sources
What §4 asks for — four evaluation items and three deployment items
§4 has two parts. 4.1, “Evaluating agents in development and in production.”, covers evaluation, and 4.2, “Deploying and scaling production workloads.”, covers deployment and operations. 4.1 has four items:
- “Creating test sets for agent evaluation”: examples include golden data, prompts and edge cases
- “Creating continuous evaluation pipelines”: evaluate agent tool execution continuously against defined success criteria
- “Determining the appropriate evaluation framework and tooling”: examples are ADK evaluation tools (evalset), the Agent Platform Gen AI evaluation service and custom autoraters
- “Evaluating an agentic system against a golden dataset”: evaluate response and retrieval quality against golden data; example: ADK
Agent Platform here is Google Cloud’s Gemini Enterprise Agent Platform. 4.2 has three items: choosing a deployment target (Agent Runtime, Cloud Run, GKE) by use case, requirements and cost; isolating agent failures (drift, tool invocation latency, reasoning loops, system failures); and monitoring and optimizing for performance, reliability and cost.
Of these seven, this article practiced parts of 4.1 items 1, 3 and 4 with local ADK: writing a test set, running the evaluation and reading the results, all without a Google Cloud account. Item 2, continuous evaluation, can be built by running local evaluations repeatedly in CI (a system that runs tests automatically on every change); the ADK docs say evaluations written in pytest can go into CI/CD pipelines, but we did not build that. Agent Platform’s online monitors, which score production traces, and most of 4.2 assume a deployed agent and Google Cloud observability. So we describe what ran locally first and put what needs the cloud in the second half.
The evaluation set and edge cases here are our own, not exam questions. The renames of in-scope products (Agent Runtime was formerly Agent Engine, and so on) are checked against official docs in Agentic Architect Exam Scope: What AI Knows About 28 Tools.
How ADK evaluation works — evalsets, a config file and four ways to run
ADK evaluation compares a file of expected behavior with what the agent actually does. The ADK docs note that because models are probabilistic, tests that assert a fixed pass or fail often do not suit agents, so each metric produces a value that is compared with a threshold. We pinned ADK Python v2.11.0 (released October 2, 2026); adk.dev is updated for the latest version, so the defaults and behavior below are what we confirmed in that version’s code.
There are two kinds of expected-behavior files. A test file (extension .test.json) holds one session and suits unit tests. An evalset can hold several long sessions and suits integration tests that simulate multi-turn conversations. The format is the same: expected tool calls go in intermediate_data.tool_uses and the reference answer in final_response.
Criteria go in a config file (test_config.json). In v2.11.0’s code, if you pass a single evalset file to the CLI without a config file, it reads test_config.json from the same folder; if none is found, tool_trajectory_avg_score defaults to 1.0 and response_match_score to 0.8. Those are fallbacks, not recommendations; we set the values explicitly in a config file.
| How to run | Usage | Points to note |
|---|---|---|
| adk web (dev UI) | Add a conversation to eval cases with “Add current session” and run from the UI | Threshold sliders for Tool trajectory avg score and Response match score; the Trace tab follows the run |
| pytest | Call AgentEvaluator.evaluate from an async test | In v2.11.0’s code the same inference runs twice by default, and values are averaged over all runs |
| CLI (adk eval) | Pass an evalset file | No option for the number of runs; one inference per evalset |
| conformance | Regression test against previously recorded behavior | Defaults to replaying the recording; not used here |
Evaluation is an optional extra. Without pip install google-adk[eval], the CLI printed “Eval module is not installed” and stopped. The extra also installs Google Cloud SDKs such as google-cloud-aiplatform[evaluation], even if you evaluate only locally. ADK installation and local model setup are in the previous article.
Metrics fall into three groups — local only, judge LLM, and cloud evaluation service
You cannot say ADK evaluation “all runs locally” or “needs Google Cloud” as a whole. In v2.11.0’s code the metrics split into three groups by what they call. Below, the model that scores answers is called the judge (ADK’s setting is judge_model).
| Group | Metric | What it checks |
|---|---|---|
| A: computed locally | tool_trajectory_avg_score | Compares the sequence of tool calls (names and arguments) with expectations |
| A | response_match_score | ROUGE-1 F-score against the reference answer |
| A | Four efficiency metrics (tool_call_count_v1, inference_call_count_v1, token_usage_v1, invocation_duration_v1) | Report tool calls, inference calls, tokens and duration; not used for pass/fail |
| B: calls a judge LLM | final_response_match_v2 | A judge decides whether the answer matches the reference in meaning |
| B | rubric_based_final_response_quality_v1, rubric_based_tool_use_quality_v1, rubric_based_multi_turn_trajectory_quality_v1 | A judge scores against rubrics you write |
| B | hallucinations_v1 | Splits the answer into sentences and has a judge check whether each is supported by context |
| B | per_turn_user_simulator_quality_v1 | A judge checks the quality of the user simulator (only when simulation is used) |
| C: calls the cloud evaluation service | response_evaluation_score, safety_v1, multi_turn_task_success_v1, multi_turn_trajectory_quality_v1, multi_turn_tool_use_quality_v1 | Coherence, safety, conversation-goal success and more, scored via the Agent Platform evaluation SDK |
Group B judges call gemini-2.5-flash unless specified — the default in v2.11.0’s code; the gemini-flash-latest in doc examples is not the default. So by default you need a Google API key or Agent Platform authentication. Per the docs, final_response_match_v2 and the rubric metrics call the judge several times on the same input and take a majority vote; num_samples, the number of judge calls, defaults to 5. final_response_match_v2 uses this to set 1.0 or 0.0 per question. Rubric metrics set 1.0 or 0.0 per rubric item and combine them per question (averaged for rubric_based_final_response_quality_v1 and rubric_based_tool_use_quality_v1). hallucinations_v1 is the share of answer sentences that are supported or deemed not applicable.
Using a local model as the judge is not documented. v2.11.0’s code looks the judge up by name string, so a name starting with ollama_chat/ is accepted in form, but we did not try it. Also, when the judge returns no value, the metric becomes NOT_EVALUATED; in v2.11.0 the CLI leaves it out of pass/fail while pytest (AgentEvaluator) reports it as a failure, so it is easy to miss on the CLI.
Group C needs a Google API key or Google Cloud project authentication. The ADK docs are inconsistent here — the metrics page says a Google Cloud project is required, while the evaluation overview says an API key or project authentication — and v2.11.0’s code accepts either. Our measurements used group A only, not B or C.
We found no product or page named “custom autoraters”, as in the exam guide (within what we checked). Its substance appears to be configuring and tuning your own judge model and writing your own rubrics: in ADK, judge_model and rubrics; in Agent Platform, judge-model configuration (system instructions for the judge, swapping response order to reduce bias, scoring several times, using a tuned model) and static rubrics you write. Agent Platform also documents checking a judge against human ratings (Evaluate a judge model). Its repeated scoring setting, sampling_count, ranges from 1 to 32 with a default of 4 — a different setting from ADK’s num_samples (default 5), so do not confuse them.
Choosing what each metric should check, and what counts as failure, is design work that comes before any evaluation run. A broad book on building applications with foundation models treats evaluation as a first-class part of that work.
Matching tool calls — EXACT, IN_ORDER and ANY_ORDER treat extra calls differently
tool_trajectory_avg_score has three matching modes. The default, EXACT, requires expected and actual calls to match exactly: any difference in name, arguments, order or count fails, and arguments are compared by exact value. IN_ORDER only requires the expected calls to appear in the same order, with other calls allowed in between. ANY_ORDER ignores order and also allows extra calls.
Scoring is simple: each turn scores 1.0 if it matches and 0.0 if not, and a case’s value is the average over turns, so missing one of two turns gives 0.5, below a 1.0 threshold. An ignore_args setting compares names only; it exists in v2.11.0’s code but is not described on the metrics page.
What is easy to miss is the case of an empty expectation. In v2.11.0’s code, when no calls are expected, IN_ORDER and ANY_ORDER pass whatever is called. So edge cases where “calling no tool is correct” miss extra calls unless evaluated with EXACT. Because matching depends only on comparison rules, not on model output, the expected results can be fixed before running, as below.
| Actual calls | EXACT | IN_ORDER | ANY_ORDER | EXACT + ignore_args |
|---|---|---|---|---|
| T1(a) → T2(b) (as expected) | 1.0 | 1.0 | 1.0 | 1.0 |
| T1(a′) → T2(b) (different argument value) | 0.0 | 0.0 | 0.0 | 1.0 |
| T1(a) → T9(c) → T2(b) (extra call) | 0.0 | 1.0 | 1.0 | 0.0 |
| T2(b) → T1(a) (order swapped) | 0.0 | 0.0 | 1.0 | 0.0 |
| T1(a) → T1(a) → T2(b) (same call twice) | 0.0 | 1.0 | 1.0 | 0.0 |
| T1(a) called when nothing is expected | 0.0 | 1.0 | 1.0 | 0.0 |
Only the last row has an empty expectation; in the others the expectation is “call T1 with argument a, then T2 with argument b”. Only EXACT (with or without ignore_args) catches the extra call and the duplicate call. Conversely, a call that differs only in argument value is missed when ignore_args is on.
Test conditions — five questions for a three-tool agent, and three ways to break it
We evaluated the agent from the previous article. Its tools are get_spool_stock (filament stock), get_printer_status (printer status) and reserve_printer (reservation). The English instruction includes the rule: before reserving, check that printer’s status, then that material’s stock, and reserve only if it is idle and in stock. Stock and printer data are made up.
The environment is also as in that article: Windows 11, RTX 5070 Ti, Ollama 0.40.2 for Windows running gemma4:12B, called from ADK through LiteLLM as LiteLlm(model=”ollama_chat/gemma4:12B”). Installing the evaluation extras (google-adk[eval]) downgraded LiteLLM to 1.85.7, and evaluations ran on that version. gemma4:12B has thinking (emitting a separate reasoning part) on by default in Ollama. The ADK docs warn to use ollama_chat rather than ollama with Ollama, since ollama can cause endless tool calls or ignore prior context. Python was a 3.13 venv, within v2.11.0’s requirement of 3.10 or later.
The five questions in the evalset (asked in Japanese; translated here):
| Question | Expected tool calls | Purpose |
|---|---|---|
| q1 “How many PETG spools are in stock?” | get_spool_stock(material=PETG) | Call only the stock tool |
| q2 “Is p2 free right now?” | get_printer_status(printer_id=p2) | Call only the status tool |
| q3 “I want to print with PLA on p1. Please reserve it if it is free.” | get_printer_status(p1) → get_spool_stock(PLA) → reserve_printer(p1, PLA) | Check, then reserve, in order |
| q4 “Please reserve p1 with TPU.” | get_printer_status(p1) → get_spool_stock(TPU) | No reservation because stock is zero (edge case) |
| q5 “Hello, thanks for your help today.” | None | Calling no tool is correct (edge case) |
An excerpt of one question (q3), built with ADK classes (EvalSet, EvalCase, Invocation, IntermediateData), with empty fields and timestamps removed. The Japanese user message and reference answer are shown in English translation:
{
"invocation_id": "q3_reserve_ok-1",
"user_content": {
"parts": [{"text": "I want to print with PLA on p1. Please reserve it if it is free."}],
"role": "user"
},
"final_response": {
"parts": [{"text": "p1 is free and PLA is in stock, so I reserved p1 with PLA."}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{"args": {"printer_id": "p1"}, "name": "get_printer_status"},
{"args": {"material": "PLA"}, "name": "get_spool_stock"},
{"args": {"printer_id": "p1", "material": "PLA"}, "name": "reserve_printer"}
]
}
}
For EXACT, the config file looked like this; the IN_ORDER and ANY_ORDER versions differ only in match_type.
{
"criteria": {
"tool_trajectory_avg_score": {
"threshold": 1.0,
"match_type": "EXACT"
},
"response_match_score": 0.8
}
}
The four efficiency metrics are not in the config: adding them caused a validation error, and v2.11.0’s code returned a message to the effect of “remove them from criteria; they are reported automatically every time”. Indeed, they appeared every run with status INFORMATIONAL. We ran adk eval workshop_agent with the evalset file, –config_file_path with the config file and –print_detailed_results, from the agent folder’s parent with PYTHONUTF8=1 set.
The broken agents, switched with the environment variable WORKSHOP_VARIANT:
- Skip checks (skip_checks): the instruction was changed to call reserve_printer directly, without checking status and stock
- Always call stock (always_stock): added “for every message, first call get_spool_stock for PLA” to the original instruction
- Remove the status tool (no_status_tool): original instruction, but get_printer_status removed from the tool list
Each broken agent ran once and the normal agent twice. The IN_ORDER, ANY_ORDER and EXACT + ignore_args values were obtained by re-scoring the calls saved by the CLI with ADK’s TrajectoryEvaluator, not by rerunning the agent per mode.
The agent evaluated here has only three tools, but §3 and §4 of the exam guide also cover assembling and operating several agents. A practical book on building agents end to end, including tools and multi-agent setups, is a natural next step.
Results — whether broken agents were caught depended on the matching mode
Values are the number of the five questions that scored 1.0 under each mode. The final verdict is the CLI’s, under the EXACT config; the response match (threshold 0.8) also counts toward it.
| Run | EXACT | IN_ORDER | ANY_ORDER | EXACT + ignore_args | Response ≥ 0.8 | Final PASSED |
|---|---|---|---|---|---|---|
| Normal (run 1) | 5/5 | 5/5 | 5/5 | 5/5 | 0/5 | 0/5 |
| Normal (run 2) | 5/5 | 5/5 | 5/5 | 5/5 | 1/5 | 1/5 |
| Skip checks | 3/5 | 3/5 | 3/5 | 3/5 | 0/5 | 0/5 |
| Always call stock | 0/5 | 4/5 | 5/5 | 0/5 | 0/5 | 0/5 |
| Remove status tool | 4/5 | 4/5 | 4/5 | 4/5 | 0/5 | 0/5 |
The main calls made by the broken agents:
| Broken agent | Question | Actual calls | EXACT | IN_ORDER | ANY_ORDER |
|---|---|---|---|---|---|
| Skip checks | q3 | reserve_printer(p1, PLA) | 0.0 | 0.0 | 0.0 |
| Skip checks | q4 | reserve_printer(p1, TPU) | 0.0 | 0.0 | 0.0 |
| Always call stock | q3 | get_spool_stock(PLA) → get_printer_status(p1) → reserve_printer(p1, PLA) | 0.0 | 0.0 | 1.0 |
| Always call stock | q5 | get_spool_stock(PLA) | 0.0 | 1.0 | 1.0 |
| Remove status tool | q2 | get_printer_status(p2) | 1.0 | 1.0 | 1.0 |
| Remove status tool | q3 | get_printer_status(p1) → get_spool_stock(PLA) | 0.0 | 0.0 | 0.0 |
| Remove status tool | q4 | get_printer_status(p1) → get_spool_stock(TPU) | 1.0 | 1.0 | 1.0 |
Reservations without checks were caught by every mode
The skip-checks agent called only the reservation tool for q3 and q4. Expected calls were missing, so every mode scored 0.0 — nothing slipped through. In q4 it called reserve for TPU, which has zero stock, and the tool refused with “no spool in stock”. Because our tool refuses on its own, there was no real harm; what the evaluation caught was the difference in the call sequence — reserving without checking.
Only EXACT caught the extra calls
The always-call-stock agent failed all five questions under EXACT, while IN_ORDER passed four and ANY_ORDER all five. In the greeting q5, where calling no tool is correct, it called stock once, yet IN_ORDER and ANY_ORDER scored 1.0 — the same as the last row of the matching table above.
In q3 the extra PLA stock call came before the status check, so order-sensitive IN_ORDER failed while order-insensitive ANY_ORDER passed. Here the model reserved after checking status without rechecking stock: the instructed “status → stock → reserve” order was broken, and ANY_ORDER cannot see that either.
Even the normal agent rarely passed the final verdict
The normal agent matched expected tool calls on all five questions in both runs, yet passed the final verdict on 0 of 5 in run one and 1 of 5 in run two. The cause was the response match falling short of 0.8. The CLI first prints a pass/fail summary (Eval Run Summary), then each case’s metric status, value and threshold; if any metric FAILED, the case fails, so a response mismatch alone fails the whole case even when tool calls are right.
Results were saved under .adk/eval_history/ in the agent folder as files ending in .evalset_result.json. Per-case fields include final_eval_status, overall_eval_metric_results and eval_metric_result_per_invocation, and statuses are stored as numbers (1 PASSED, 2 FAILED, 4 INFORMATIONAL), not names.
A question that passed with the tool removed — matching does not check whether a call succeeded
The agent without the status tool still tried to call get_printer_status, as its instruction said. ADK returned “Invoking get_printer_status() failed as no tool with that name is available.” Yet q2 and q4 scored 1.0 under EXACT, because the call names and arguments matched expectations.
In v2.11.0’s code, tool_trajectory_avg_score compares only call names and arguments. It does not look at whether the tool existed, whether the call succeeded or what came back. Only q3 scored 0.0 in every mode, because after the status and stock calls it did not call reserve; its answer correctly said it could not confirm the status. The agent behaved sensibly, but looking at tool-call matching alone, two questions count as “as expected”.
This agent failed the final verdict on all five questions, but the four that passed EXACT failed because the response match was below 0.8 — the same reason the normal agent failed. The evaluation did not detect the missing tool.
Catching this kind of breakage requires looking somewhere other than the call sequence. Whether group B metrics that check meaning (final_response_match_v2 or rubric_based_tool_use_quality_v1) would catch it, we did not test. You can also test the list of tools passed to the agent separately from evaluation. In production, the observability view described later shows per-tool error rates.
Response matching counts overlapping characters, not correctness of meaning
response_match_score is the ROUGE-1 F-score against the reference answer. The ADK docs describe ROUGE-1 in terms of English words, but v2.11.0’s code counts Japanese character by character, so for Japanese answers this metric measures character overlap.
In v2.11.0, response_match_score also joins the text of every part of the answer, including the thinking parts (thought). In this run gemma4:12B sometimes returned English reasoning as thinking parts, and those were compared with the reference too, lowering the score. Here are ADK’s values next to values we recomputed with the same ADK function after removing thinking parts:
| Run | Question | ADK value | Without thinking parts |
|---|---|---|---|
| Always call stock | q1 | 0.248 | 1.0 |
| Skip checks | q1 | 0.306 | 1.0 |
| Normal (run 2) | q2 | 0.434 | 0.842 |
| Normal (run 2) | q1 | 0.929 | 0.929 |
| Normal (run 1) | q3 | 0.5 | 0.5 |
Of 25 answers (five runs × five questions), one reached 0.8 by ADK’s value and six after removing thinking parts: two from the always-call-stock agent, one from the no-status-tool agent, two from normal run two and one from the skip-checks agent. Even without thinking parts, the value drops whenever the wording differs from the reference.
So how well does character overlap track correct meaning? Without any model, we fed fixed sentence pairs into ADK’s scoring function. The sentences were Japanese and scored character by character; English translations are shown.
| Pair | Reference answer | Compared sentence | response_match_score |
|---|---|---|---|
| Opposite meaning | p1 is free and PLA is in stock, so I reserved p1 with PLA. | p1 is not free and PLA is not in stock, so I could not reserve p1 with PLA. | 0.772 |
| Paraphrase | p1 is free and PLA is in stock, so I reserved p1 with PLA. | Reservation complete. Printer number 1 was free, and PLA resin was still in stock. | 0.464 |
| Same meaning, different word order | There is one PETG spool. | The PETG in stock is one. | 0.545 |
The opposite sentence scored 0.772, higher than the correct paraphrase at 0.464. Character overlap does not represent correctness of meaning. The metric is useful mainly as a regression test where the wording of answers is nearly fixed, to see that nothing broke between versions. To check meaning, group B’s final_response_match_v2 exists, but it needs a judge LLM and we did not test it.
Efficiency metrics and duration — read a jump in inference calls as a sign of looping
The four efficiency metrics only report values; they never pass or fail a case, and their status is always INFORMATIONAL. The docs say token counts show n/a when the model returns no usage, but in this run the local model did return values: in normal run two, tokens ranged from 926 to 2,033 (448 for the greeting q5).
The ADK docs say a jump in inference calls usually means the agent is looping or re-planning. Normal run two versus the always-call-stock agent:
| Question | Inference calls (normal run 2) | Inference calls (extra stock) | Tool calls (normal run 2) | Tool calls (extra stock) |
|---|---|---|---|---|
| q1 | 2 | 3 | 1 | 2 |
| q2 | 2 | 2 | 1 | 2 |
| q3 | 3 | 3 | 3 | 3 |
| q4 | 2 | 2 | 2 | 3 |
| q5 | 1 | 2 | 0 | 1 |
The extra stock call added one inference call in q1 and q5, and one tool call in four questions (all but q3). This breakage is a single extra call added to the instruction, not a loop, so the increase is small.
The docs also say inference calls and token counts are deterministic for the same input and model. In this run, inference calls were identical across normal runs one and two for all five questions, but token counts differed (1,863 vs 2,033 for q3). This was not an experiment to test the docs and we did not check sampling settings, so we draw no conclusion.
Durations were single measurements and vary with server load: the normal q2 took 26.776 s in run one and 16.453 s in run two. The docs also advise comparing distributions over many runs rather than two numbers. As a brake on reasoning loops, RunConfig has max_llm_calls (a cap on inference calls), which defaults to 500 in v2.11.0.
Production evaluation lives on Agent Platform — continuous evaluation and drift
“Continuous evaluation” in 4.1 item 2 can take two forms. One is running local evaluations repeatedly in CI. The ADK docs recommend group A’s tool_trajectory_avg_score and response_match_score for CI/CD pipelines and regression tests because they are fast and predictable, and say adk eval can be added to build and validation steps. We only ran the CLI per condition and did not build such a pipeline. The other is Agent Platform’s online monitors, which score production traces on Google Cloud.
Online monitors continuously evaluate the quality of production agents. They require a deployed agent, Cloud Trace enabled in the project, and the OpenTelemetry signals evaluation needs: attributes such as agent name and conversation ID, plus inference events that record input and output messages, system instructions and tool definitions. In ADK, you emit these by setting OTEL_SEMCONV_STABILITY_OPT_IN and OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT.
Evaluation runs on a schedule, typically every 10 minutes. Matching data is pulled from Cloud Trace and Cloud Logging, scored, written back to Cloud Logging, and the numbers are sent to Cloud Monitoring. A per-run cap on the number of items can keep evaluation costs down.
On the same side, quality alerts notify you when agent performance falls below a threshold. The exam guide’s drift is officially described as an observable decline in performance over time caused by changes in user behavior or external data, which can happen even when the underlying model is unchanged. Locally, the most you can do is run the same evalset per version and compare; there is no local mechanism that periodically scores production traces.
The Gen AI evaluation service is a set of tools for objectively evaluating generative AI models. Methods include adaptive rubrics that generate pass/fail criteria per prompt (described as recommended), computed metrics such as ROUGE and BLEU when references exist, and evaluation functions you write in Python. For agents it evaluates both the final response and the trajectory (the sequence of tool calls) leading to it.
Trajectory metrics include exact match (trajectory_exact_match), in-order match, any-order match, precision, recall and single-tool use. The names resemble ADK’s matching modes but the implementation is separate, and ADK has no precision or recall. As of October 2026, both pages that describe agent evaluation (Evaluate Gen AI agents, and Evaluate agents with the GenAI Client) carry a Preview label.
Another entry point is Agent evaluation under Optimize. It can evaluate a local ADK agent to quickly fix prompts and tool settings, or measure a deployed agent’s quality with past traces or synthetic benchmarks. It can also simulate environments that inject HTTP 503 errors or latency into tool calls, and there is a flow using the agents-cli eval command. We tried none of these.
Choosing a deployment target — Agent Runtime, Cloud Run and GKE from official materials
From here on this is a summary of official materials; we did not deploy anywhere. 4.2’s first item is choosing a deployment target by use case, requirements and cost. A Google Cloud Architecture Center page (updated April 21, 2026) frames the choice like this:
| Architecture Center condition | Target |
|---|---|
| Python agent needing a fully managed environment with minimal operations work | Agent Runtime |
| Containers needing serverless, event-driven scaling and language freedom | Cloud Run |
| Containers with complex stateful requirements and fine-grained infrastructure control | GKE |
| Already running apps on Cloud Run or GKE | Using the same platform simplifies development and operations |
Mind the language condition. The Agent Runtime product page, updated October 9, 2026, says any agent in any language or framework can be deployed if it can be containerized and follows the runtime contract. The Architecture Center table is older and still says Agent Runtime suits Python. Do not read the table’s “Python” as meaning Agent Runtime accepts only Python.
Agent Runtime is a fully managed environment for deploying, operating and scaling agents. It is a paid service with a free tier (amounts are not covered here). Its names have changed over time: the API resource is still ReasoningEngine for compatibility, and in v2.11.0 the ADK CLI subcommand is still adk deploy agent_engine.
Cloud Run scales instances with request volume, down to zero. Its maximum request timeout is one hour, which can constrain long-running agent work. The Cloud Run docs split agent hosting into four resource types and their best uses:
- Services: request-driven agents with varying user traffic; scaling to zero is described for Services in this doc
- Jobs: work that runs to completion, such as batch evaluation
- Instances: single stateful agents that keep running
- Worker pools: fleets of agents working in the background on messages from a queue (Pub/Sub, Kafka and the like)
Deploy with adk deploy cloud_run (recommended for Python) or gcloud run deploy. The basic differences between Cloud Run and GKE are covered in Cloud Digital Leader Part 2: Infrastructure, Security, and Operations.
The ADK docs describe GKE as the choice when you want fine-grained control over deployment or want to run open models. The Architecture Center notes that this control comes with a larger operational burden and complexity. Deploy with adk deploy gke or Kubernetes manifests. ADK agents can also run in containers in environments with no connection to Google Cloud.
As a supplement we also read the agents-cli docs in Google’s repository (an instruction file for coding agents, not product documentation). They treat all three as production options and rate setup effort as low for Agent Runtime and higher for GKE because of the Kubernetes knowledge required.
Isolating failures and monitoring — what you can see locally and what you see in the cloud
4.2’s remaining two items are isolating failures and monitoring performance, reliability and cost. Here are the exam guide’s example terms, with the official description and where you look:
| Exam guide term | What you can see locally | What you see in the cloud |
|---|---|---|
| Drift (performance decline observable over time) | Run the same evalset per version and compare | Online monitors and quality alerts |
| Tool invocation latency | ADK traces, exported in OpenTelemetry OTLP format to compatible tools | The observability Tools view (per-tool p95 latency, call counts, error rates) and Cloud Trace |
| Agent reasoning loops | Inference-call counts in the efficiency metrics, the adk web Trace tab, the max_llm_calls cap | Traces sent to Cloud Trace and similar tools (loops show up as repeated layers) |
| System failures | Change log levels (the ADK docs recommend INFO or WARNING in production) | Agent Runtime sends stdout and stderr to reasoning_engine_stdout and reasoning_engine_stderr by default; the observability views show p50, p95 and p99 latency and error rates |
Cloud observability requires the agent to send OpenTelemetry data to Google Cloud Observability. On Agent Runtime, the environment variable GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY turns on traces, logs and metrics, but it does not record prompt and response content; for that, also set OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, which online monitors need for scoring. The ADK docs say input-and-output-only monitoring is not enough for agents of any real complexity.
Local models have their own loop causes. As noted, the ADK docs warn that specifying Ollama as ollama can cause endless tool calls. In this run we used ollama_chat and saw at most three inference calls. If inference calls suddenly jump, the way the model is specified is worth suspecting too.
What this run cannot tell us
- One run per condition, two for the normal agent; the numbers are “what happened in this run” and do not generalize
- One model (gemma4:12B) and a five-question, single-turn evalset; multi-turn conversations and multi-agent handoffs were not tested
- Only group A metrics were used; whether group B (judge LLM) or group C (cloud service) catch a missing tool or an opposite-meaning answer is unknown
- IN_ORDER, ANY_ORDER and EXACT + ignore_args values are re-scored, not rerun per mode
- The without-thinking values are our recomputation; ADK has no such setting, and we did not pin down when thinking parts appear
- Durations are single measurements, too few to compare
- No continuous evaluation was built; we ran the CLI per condition, with neither CI runs nor online monitors
- Nothing was deployed; §4.2 is a summary of official materials, and on pricing we confirmed only that Agent Runtime is paid with a free tier
- The evalset is our own, not exam content; we have not taken the exam, and local practice does not replace the labs
Summary — ADK evaluation starts with choosing the matching mode
- ADK evaluation metrics split into those computed locally (A), those needing a judge LLM (B, gemini-2.5-flash by default) and those needing Google’s cloud evaluation service (C). Within the official docs, group A is what you can run locally without Google APIs
- For tool-call matching, only the default EXACT caught extra calls. IN_ORDER and ANY_ORDER gave 1.0 to a question where calling no tool is correct even with an extra call. Evaluate edge cases with EXACT
- Matching compares only call names and arguments, not success. With the status tool removed, two of the three questions that use it still passed EXACT
- Response matching is character overlap and, in v2.11.0, includes thinking parts. The opposite-meaning sentence (0.772) beat the paraphrase (0.464)
- Continuous evaluation can be built by running local evaluations in CI (not built here). Online monitors that score production traces, and choosing a deployment target, live on Google Cloud and were summarized from official materials
If you use ADK evaluation to prepare for the exam, decide the matching mode and how to write edge cases before writing the evalset. If you do not decide in advance what counts as a failure, the machine’s pass/fail tells you little. Writing outputs and failure conditions so a machine can judge them is also covered in Writing CAD as Code and Letting the Machine Check It. Then build one broken agent and run it through the evaluation, and you will see concretely what your evalset misses.
Sources
- Professional Agentic Architect exam guide (PDF)
- ADK docs: Why evaluate agents
- ADK docs: Evaluation Criteria
- ADK Python v2.11.0 release
- ADK v2.11.0 source: trajectory_evaluator.py
- ADK v2.11.0 source: final_response_match_v1.py
- ADK v2.11.0 source: eval_config.py
- ADK v2.11.0 source: eval_metrics.py
- ADK docs: Ollama model host
- ADK docs: Deploying your agent
- ADK docs: Deploy to Agent Runtime
- ADK docs: Deploy to Cloud Run
- ADK docs: Deploy to GKE
- ADK docs: Observability for agents
- Agent Platform: Gen AI evaluation service overview
- Agent Platform: Configure a judge model
- Agent Platform: Evaluate a judge model
- Agent Platform: Evaluate Gen AI agents
- Agent Platform: Agent evaluation
- Agent Platform: Continuous evaluation with online monitors
- Agent Platform: Configure quality alerts
- Agent Platform: Observability overview
- Agent Platform: Agent Runtime
- Cloud Architecture Center: Choose your agentic AI architecture components
- Cloud Run: Host AI agents on Cloud Run resources
- google/agents-cli deploy skill (SKILL.md)





