Skip to main content
Turn a business rule into a repeatable evaluation by pairing an ADK eval set with a custom Python scoring function. This walkthrough checks that every answer includes two required location terms, then saves the scored result.
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 from a working Harnest agent with its dependencies installed. Live evaluation uses the agent’s normal model and service configuration. The scorer below is deterministic and makes no additional model or service calls.

1. Add the scorer

Use the root agent’s lib/ directory for scoring code. Keep only eval set JSON and the shared configuration under evals/.
lib/eval_metrics.py
Each answer scores 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 is a text-presence check, not a factuality check: an incorrect answer that mentions both terms still passes. Replace the scoring rule with your actual product contract, or combine it with a reference or judge metric. See the custom metric API for the full input and result contract.

2. Describe the test cases

evals/location-answers.evalset.json
Harnest sends userContent to your real agent. The authored finalResponse is a golden reference, not the actual output and not an answer injected into the agent. This scorer checks the actual output; reference-based metrics can also use the golden answer. 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
Use the same metric key in criteria and customMetrics. Harnest compiles root lib/ modules under harnest.lib, so the registered path differs from the local unit-test import below. If test_config.json already exists, merge this criterion and registration into it. Do not create a second config or replace existing quality checks. You can use built-in and custom metrics in the same criteria object.

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
See Testing and compilation for the authored unit-test lane and imports.

5. Run the eval and inspect evidence

Use this command for either an ADK or a LangGraph project; Harnest uses the configured agent framework. Unit tests run first. The live eval then runs the agent, calls your registered scorer, and writes the result. Add --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_data when your rule needs captured tool calls or results; do not infer tool execution from answer text alone.
  • Use the full actual list 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=None for 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.
A custom scorer is trusted Python code executed during evaluation. It can access the process environment and call external systems. Review implementations before running them, and do not put credentials into eval files or return them as diagnostic evidence.