Skip to content

Commit 0f0fa6b

Browse files
haranrkcopybara-github
authored andcommitted
feat: Add llm_judge_match ADK eval sample
Add contributing/samples/evaluation/llm_judge_match/, demonstrating final_response_match_v2 (LLM-judged semantic response match) run via `adk eval` over the shared home-automation agent. Co-authored-by: Haran Rajkumar <haranrk@google.com> PiperOrigin-RevId: 952611733
1 parent f71d9df commit 0f0fa6b

4 files changed

Lines changed: 121 additions & 0 deletions

File tree

contributing/samples/evaluation/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ answer.
4242
| [`basic_criteria`](./basic_criteria/) | Deterministic, reference-based scoring | `tool_trajectory_avg_score`, `response_match_score` |
4343
| [`test_file_vs_evalset`](./test_file_vs_evalset/) | `.test.json` vs `.evalset.json` conventions | `tool_trajectory_avg_score`, `response_match_score` |
4444
| [`custom_metric`](./custom_metric/) | Write your own metric | `temperature_safety_score` (custom) |
45+
| [`llm_judge_match`](./llm_judge_match/) | LLM-judged semantic match | `final_response_match_v2` |
4546

4647
## Graph
4748

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# LLM-judged response match
2+
3+
## Overview
4+
5+
Evaluates the shared home-automation agent with `final_response_match_v2`, a
6+
criterion that uses a judge model to decide whether the agent's final answer
7+
is *semantically* equivalent to a reference response. Because the judge reasons
8+
about meaning rather than word overlap, it tolerates phrasing and formatting
9+
differences that `response_match_score` (ROUGE-1) would penalize, for example
10+
"The temperature in the Kitchen is currently 24°C." vs. the reference "It's
11+
currently 24 degrees Celsius in the Kitchen."
12+
13+
This criterion needs a model credential for the judge (a Gemini API key or
14+
Vertex), in addition to the credential used for the agent's own inference.
15+
16+
## Sample Inputs
17+
18+
The eval set (`home_automation.evalset.json`) contains one single-turn case:
19+
20+
- `How warm is the Kitchen right now?`
21+
22+
The reference answer is deliberately phrased differently from how the agent is
23+
likely to respond, so ROUGE-1 would score low while the semantic judge passes.
24+
25+
## How To
26+
27+
Run the sample from the workspace root:
28+
29+
```bash
30+
adk eval contributing/samples/evaluation/home_automation_agent \
31+
contributing/samples/evaluation/llm_judge_match/home_automation.evalset.json \
32+
--config_file_path contributing/samples/evaluation/llm_judge_match/eval_config.json \
33+
--print_detailed_results
34+
```
35+
36+
`adk eval` takes the agent folder and the eval-set file as two separate
37+
arguments, so this folder holds only eval data (`home_automation.evalset.json`),
38+
the criteria config (`eval_config.json`), and this README, with no agent code.
39+
40+
### `judge_model_options`
41+
42+
`final_response_match_v2` is configured through `judge_model_options` in
43+
`eval_config.json`:
44+
45+
- `judge_model`: the model that acts as the judge. It is resolved through the
46+
standard ADK model registry, so it is a normal model name (here
47+
`gemini-2.5-flash`). The judge is a separate model from the one the agent uses
48+
for its own inference.
49+
- `num_samples`: how many independent judgements to request from the judge
50+
model (here `5`). The criterion takes a majority vote across those samples
51+
and converts the fraction of "equivalent" votes into the score, which reduces
52+
the impact of any single noisy judgement.
53+
54+
The `threshold` is `0.8`, so at least a strong majority of the judge samples must
55+
find the responses equivalent for the case to pass.
56+
57+
### Semantic vs. lexical matching
58+
59+
Use `final_response_match_v2` when a correct answer can legitimately be worded or
60+
formatted many different ways and you care about *meaning*, not exact wording:
61+
paraphrases, reordered clauses, "24°C" vs. "24 degrees Celsius", extra polite
62+
framing, and so on. A lexical metric like `response_match_score` (ROUGE-1, used
63+
in the `basic_criteria` sample) only measures word overlap, so it would penalize
64+
these harmless rephrasings and force you to lower the threshold until it no
65+
longer distinguishes right answers from wrong ones. The trade-off is that the
66+
LLM judge requires a model call per sample (cost and latency) and, being
67+
model-based, can vary slightly between runs; `response_match_score` is fully
68+
local and deterministic. Reach for the semantic judge when meaning matters more
69+
than phrasing, and keep the lexical metric when you need cheap, deterministic
70+
scoring.
71+
72+
## Related Guides
73+
74+
- Evaluation overview: https://adk.dev/evaluate/
75+
- Evaluation criteria reference: https://adk.dev/evaluate/criteria/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"criteria": {
3+
"final_response_match_v2": {
4+
"threshold": 0.8,
5+
"judge_model_options": {
6+
"judge_model": "gemini-2.5-flash",
7+
"num_samples": 5
8+
}
9+
}
10+
}
11+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"eval_set_id": "llm_judge_match",
3+
"name": "LLM-judged semantic match",
4+
"description": "final_response_match_v2 tolerates rephrasing that ROUGE-1 would penalize.",
5+
"eval_cases": [
6+
{
7+
"eval_id": "kitchen_temp_rephrased",
8+
"conversation": [
9+
{
10+
"invocation_id": "lj-1",
11+
"user_content": {
12+
"parts": [{"text": "How warm is the Kitchen right now?"}],
13+
"role": "user"
14+
},
15+
"final_response": {
16+
"parts": [{"text": "It's currently 24 degrees Celsius in the Kitchen."}],
17+
"role": "model"
18+
},
19+
"intermediate_data": {
20+
"tool_uses": [
21+
{"name": "get_temperature", "args": {"location": "Kitchen"}}
22+
],
23+
"intermediate_responses": []
24+
}
25+
}
26+
],
27+
"session_input": {
28+
"app_name": "home_automation_agent",
29+
"user_id": "user",
30+
"state": {}
31+
}
32+
}
33+
]
34+
}

0 commit comments

Comments
 (0)