AIdeazz Blog About Portfolio

131 Tests, 4 Layers, $00.03/Run: My AI Agent Eval Harness

· by

I shipped a silent failure. It wasn't a crash, a 500 error, or a memory leak. It was a perfectly executing agent that delivered the wrong answer, confidently, to a customer. The kind of wrong answer that costs money and trust. This happened before I built my AI agent evaluation harness. Now, 131 tests across four distinct layers run for $0.03 per full suite execution, catching these failures before they ever see production.

My multi-agent systems, running on Oracle Cloud Infrastructure (OCI) and routing between Groq and Claude, handle critical production tasks for clients via Telegram and WhatsApp. A single agent might involve a planning LLM, a tool-calling LLM, a retrieval system, and a final synthesis step. Unit tests catch code bugs. They do not catch an LLM hallucinating a non-existent product ID, a tool misinterpreting a nuanced user query, or a retrieval system pulling irrelevant context that poisons the final answer. These are emergent failures, unique to AI agents, and they demand a different testing approach.

The Silent Failure: A $250,000 Quote Mistake

My first production agent was designed to generate quotes for custom manufacturing orders. It pulled product data from a PostgreSQL database, applied pricing rules, and formatted the output. My unit tests covered the database queries, the pricing logic, and the output formatting. The agent passed every single one.

A customer asked for a quote on "200 units of the 'Alpha-Pro' component, with the 'Titanium' finish option." The agent returned a quote for "200 units of the 'Alpha-Pro' component, with the 'Standard' finish." The price difference was 15%. The customer, seeing a lower price, approved the quote. We only caught it during the final production review, after materials were ordered. The cost of rework and lost margin was estimated at $250,000 for that single order.

The root cause: the planning LLM (then GPT-4, now Claude 3 Opus for complex planning) misinterpreted "Titanium" as a descriptive adjective rather than a specific finish option ID. It then passed "Standard" to the tool responsible for fetching finish options, which returned a valid ID. The tool executed correctly. The database query executed correctly. The pricing logic executed correctly. The agent failed. This is the kind of failure an eval harness is built to prevent.

Layer 1: Atomic Tool Tests (38 Tests, $0.0001/run)

Before an agent can fail, its tools must work. My tools are Python functions wrapped with Pydantic schemas for input/output validation. An "atomic tool test" verifies that a tool, given a specific, valid input, produces the expected output. This is similar to a traditional unit test, but with a focus on the semantic correctness of the tool's interaction with external systems or complex logic.

Example: get_product_details(product_id: str)
Test case: get_product_details("PROD-001") should return {"name": "Alpha-Pro", "base_price": 120.00}.
Another test case: get_product_details("NON-EXISTENT-ID") should raise a ProductNotFoundError.

These tests run locally, using mocked external services where necessary, and are part of my standard CI/CD pipeline. They are fast and cheap. The 38 tests here ensure the foundational building blocks are solid. Cost is negligible, often less than $0.0001 per run as they don't hit LLMs.

Layer 2: LLM-Tool Interaction Tests (45 Tests, $0.005/run)

This layer is where the LLM's ability to correctly invoke tools is tested. Given a user query, does the LLM call the right tool with the right arguments? This is crucial for agents that rely on function calling.

My harness feeds a user query to the LLM and captures the tool call (function name and arguments). It then asserts against an expected tool call.

Example: User query: "What's the price of the Alpha-Pro component?"
Expected tool call: get_product_details(product_id="PROD-001")

This layer catches issues like:

I use a small, dedicated dataset of user queries and their expected tool calls. Each test involves one LLM call. With Groq's Llama 3 8B, these are incredibly fast and cheap. A full run of 45 tests costs about $0.005. I run these daily.

Layer 3: End-to-End Agent Trajectory Tests (30 Tests, $0.02/run)

This is where the entire agent chain is tested, from initial user query to final output. These tests verify the sequence of tool calls, the intermediate reasoning steps (if exposed), and the final response's correctness and adherence to instructions.

Each test case consists of:
1. User Query: The input to the agent.
2. Expected Final Output: The desired response from the agent. This is often a regex pattern or a semantic similarity check, not an exact string match.
3. Expected Intermediate Steps (Optional): For complex agents, I might assert that specific tools were called in a certain order, or that certain internal states were reached.

Example: User query: "Generate a quote for 100 units of Alpha-Pro with Titanium finish."
Expected final output: A quote containing "Alpha-Pro", "Titanium", and a specific price range.

These tests are more expensive because they involve multiple LLM calls and tool executions. I use a mix of Groq for speed-sensitive agents and Claude 3 Haiku/Sonnet for more complex reasoning. A full run of 30 tests costs around $0.02. I run these before every major deployment and weekly for regression.

Layer 4: Adversarial & Edge Case Tests (18 Tests, $0.005/run)

This layer focuses on robustness. How does the agent handle ambiguous queries, out-of-scope requests, or malicious inputs?

Examples:

These tests often don't have a single "correct" answer but rather an expected behavior (e.g., "should not generate a quote," "should ask for more information," "should refuse to answer"). I use a combination of regex matching and a small, dedicated LLM (Groq's Llama 3 8B) to evaluate the agent's response for adherence to safety and scope guidelines.

These 18 tests are crucial for production readiness and cost about $0.005 per run, as they often involve a single LLM call for the agent and another for evaluation. I run these weekly.

The Cost and Infrastructure

My entire eval harness is built in Python, using pytest for test orchestration. The test data is stored in YAML files for easy readability and version control.

* Layer 1: $0.0001 * Layer 2: $0.005 * Layer 3: $0.02 * Layer 4: $0.005 * Total: ~$0.03

This $0.03 cost is for a full, end-to-end regression suite. It's a trivial expense compared to the $250,000 mistake I made. The harness is integrated into my CI/CD, running automatically on every push to main and nightly. This proactive testing is non-negotiable for shipping reliable AI agents.

Frequently Asked Questions

Q: How do you handle non-deterministic LLM outputs in your tests?
A: For Layer 2 (LLM-Tool Interaction), I assert against the structure and semantic intent of the tool call, not exact string matches. For Layer 3 (End-to-End), I use regex patterns or semantic similarity checks (e.g., cosine similarity of embeddings) against a range of acceptable outputs, often with a small, dedicated LLM to evaluate the response.

Q: What if the LLM provider changes its model behavior?
A: This is a constant risk. My eval harness acts as a regression safety net. A significant change in model behavior will cause tests in Layers 2, 3, or 4 to fail, alerting me immediately. This allows me to either re-tune the agent or adjust the expected test outputs.

Q: How do you keep your test data up-to-date with evolving agent capabilities?
A: Test data is version-controlled alongside the agent code. When new features are added or agent behavior is intentionally modified, I update the relevant test cases. For complex agents, I use a "golden dataset" approach where successful production interactions are periodically anonymized and added to the test suite.

Q: Why not just use an existing evaluation framework?
A: Many existing frameworks are either too generic (focused on LLM benchmarks) or too opinionated for my specific multi-agent, multi-LLM, multi-tool architecture. Building a custom harness allowed me to precisely target the failure modes unique to my production agents and integrate seamlessly with my existing CI/CD and Oracle Cloud infrastructure. The cost control was also a major factor.

Q: How do you manage the cost of LLM calls for testing?
A: I strategically route tests to the cheapest LLM capable of performing the required task. Groq's Llama 3 8B is used for most tool-calling and simple reasoning tests. More complex planning and synthesis tests might use Claude 3 Haiku/Sonnet. I also cache LLM responses for identical prompts during local development runs to minimize API calls.

— Elena Revicheva · AIdeazz · Portfolio