Evaluating agents: component scorers, trajectory checks, and end-to-end harnesses
Build
Why evaluation is the hardest part of agentic engineering
An agent that looks correct in a demo can fail silently in production. It may call the right tool most of the time but pass subtly wrong arguments. It may reach the right final answer by an unreliable path that will break on a slightly different input next week. Evaluation is the discipline that catches these failures before users do — and it is harder for agents than for traditional software because the surface area is large: you must check not just the final output but also the intermediate steps the agent took to get there.
The goal of this sprint is to make agent evaluation concrete. You will learn three diagnostic depths, understand the difference between deterministic and model-based scorers, and build a small eval harness that demonstrates these ideas in runnable code.
Three diagnostic depths
Evaluation of an agent system naturally decomposes into three levels, each of which catches different failure modes.
Component evaluation tests a single step in isolation — most commonly a single tool call. Given a specific input, did the agent call the right tool? Did it pass the right arguments? Component checks are narrow and fast. They can be run as unit tests and make regression detection cheap. Their limitation is scope: a system where every component passes can still fail at the system level if the components are composed in the wrong order.
Trajectory evaluation tests the path the agent took through a task — the sequence of steps, the reasoning that connected them, and whether that path makes sense. A trajectory check might ask: did the agent try to retrieve context before calling a write tool? Did it attempt a retry after a tool failure? Did it avoid calling a destructive tool before checking preconditions? Trajectory checks are more expensive than component checks and harder to write, but they catch a class of failure that component checks miss entirely: an agent that does the right things in the wrong order.
End-to-end evaluation tests whether the agent satisfied the original goal — ignoring how it got there. This is the most important check for correctness from the user's perspective, but also the most expensive and the hardest to make deterministic. The evaluation criterion for an open-ended task (summarise this document, plan this trip) is hard to express as code, which is where model-as-a-judge enters.
Deterministic scorers vs. model-as-a-judge
A deterministic scorer computes a pass/fail result through code alone, with no model in the loop. Examples include checking whether the agent called a specific tool by name, verifying that an argument matched an expected value, counting whether all required fields are present in a JSON response, or asserting that a file was written to the right path. Deterministic scorers are fast, free to run, fully reproducible, and produce no variance between evaluation runs.
A model-as-a-judge scorer sends the agent's output to a second language model and asks it to rate quality, accuracy, or relevance according to a rubric. The approach was formalised by Zheng et al. (2023), who introduced MT-Bench and Chatbot Arena as structured frameworks for using models to evaluate open-ended responses. Model-as-a-judge is appropriate when the correctness criterion genuinely cannot be expressed as code — for example, judging whether a generated explanation is accurate and clear, or whether a plan is reasonable given underspecified constraints.
The tradeoffs between the two approaches are substantial. Deterministic scorers are reproducible, cheap, and require no model quota. Model-as-a-judge scorers are flexible but introduce variance (the judge model's outputs are stochastic), add latency and cost, and raise the question of which judge model to use and how to calibrate its rubric. Eval frameworks such as DeepEval and OpenAI Evals provide infrastructure for both approaches, including built-in judge templates and metric libraries.
Tracing and regression
An eval harness is most valuable when it is run repeatedly — not just once after a change but continuously as part of a development loop. To support this, the harness must be able to capture a run (record the inputs, the agent's decisions, and the outputs), and re-run a fixed suite against a new agent version to detect regressions.
Observability tools such as LangSmith attach to agent frameworks and record each step of a run as a structured trace, which can then be replayed or compared across versions. Even without a dedicated observability tool, a well-structured eval harness can serve as a lightweight regression guard: if all cases pass today and one fails tomorrow after a code change, you know exactly where the regression is. That tight feedback loop is what makes iterative agent improvement tractable.
Anthropic's guidance on building effective agents emphasises this iteration discipline: evaluating agents on real representative tasks, fixing the failures you discover, and re-running the suite after each fix to confirm forward progress without regressing. The eval harness is the tool that makes this loop mechanical rather than manual.
Never ask an LLM to do something a deterministic function can do reliably. Applied to evaluation: prefer a deterministic scorer wherever the correctness criterion can be expressed as code. Reserve model-as-a-judge for genuinely open-ended quality judgments that code cannot make — and be explicit about which checks in your harness are which, so you know where variance can enter.
When writing a new eval case, ask first: can I express pass/fail as a code assertion? If yes — exact match, JSON key presence, tool name comparison, argument type check — write a deterministic scorer. You get reproducibility and zero cost. If the criterion is genuinely about quality, helpfulness, or coherence that code cannot capture, then model-as-a-judge earns its overhead. Most tool-correctness checks and many task-completion checks can be made deterministic with thoughtful case design; the open-ended residual that genuinely requires a judge is smaller than it first appears.
What to build this sprint
The reference implementation at projects/sprint-5/ is a self-contained eval harness (eval_harness.py) that scores a local agent stub on two dimensions: tool-correctness (did it call the right tool with the right arguments — deterministic) and task-completion (did the final answer match expected — deterministic for the demo). Read it before writing your own version.
-
Run the eval harness. No pip install required — the harness uses only Python's standard library. Run it to see the per-case report:
Confirm you see the per-case pass/fail table and the aggregate report. Note which checks are labelled deterministic and which would need a model-as-a-judge in a real system.bash
$ python agentic-engineering/projects/sprint-5/eval_harness.py --- Agentic Eval Harness --- Case 1: word_count basic tool_call: PASS (expected: count_words, got: count_words) args_correct: PASS (text arg present and non-empty) task_complete: PASS (answer contains expected token "3") Result: PASS Case 2: sentiment basic tool_call: PASS (expected: classify_sentiment, got: classify_sentiment) args_correct: PASS (text arg present and non-empty) task_complete: PASS (answer contains expected token "positive") Result: PASS Case 3: wrong tool selected (deliberate fail) tool_call: FAIL (expected: classify_sentiment, got: count_words) args_correct: PASS (text arg present and non-empty) task_complete: FAIL (answer "3" does not contain expected token "positive") Result: FAIL Case 4: word_count multi-word tool_call: PASS (expected: count_words, got: count_words) args_correct: PASS (text arg present and non-empty) task_complete: PASS (answer contains expected token "5") Result: PASS --- Aggregate --- Passed: 3 / 4 Failed: 1 / 4 tool_call accuracy: 3/4 task_complete accuracy: 3/4 Note: Cases 1, 2, 4 use deterministic scorers (exact tool name + answer token match). Case 3 is a deliberate failure to verify the harness catches regressions. A real system might use model-as-a-judge for open-ended quality checks where deterministic matching is insufficient -- e.g., evaluating a generated summary.
-
Read the eval case structure. Open
projects/sprint-5/eval_harness.pyand find theEvalCasedataclass. Each case carries: aninput_textstring, anexpected_toolname, anexpected_answer_token, and optionally aforce_tooloverride. Identify the three scorer functions (score_tool_call,score_args, andscore_task_completion) and confirm that none calls a model or makes a network request. This is the deterministic-scorer pattern. -
Add a fifth eval case. Choose an input that exercises one of the agent's tools in a new way. Add it as a new
EvalCaseentry in theCASESlist insideeval_harness.py, run the harness, and confirm the new case appears in the report. Make sure it passes — do not add a second deliberate fail. - Identify the model-as-a-judge boundary. Read the README's "Where model-as-a-judge would be needed" note. Then look at the fifth case you just added: decide whether its task-completion check is reliable with a deterministic token match, or whether a real version of that task would need a model-as-a-judge instead. Record your reasoning in a one- or two-sentence comment in the harness code directly above your new case. Do not delete the README's existing explanation — keep it; your new case plus its reasoning comment are the deliverable.
- Trace a regression scenario. Temporarily change the agent stub so it always calls the wrong tool, run the harness, and observe which cases now fail. Then revert the change and confirm the harness returns to its original pass count. This is the regression-detection loop the harness is designed to support — catch the failure with the harness before it reaches production.
You have run the eval harness and seen a real failing case in the report. You can identify the three scorer functions and explain why they are deterministic. You have added a fifth case and confirmed the harness reports it. You can point to where a deterministic token match stops being sufficient and a model-as-a-judge would be needed, and you have recorded that reasoning as a comment above your new case. You have traced a regression scenario by breaking and fixing the agent stub. That is the Sprint 5 ship condition.