These are ADK-based evaluations even when your team builds agents with LangGraph. Keep your agent in its current framework. Use the same eval JSON, Python scorer, and Harnest command in either project; Harnest adapts LangGraph responses into the ADK objects the scorer receives.
Decide what needs to be custom
Start with a working agent and installed dependencies. Evaluation calls the agent’s configured model and services; this deterministic scorer makes no additional calls.
1. Add the scorer
Use the root agent’slib/ directory for scoring code. Keep only eval set JSON and the shared configuration under evals/.
lib/eval_metrics.py
1.0 when both terms are present and 0.0 otherwise. The conversation score is the mean of its turn scores. With a threshold of 1.0, every turn must satisfy the rule. A lower threshold permits some turns to fail.
This checks text presence, not accuracy: an incorrect answer mentioning both terms passes. Adapt the rule or combine it with a reference or judge metric. See the custom metric API.
2. Describe the test cases
evals/location-answers.evalset.json
userContent to the agent and scores its response. finalResponse is a golden reference for reference-based metrics; it is never injected into the agent.
The filename stem must match eval_set_id. Add further cases to cover the behavior you care about. Put sequential turns inside one case for multi-turn evaluation; separate cases do not stand in for a shared conversation.
3. Register the metric and threshold
evals/test_config.json
criteria and customMetrics. Harnest compiles root lib/ modules under harnest.lib, so the registered path differs from the local unit-test import below.
Merge the criterion and registration into an existing test_config.json without replacing other checks.
4. Unit-test the scorer
Test passing, failing, missing-response, and multi-turn inputs before running the live agent. These tests evaluate only your scorer and require no model credentials.tests/unit/test_eval_metrics.py
5. Run the eval and inspect evidence
--no-output for quiet terminal output while retaining the file.
Find required_location_terms in each case’s overallEvalMetricResults. Inspect evalMetricResultPerInvocation for the turn scores, actual answers, and available reference answers supplied by the scorer. The command fails when the quality gate fails. See Run and inspect results for result fields and CI handling.
Extend the rule safely
- Inspect
intermediate_datawhen your rule needs captured tool calls or results; do not infer tool execution from answer text alone. - Use the full
actuallist to inspect a conversation-level rule. Keep overall and per-turn scores consistent; ADK’s command gate averages the scored turn results. See score aggregation before implementing a final-turn-only metric. - Handle
expected=Nonefor simulated conversations. Add a conversation scenario when you need generated user turns. - For LLM judgment, prefer a built-in rubric metric where it fits. Arbitrary network or model calls inside your Python scorer do not automatically inherit Harnest’s judge transport adapter; configure and clean up those clients explicitly.