
Table of Contents
By Khimananda Oli | Last reviewed: October 2026
Evaluating an AI agent means scoring the path it took, not just the answer it gave — because a correct final answer can hide a wasteful loop, a wrong tool that happened not to matter, or a lucky recovery from an error a stricter judge would never excuse. Score tool-call correctness deterministically against an expected list, score task completion with an LLM judge comparing the goal to the outcome, and track step count and cost as a third, separate signal. Combining all three into one pass/fail number hides exactly the failure you built the eval to catch.
Why is evaluating an agent a different problem from evaluating an LLM's output?
To evaluate AI agents properly you need more than a single-turn eval. If you have already built evals for single-turn LLM responses — see how to evaluate LLM outputs — the instinct is to point the same machinery at an agent: capture the final answer, score it against a reference, done. That approach misses the part of an agent that actually breaks in production.
An agent is not one call; it is a sequence — the model picks a tool, the tool returns a result, the model decides what to do next, and the loop repeats until it produces a final answer. Two agents can reach the identical correct answer through completely different paths, and only one of those paths is the one you want running at scale:
- The efficient path: three tool calls, each one informed by the last, reaching the answer in the minimum necessary steps.
- The lucky path: nine tool calls, two of them wrong, one argument malformed and silently retried, the agent effectively guessing its way to a correct answer that an output-only eval scores as a perfect pass.
Score only the final answer and both paths look identical. The lucky path is the one that burns ten times the tokens, takes ten times as long, and — critically — is the one that fails differently and unpredictably the next time the same question is asked with slightly different phrasing. This is why agent evaluation decomposes into at least three separate measurements, each catching a failure mode the others cannot see.
What does trajectory matching actually check?
Trajectory evaluation compares the sequence of tool calls an agent actually made against a reference sequence you define for the test case — no LLM judge required, which makes it fast, cheap and perfectly repeatable. Google's Agent Development Kit (ADK) defines this with three match strategies that are a clean mental model regardless of which framework you use:
| Match type | Requirement | Use when |
|---|---|---|
| Exact | The actual and expected tool calls match perfectly — no extra calls, none missing, same order | The task has exactly one correct procedure, e.g. a fixed approval workflow |
| In-order | Every expected call appears in the actual sequence in the same relative order, but other calls may appear between them | Steps must happen in sequence, but the agent may legitimately check extra things along the way |
| Any-order | Every expected call appears somewhere in the actual sequence, order unconstrained | A set of lookups must all happen, but which one happens first genuinely does not matter |
Each tool call is compared by name and, optionally, by arguments — ADK's evaluator exposes an ignore_args option for exactly this, because sometimes you only care that get_customer_record was called at all, not whether it was called with customer_id=481 versus customer_id="481". Pick exact matching when the procedure itself is the thing under test; pick any-order when you only care that the right information was gathered.
How do you score tool-call correctness without writing your own matcher?
DeepEval's ToolCorrectnessMetric implements the same idea as a drop-in metric, comparing the tools an agent actually called against the ones you expected:
from deepeval.test_case import LLMTestCase, ToolCall
from deepeval.metrics import ToolCorrectnessMetric
from deepeval import evaluate
test_case = LLMTestCase(
input="A customer wants a refund for order #4821.",
actual_output="Refund of $42.00 has been issued for order #4821.",
tools_called=[
ToolCall(name="lookup_order",
input={"order_id": "4821"}),
ToolCall(name="check_refund_eligibility"),
ToolCall(name="issue_refund",
input={"order_id": "4821", "amount": 42.00}),
],
expected_tools=[
ToolCall(name="lookup_order"),
ToolCall(name="check_refund_eligibility"),
ToolCall(name="issue_refund"),
],
)
metric = ToolCorrectnessMetric(
should_consider_ordering=True, # like ADK's in-order match
should_exact_match=False, # names only, args ignored
)
evaluate(test_cases=[test_case], metrics=[metric]) should_consider_ordering and should_exact_match are the two levers worth understanding before you write a single test case. Leave both off for a loose, "did it touch the right tools" check during early development; turn both on once you are hardening a workflow where the sequence and the arguments genuinely matter — a payment flow is a reasonable candidate, a free-form research assistant usually is not.
How do you measure whether the agent actually finished the job?
Tool-call correctness tells you the agent used the right tools. It does not tell you the agent's final state is actually what the user asked for — an agent can call every expected tool in the right order and still fail to synthesize them into a complete answer, or stop one step early. That is what task completion metrics are for, and because "did this accomplish the goal" is a judgment call rather than a string comparison, this layer needs an LLM judge.
DeepEval's TaskCompletionMetric works directly from a trace rather than a single input/output pair — it extracts the task and the outcome from the full agent trajectory, then scores how well the outcome aligns with the task:
from deepeval.tracing import observe
from deepeval.metrics import TaskCompletionMetric
from deepeval.dataset import Golden
from deepeval import evaluate
@observe()
def refund_agent(user_input: str) -> str:
# ... your agent's full tool-calling loop runs here ...
return "Refund of $42.00 has been issued for order #4821."
metric = TaskCompletionMetric(
threshold=0.7, model="gpt-4o", include_reason=True
)
goldens = [Golden(input="A customer wants a refund for order #4821.")]
evaluate(goldens=goldens, observed_callback=refund_agent, metrics=[metric]) Because this runs an LLM judge over the trace, give it a tighter rubric than "was this good" — the judge model should be told explicitly what counts as task completion for your domain (did the refund actually get issued, not just discussed), the same discipline covered generally in how to evaluate LLM outputs. A vague judge prompt produces a score that moves with the judge model's mood more than with the agent's actual behaviour, and it inherits every weakness of LLM-as-judge scoring — including the tendency to reward confident, well-formatted answers that are quietly wrong, which is exactly the failure mode covered in reducing LLM hallucinations.
What should you track alongside correctness?
A fourth dimension sits outside pass/fail entirely: efficiency. Two trajectories can both score a perfect 1.0 on trajectory match, tool correctness and task completion, and still differ by an order of magnitude in cost and latency — exactly the efficient-versus-lucky-path problem from the opening example, except now both paths are "correct" by every quality metric you have. Track these as their own time series, not folded into a pass/fail score:
- Step count — the number of tool calls per successful trajectory. A sudden increase in the median, even with completion rate unchanged, usually means a tool's output format changed and the agent is now working around it.
- Loop/retry count — how often the agent calls the same tool with near-identical arguments in succession. This is the signature of an agent that is confused rather than exploring, and it is invisible to a metric that only looks at the final trajectory.
- Cost and latency per completed task — the metric that actually appears on your bill. If you are already instrumenting LLM calls per OpenTelemetry's GenAI semantic conventions,
gen_ai.invoke_agent.durationand the per-toolgen_ai.execute_tool.durationgive you exactly this, broken down by which step in the trajectory is slow.
How do you build a golden set to evaluate AI agents?
The same golden-set discipline that applies to RAG evaluation — see RAG evaluation metrics for the general approach — applies here with one addition: each test case needs an expected trajectory, not just an expected answer. For the refund example above that means recording not only "a $42 refund should be issued" but "the agent should call lookup_order, then check_refund_eligibility, then issue_refund, in that order." Writing that down is the part teams skip, and it is the part that makes trajectory and tool-call evaluation possible at all — without it you are back to scoring the final answer alone and losing the one thing agent evaluation exists to catch.
Stratify the set the same way you would for any eval: include cases with a single clear correct path, cases where more than one tool sequence legitimately reaches the right answer (use any-order matching for these), and — importantly — cases where the right behaviour is to call no tools at all, such as a question the agent should answer directly or decline. An agent that reaches for a tool on every input, including the ones that do not need one, is a real and common failure that only shows up if your golden set contains examples where the expected tool list is empty.
What does a worked example of all three layers together look like?
Concreteness helps more than any abstract description. Take an internal IT-helpdesk agent that resolves "reset my password" and similar tickets, evaluated against three golden test cases after a prompt change intended to make it more cautious about irreversible actions.
| Test case | Trajectory match | Tool correctness | Task completion | Verdict |
|---|---|---|---|---|
| Standard password reset | Exact: pass | 1.0 | 0.95 | Ship — behaves as before |
| Reset for a locked, high-privilege admin account | In-order: pass | 1.0 | 0.40 | Tools correct, but the agent reset the account without the extra approval step the new prompt was meant to add — task completion catches what tool correctness could not |
| Ambiguous request needing clarification | Any-order: fail — called reset_password directly | 0.0 | 0.60 | Block — the agent should have called ask_clarifying_question first; it guessed instead and got a plausible-sounding but wrong answer |
Each layer earns its place in this example. Trajectory matching alone would have shipped the regression in row two, since the expected tools were still called in a valid order — the extra approval step was simply never defined as part of the expected trajectory, so trajectory matching had no way to flag its absence. Tool correctness alone would have missed it for the same reason: every expected tool was present. Only task completion, scored against the explicit intent "irreversible admin actions require approval," caught that the agent had technically used the right tools while skipping the actual point of the prompt change. Row three shows the opposite pattern — trajectory and tool correctness both fail loudly and immediately, with no LLM judge required at all, because the agent simply called the wrong tool.
Where does this fit with safety evaluation?
Trajectory, tool-call and task-completion metrics answer "did the agent do its job well." They say nothing about whether it should have been allowed to take a given action at all — that is a permissions and approval question, covered in guardrails for autonomous AI agents and, for the specific case of an agent operating infrastructure, building an AI agent for Linux server administration. Run both kinds of evaluation. A trajectory that scores perfectly on correctness and efficiency but included a tool call your guardrails should have blocked is not a quality bug — it is the guardrail failing, and no amount of task-completion scoring will surface that; a deliberate adversarial pass, as in the hallucination-reduction and red-teaming work for LLM apps generally, is what finds it. If you are coordinating several agents rather than one, the trajectory question becomes which agent called which tool and in what order across the whole system, which is where multi-agent systems: patterns and pitfalls picks up.
If you have not built your first agent yet, what are AI agents: a practical introduction and building your first AI agent with tool use are the right starting point before any of this evaluation machinery is worth setting up — there is no trajectory to score until there is a working tool-calling loop to score it from.
If you want a harness built to evaluate AI agents properly — not just their final output — — golden trajectories, tool-correctness gates in CI, and task-completion judging with a rubric tuned to your domain — my DevOps and cloud consulting services cover exactly this.