Skip to content

Commit 6ba41ef

Browse files
haranrkcopybara-github
authored andcommitted
feat: Add ADK evaluation samples: shared home-automation agent
Start contributing/samples/evaluation/, a family of single-concept samples that demonstrate ADK evaluation through the `adk eval` CLI over one shared agent. This first change adds the foundation shared by every sub-sample: - home_automation_agent/: the shared agent (device-control tools) that all sub-samples evaluate. - README.md: an overview of the family and how each sub-sample is run. Subsequent changes each add one single-concept sub-sample folder. Co-authored-by: Haran Rajkumar <haranrk@google.com> PiperOrigin-RevId: 952558506
1 parent 67ab27f commit 6ba41ef

3 files changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# ADK evaluation samples
2+
3+
## Overview
4+
5+
A family of single-concept samples that each show one way to evaluate the *same*
6+
shared home-automation agent with the `adk eval` CLI. Every sample points `adk eval` at one agent and differs only in its eval data and criteria, so you can
7+
compare evaluation techniques (deterministic reference matching, custom metrics,
8+
LLM-as-a-judge, rubrics, and user simulation) side by side.
9+
10+
## The shared agent
11+
12+
`home_automation_agent/` is a small agent that controls smart-home devices and
13+
temperatures. Its five tools (`get_device_info`, `set_device_info`,
14+
`get_temperature`, `set_temperature`, `list_devices`) are deterministic, backed by
15+
in-memory state, so eval trajectories are reproducible. The module exposes
16+
`reset_data()`, which `adk eval` calls to reset that state between eval cases.
17+
18+
Every sample evaluates this same agent. `adk eval` takes the agent path and the
19+
eval-set path as two separate arguments, so each sub-sample folder holds only
20+
eval data and its criteria config, never a copy of the agent code.
21+
22+
## How evaluation runs
23+
24+
`adk eval` runs in two phases: (1) live inference, where it actually runs the
25+
agent against each eval input to produce responses and tool calls, and then (2)
26+
scoring, where it compares that output against the case's criteria. Because phase 1
27+
runs the real agent, a model credential is required for every sample, even the
28+
deterministic ones. Provide a Gemini API key in `home_automation_agent/.env`,
29+
or configure Vertex.
30+
Samples that use an LLM judge or a user simulator make additional model calls, but
31+
they resolve through the same model registry and credentials.
32+
33+
Because live responses vary from run to run, the deterministic, reference-based
34+
criteria use lenient response thresholds (e.g. `response_match_score` at
35+
`0.5`) so that harmless phrasing differences don't fail an otherwise-correct
36+
answer.
37+
38+
## Samples
39+
40+
| Sample | Concept | Criteria |
41+
| ------ | ------- | -------- |
42+
43+
## Graph
44+
45+
```mermaid
46+
graph TD
47+
A[home_automation_agent] --> B(get_device_info)
48+
A --> C(set_device_info)
49+
A --> D(get_temperature)
50+
A --> E(set_temperature)
51+
A --> F(list_devices)
52+
```
53+
54+
## Related Guides
55+
56+
- Evaluation overview: https://adk.dev/evaluate/
57+
- Evaluation criteria reference: https://adk.dev/evaluate/criteria/
58+
- User simulation guide: https://adk.dev/evaluate/user-sim/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""A small home-automation agent shared by the evaluation samples.
16+
17+
All tools are deterministic (backed by in-memory dicts) so that eval
18+
trajectories are reproducible. `reset_data()` is picked up by `adk eval` to
19+
reset state between eval cases.
20+
"""
21+
22+
from google.adk import Agent
23+
24+
_DEVICES = None
25+
_TEMPERATURES = None
26+
27+
28+
def reset_data() -> None:
29+
"""Resets in-memory state. Called by adk eval between eval cases."""
30+
global _DEVICES, _TEMPERATURES
31+
_DEVICES = {
32+
"device_1": {"status": "ON", "location": "Living Room"},
33+
"device_2": {"status": "OFF", "location": "Bedroom"},
34+
"device_3": {"status": "OFF", "location": "Kitchen"},
35+
}
36+
_TEMPERATURES = {"Living Room": 22, "Bedroom": 20, "Kitchen": 24}
37+
38+
39+
# Initialize module-level state at import time.
40+
reset_data()
41+
42+
43+
def get_device_info(device_id: str) -> dict:
44+
"""Returns the status and location of a device, or an error string."""
45+
return _DEVICES.get(device_id, {"error": "Device not found"})
46+
47+
48+
def set_device_info(device_id: str, status: str) -> str:
49+
"""Sets a device status to 'ON' or 'OFF'."""
50+
if device_id not in _DEVICES:
51+
return "Device not found"
52+
_DEVICES[device_id]["status"] = status
53+
return f"Device {device_id} is now {status}."
54+
55+
56+
def get_temperature(location: str) -> str:
57+
"""Returns the current temperature (Celsius) of a location."""
58+
if location not in _TEMPERATURES:
59+
return "Location not found"
60+
return f"{_TEMPERATURES[location]}"
61+
62+
63+
def set_temperature(location: str, temperature: int) -> str:
64+
"""Sets the target temperature (Celsius) for a location.
65+
66+
Acceptable range is 18-30 Celsius. Do not call this tool with a value
67+
outside that range.
68+
"""
69+
if location not in _TEMPERATURES:
70+
return "Location not found"
71+
_TEMPERATURES[location] = temperature
72+
return f"Temperature in {location} set to {temperature}C."
73+
74+
75+
def list_devices(status: str = "", location: str = "") -> list:
76+
"""Lists devices, optionally filtered by status and/or location."""
77+
result = []
78+
for device_id, info in _DEVICES.items():
79+
if (not status or info["status"] == status) and (
80+
not location or info["location"] == location
81+
):
82+
result.append({"device_id": device_id, **info})
83+
return result
84+
85+
86+
root_agent = Agent(
87+
name="home_automation_agent",
88+
description="Controls smart-home devices and temperature.",
89+
instruction=(
90+
"You are a home-automation assistant. Use the available tools to"
91+
" inspect and control devices and temperatures. When the user asks to"
92+
" change something, call the matching tool, then confirm the result in"
93+
" one short sentence. If the user asks to set a temperature outside the"
94+
" safe range of 18-30 Celsius, refuse and do not call the tool."
95+
),
96+
tools=[
97+
get_device_info,
98+
set_device_info,
99+
get_temperature,
100+
set_temperature,
101+
list_devices,
102+
],
103+
)

0 commit comments

Comments
 (0)