AIdeazz Blog About Portfolio

131 Tests, 4 Layers, $00.03/Run: Why I Built My AI Agent Eval Harness First

· by

I shipped a silent failure. My first production AI agent, a simple lead qualification bot for WhatsApp, passed all 23 unit tests. It handled happy paths, edge cases, and even some adversarial inputs. Then, in its first hour live, it hallucinated a product feature that didn't exist, quoted a price 3x too low, and then, when challenged, apologized and doubled down on the incorrect information. The user, understandably, churned. That single interaction cost me a potential client and taught me a brutal lesson: unit tests are insufficient for AI agents.

This failure led directly to my current AI agent evaluation harness, which now runs 131 tests across four distinct layers for every significant code change. Each full evaluation costs me $0.03 on Oracle Cloud Infrastructure (OCI) and takes 11 minutes. This harness is now the first thing I build for any new agent system, before writing a single line of feature code. It catches failures that traditional testing simply cannot, preventing silent, costly production errors.

The Fundamental Flaw of Unit Tests for AI Agents

Unit tests verify deterministic code. add(2, 2) must always return 4. An AI agent, by design, is non-deterministic. Its output depends on the LLM, the prompt, the tools, the context window, and even the specific token sampling. Testing an agent with assert_equals(agent.process("hello"), "Hello there!") is a fool's errand. The agent might respond "Greetings!", "Hi!", or even "How can I assist you today?". All could be valid, yet none would pass a strict equality check.

The real problem isn't just varied output; it's semantic correctness and goal adherence. Did the agent achieve its objective? Did it use the correct tool? Did it extract the right information? Did it avoid hallucinating? These are complex, multi-faceted questions that require an AI to evaluate another AI, or at least a sophisticated, multi-step validation process. My lead qualification bot's unit tests confirmed it could call the product catalog tool. They didn't confirm it would correctly interpret the tool's output and accurately convey that information to the user.

My 4-Layer Evaluation Harness

My 131 tests are organized into four distinct layers, each targeting a different aspect of agent behavior. This layered approach allows me to pinpoint failures quickly and understand their root cause, whether it's a prompt issue, a tool malfunction, or an LLM regression.

Layer 1: Tool Functionality (38 Tests)

This layer focuses on the agent's ability to correctly use its tools. These are essentially integration tests for the tools themselves, but run within the agent's execution context. For example, if an agent has a search_product_catalog tool, I test:

Edge cases: search_product_catalog("") or search_product_catalog("a" 200) should handle input constraints gracefully.

These tests are mostly deterministic and use assert_contains or assert_not_contains on the tool's raw output. They ensure the agent can access and process information correctly, regardless of the LLM's interpretation.

Layer 2: Prompt Adherence & Guardrails (47 Tests)

This layer is where the non-deterministic nature of AI agents truly emerges. Here, I evaluate if the agent follows its instructions and respects guardrails, even under pressure. I use a separate, smaller LLM (usually a fine-tuned Llama 3 8B on OCI, or sometimes Groq's Mixtral for speed) as an evaluator.

Examples:

The evaluator LLM is prompted with the agent's output and a specific instruction, e.g., "Does the following text contain any mention of political topics? Respond 'YES' or 'NO'." This setup is surprisingly robust and much faster than human evaluation.

Layer 3: Goal-Oriented Behavior (32 Tests)

This is the most critical layer for production agents. It verifies if the agent achieves its intended purpose, often requiring multiple turns or tool calls. This is where the silent failure of my lead qualification bot would have been caught.

Examples for a lead qualification agent:

These tests often involve multi-turn conversations simulated by the harness, with the evaluator LLM assessing the overall conversation flow and final outcome. I use Claude 3 Haiku for this layer due to its strong reasoning and cost-effectiveness for complex evaluations.

Layer 4: Performance & Latency (14 Tests)

While not directly about correctness, performance is crucial for user experience. This layer measures:

These tests are run periodically and trigger alerts if metrics deviate significantly from baselines. My agents often route between Groq for speed-critical tasks (e.g., initial greeting, simple Q&A) and Claude 3 Opus for complex reasoning. This layer helps validate the routing logic is performing as expected. I use OCI's native monitoring for this, pushing custom metrics from the harness.

The Cost and Infrastructure

Running 131 tests, often involving multiple LLM calls per test, could quickly become expensive. My current setup costs $0.03 per full run. This is achieved through several optimizations:

1. Strategic LLM Choice: I don't use the most expensive LLM for every evaluation.
* Layer 1 (Tool Functionality): No LLM needed, just Python assertions.
* Layer 2 (Prompt Adherence): Fine-tuned Llama 3 8B on OCI or Groq Mixtral for speed. Cost: ~$0.0001 per evaluation.
* Layer 3 (Goal-Oriented Behavior): Claude 3 Haiku. Cost: ~$0.0005 per evaluation.
* Layer 4 (Performance): No LLM needed, just timing.
2. Parallel Execution: Tests within each layer are run in parallel using Python's multiprocessing module on an OCI VM.
3. Caching: For deterministic tool calls, I cache responses to avoid redundant external API calls during repeated test runs.
4. Targeted Runs: For minor changes, I can run only specific layers or subsets of tests, reducing cost and time.

The entire harness runs on a single OCI VM (VM.Standard.E4.Flex, 2 OCPUs, 32GB RAM) provisioned via Terraform. The test data (prompts, expected outputs, simulated conversations) is stored in a Git repository alongside the agent code. This ensures version control and reproducibility.

What AI Agent Tests Catch That Unit Tests Cannot

The silent failure I would have shipped without this AI agent evaluation harness 131 tests production was a subtle misinterpretation of tool output. My unit tests confirmed the search_product_catalog tool returned a list of products. They did not confirm the LLM would correctly extract the price from that list and present it accurately to the user.

My eval harness, specifically Layer 3 (Goal-Oriented Behavior), now includes a test case: "User asks for price of Product X." The evaluator LLM then checks if the agent's response contains the exact price from the mocked tool output. If the agent hallucinates a price or misreads the tool's response, this test fails. This is a semantic failure, not a code bug. The code executed perfectly, but the meaning was lost or corrupted by the LLM.

Another example: a unit test might confirm a send_email tool is called. An AI agent test would confirm the content of the email is appropriate, addresses the user's query, and adheres to brand guidelines. This requires an LLM to assess the generated email text, a task impossible for a regex or string comparison.

Conclusion

Building an AI agent evaluation harness before writing features felt counter-intuitive at first. It added upfront complexity and delayed initial "progress." However, it has proven to be the single most impactful decision in my AI agent development workflow. It catches subtle, semantic failures that traditional testing misses, preventing costly production errors and safeguarding user trust. For anyone building production AI agents, especially multi-agent systems, a robust, layered evaluation harness is not optional; it's foundational.

Frequently Asked Questions

Q: How do you prevent the evaluator LLM from hallucinating or being biased?
A: I use a highly constrained prompt for the evaluator, forcing a "YES/NO" or specific keyword response. For more complex evaluations, I use few-shot examples of correct and incorrect agent behavior to guide the evaluator. Regular manual spot-checks of evaluator decisions are also crucial.

Q: What if the LLM provider changes their model, causing tests to fail unexpectedly?
A: This is a common issue. I pin specific model versions (e.g., claude-3-haiku-20240307). When a new model version is released, I run the full eval harness against it in a staging environment and manually review failures before updating the production model. This is a form of regression testing for the LLM itself.

Q: How do you handle non-deterministic failures in the eval harness?
A: For tests in Layers 2 and 3, I allow for a small tolerance (e.g., 95% pass rate). If a test fails intermittently, I investigate if it's a genuine agent issue or an evaluator sensitivity. I also re-run failed tests a few times to confirm consistency before marking a failure.

Q: Is $0.03 per run sustainable for frequent testing?
A: Yes. For my current scale, running 10-20 full evaluations per day, it's $0.30-$0.60 daily. This is a negligible cost compared to the potential revenue loss from a single production failure. For larger teams, this cost scales linearly but remains manageable.

Q: How do you manage the test data (prompts, expected outputs) for 131 tests?
A: All test data is stored in YAML files, organized by layer and test case. Each YAML file defines the user input, expected agent behavior (e.g., tool calls, specific phrases), and the evaluation criteria for the LLM evaluator. This keeps it version-controlled and human-readable.

— Elena Revicheva · AIdeazz · Portfolio