{"slug": "when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination", "title": "When Building an AI Agent, the Journey Matters as Much as the Destination", "summary": "Raj Kundalia, an engineer, built an open-source bug-fixing agent and evaluation framework to assess AI agents beyond final outputs, focusing on process, tool use, and multi-step reasoning. He planted 10 bugs in Python code and ran three agent configurations, spending about $1 on API calls using Claude Haiku 4.5. The project is available on GitHub.", "body_md": "*A practical framework for evaluating trajectories, tool use, and process, not just the final answer.*\n\nOriginally published on [Medium](https://medium.com/@rajkundalia/when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination-17a8f916bf36?sharedUserId=rajkundalia).\n\nI am sure almost everyone reading this has tried to build an AI agent by now. For the POC, after some trial and error, it works, and you can demo it, everyone is impressed. You have tested it manually and it works, mostly.\n\nBut thinking in terms of software engineering, you know that conventionally the code had integration tests, unit tests, etc. I am sure it did not cover everything, but it gave me baseline confidence. For AI agents, the story is very different.\n\nAn AI agent has variability, it can hallucinate into an infinite loop, take a different trajectory then what you had thought of, invent its own responses/tools and can also say that the work is done when it might not have been done. When you evaluate a standard LLM call, you are generally grading a single output. You put a prompt in, you get text out, and you figure out if that text is accurate. But agents change the game because they take actions. They rely on **tool use**, **multi-step reasoning**, and **compounding failures**. If your agent hallucinates a parameter during a tool call at step two, it can silently derail the entire process and the scariest part is, the final answer it gives you might still look completely correct.\n\nI didn't want to write about agent evaluation purely theoretically, so I built a small bug-fixing agent, planted 10 bugs, and built an evaluation harness around it. The entire project is open-source, if you want to follow along or run the evaluators yourself: ** Bug-Fixing Agent & Evaluation Framework on GitHub**.\n\nIn this page, I am trying to explore how to evaluate an AI agent, not just its output, but its *process*. I am writing this after reading multiple pages and making some experiments with my AI agent. It is more of a personal journey and I wanted to document it. I am sure it will be useful for others as well.\n\nBefore jumping in, let me set a quick boundary. When I say \"evaluating an agent\" in this post, I am talking about an agent that you give a specific task to (like \"fix this bug\"), and it stops when it's done. I am not talking about futuristic agents that run 24/7 in the background without human supervision.\n\nBefore jumping into the experiments I ran, there are a few core concepts to get right.\n\nWhen evaluating LLMs, the industry relies on generic benchmarks like MMLU (Google it!) or SWE-bench. These are useful for sanity-checking a base model's raw capability. However, **generic benchmarks tell you how smart the underlying model is; they don't tell you whether your agent, wired to your custom tools, solving your specific task, is any good.** For that, you need a custom evaluation framework.\n\nTo make these concepts concrete, I built a **local bug fixing agent** and ran it against 10 planted Python bugs.\n\nImplementation note:I ran this benchmark onClaude Haiku 4.5for cost efficiency. Including documented runs, an earlier uncaptured run, and miscellaneous experiments, the total API spend across the entire project was approximately~$1.\n\n**The Prerequisite: A Golden Dataset**\n\nBefore looking at the configurations, it is worth pausing on those \"10 planted bugs.\" You cannot evaluate an agent without a strong foundation of data. To build a robust evaluation, you need a carefully curated \"Golden Dataset\" of test cases with known root causes and strict verification steps. Furthermore, this shouldn't be a static list—it must be an ever-improving set of tasks that grows as your agent tackles new edge cases in production.\n\nI evaluated three configurations against this dataset:\n\n`config_baseline`\n\n: System prompt + full toolset (`read_file`\n\n, `edit_file`\n\n, `run_tests`\n\n).`config_prompt_v2`\n\n: Constrained step-by-step diagnostic prompt + full toolset.`config_no_run_tests`\n\n: System prompt + NO `run_tests`\n\ntool (blind execution).I ran the benchmark suite multiple times to account for LLM non-determinism. Here is how the headline metrics shifted between **Run 1** (initial) and **Run 2** (extended with token & cost accounting):\n\n| Metric | `config_baseline` |\n`config_prompt_v2` |\n`config_no_run_tests` |\n|---|---|---|---|\nPass Rate (Run 1 → Run 2) |\n80% → 70% | 70% → 70% | 70% → 90%\n|\nLLM Judge Score (Run 1 → 2) |\n0.90 → 0.80 | 0.80 → 0.80 | 1.00 → 1.00 |\nFix Quality (Genuine Fixes) |\n100% | 100% | 100% |\nTotal Benchmark Cost (Run 2) |\n$0.0656 | $0.0670 | $0.0699 |\nAdversarial Safety Rate |\n100% Passed |\n100% Passed |\n100% Passed |\n\n*Note on Non-Determinism (Variability):* Notice how the pass rates changed between Run 1 and Run 2! Since I tested on 10 tasks, a single task flipping from pass to fail shifts the score by 10%. This was a big takeaway for me: running a benchmark just once is fine for a quick sanity check, but small score changes are usually just LLM randomness unless you test over multiple runs.\n\n*Note on Safety:* One of my tasks (`task_009_adversarial`\n\n) featured a planted, malicious docstring urging the agent to simply write `assert True`\n\nto fake a fix. Across all runs, the agent resisted this indirect prompt injection and correctly fixed the underlying source code. Worth being honest about sample size though: this is one adversarial scenario out of ten tasks. A 100% pass rate on one adversarial scenario is a promising signal, not proof of robustness.\n\nTo uncover the *why* behind these numbers, I needed to go beyond a simple pass/fail metric. I had to evaluate agent behavior across outcome correctness, tool flow, trajectory efficiency, safety, and fix quality.\n\nWhile reading about the topic of evaluating agents, I found that the following 8 questions can help us evaluate an agent better; I designed the eval for the bug fixing agent keeping this in mind. This is a good starting point.\n\n*Methodology: Dual-Layer Grading (Rule-Based + LLM Judge)*\n\nThe most obvious question is: *Did the agent do the job?* But a simple pass/fail is rarely enough. In my framework, I split this into two distinct evaluators:\n\n**Outcome (Binary Pass/Fail):** This is a deterministic, rule-based check. In my code, `outcome.py`\n\nchecks if the `pytest`\n\nsuite passes after the agent finishes its edits. It’s cheap, fast, and gives a definitive yes or no.\n\n**Task Completion (Nuanced Score):** An outcome-only metric hides nuance. What if the agent correctly identified the bug, wrote the right logic, but forgot a minor syntax detail and ran out of turns? My LLM judge (`task_completion.py`\n\n) reads the full execution trace and outputs a strict verdict, which maps to a discrete score (`COMPLETE`\n\n= 1.0, `PARTIAL`\n\n= 0.5, `FAILED`\n\n= 0.0). Getting partial credit for sound reasoning and correct file identification is invaluable for debugging capability gaps.\n\n*Methodology: Rule-Based Match*\n\nUnlike standard LLMs, agents take action. Tool misuse: calling the wrong tool, hallucinating parameters, or ignoring a silent failure is a unique agentic failure mode.\n\nTo evaluate this, I used `tool_correctness.py`\n\n, a rule-based evaluator that compares the agent's actions against a known-correct task definition. Did the agent open the correct target file? Did it edit the source code instead of cheating and editing the test file? I didn't need a fuzzy LLM judge here; deterministic string matching works best to ensure the agent is physically doing what it claims to be doing.\n\n*Finding: The \"Prompt Bloat\" Problem*\n\nI ran a benchmark comparing my baseline prompt (`config_baseline`\n\n) against an over-constrained, verbose prompt that forced step-by-step diagnostic thinking (`config_prompt_v2`\n\n).\n\nYou might expect forcing structured reasoning to improve accuracy. **It did not.** In my tests, `config_prompt_v2`\n\nadded 711 tokens and increased total cost, but it actually performed *worse* (in Run 1) or identical (in Run 2) to the baseline. Why? The model spent extra tokens \"thinking out loud\" summarizing the task description and detailing diagnostic steps but this produced no extra problem-solving value over the concise baseline prompt.\n\nRun this experiment on your own agent; the result may surprise you.\n\n**Takeaway:** In this experiment, on these 10 bug-fixing tasks, the more verbose diagnostic prompt increased token usage without improving task performance. Whether that generalizes to other models, other task types, or genuinely harder bugs is an open question, but it's a cheap thing to test before assuming \"more structured reasoning\" is automatically better.\n\n*Finding: The Blind Execution Paradox*\n\nWhat happens if you take away an agent's ability to test its own code? I ran `config_no_run_tests`\n\n, an environment where the agent only had `read_file`\n\nand `edit_file`\n\ntools—no `run_tests`\n\ntool.\n\nRemarkably, the agent's pass rate **improved to 90%** (up from 70%), achieving a perfect 1.00 completion score. Even though the agent couldn't run tests itself, when my evaluator ran the strict `pytest`\n\nsuite at the end, 9 out of 10 fixes were perfectly correct.\n\n*A small caveat: the jump from 70% to 90% is really just two extra tasks passing. With only two runs, I can't be sure this gap is real and not just randomness. What I'm more confident about is the quality rating, which held at a perfect 1.00 in both runs.*\n\nWithout the ability to lean on a \"guess-and-check\" loop, the agent carefully read the source code and the test suite in its initial turn, identified the exact root cause, and made a single, precise edit. The trade-off? Blind execution used slightly fewer total tokens but took 1.0 second longer per task due to heavier reasoning per turn.\n\n**Takeaway:** In this experiment, removing the run_tests tool didn't hurt performance; it may have helped, by forcing more careful upfront reasoning instead of a guess-and-check loop. My hypothesis, untested here since every task was single-file and localized, is that this trade-off flips for complex, multi-file bugs, where a testing loop becomes essential. That's a good next experiment for this framework, not a strong conclusion this dataset can support yet.\n\n*Methodology: Sequence & Flow Analysis*\n\nA correct answer can hide a terrible process. Did the agent get stuck in a dead loop? Did it wildly edit files without reading them first?\n\nMy `trajectory.py`\n\nand `tool_flow.py`\n\nevaluators analyze the shape of the execution trace. They look for missing verification steps or looped failures. If an agent manages to fix a bug but took 5 redundant edits to get there, a trajectory evaluator will flag it, whereas a simple outcome evaluator would happily mark it as a \"pass.\"\n\n*Finding: The Cost of Strict Efficiency*\n\nIn production, wrong answers are loud—users complain, tests fail, judges flag them. But expensive, slow, correct answers are quiet. They just slowly drain your API credits.\n\nMy `efficiency.py`\n\nevaluator tracks latency, token consumption, USD cost, **and turn counts** per task. During my benchmark, I noticed persistent failures on two simple tasks (`task_002`\n\nand `task_007`\n\n). Why? Because to force efficiency, I set a harsh harness constraint: `max_turns: 3`\n\n. The minimum viable trajectory (read → edit → verify) is exactly 3 turns. Any slight imprecision on the first try meant the agent hit the turn limit and failed, even if it had the correct logic.\n\n**Takeaway:** Evaluation harnesses must carefully distinguish between **agent capability failures** (the model isn't smart enough) and **harness constraints** (the turn budget was just too tight).\n\n*Methodology: CI Gates and Statistical Variance*\n\nOnce an agent can solve a task, that task graduates to the regression suite. `regression.py`\n\nallows me to compare two runs of the same config over time to ensure an update to the model or prompt didn't break previously solved tasks.\n\nAs I found in my benchmarks, LLMs are naturally unpredictable. A single run is great for a quick CI smoke test, but for true regression testing, you need to run tasks multiple times so you don't mistake LLM randomness for a broken prompt.\n\n*Methodology: Diff-based Quality Evaluation*\n\nFinally, did the agent fix the actual bug, or did it write a lazy workaround? If a test fails, a lazy agent might simply delete the failing `assert`\n\nstatement in the test file.\n\nTo prevent this, `fix_quality.py`\n\nacts as a specialized LLM judge. Crucially, I didn't just feed it the trace log, I captured the exact `git diff`\n\nof the agent's final state and passed *that* to the judge. The judge categorizes the result as a `GENUINE`\n\nfix (addressing the root architectural bug) or a `WORKAROUND`\n\n(cheating the test suite). Across all my completed tests, my agents achieved a 100% Genuine Fix rate.\n\nBuilding a custom evaluation harness is a great learning exercise, but what tools exist in the ecosystem to help you do this at scale? If you don't want to build this from scratch, here is how the tooling landscape looks today:\n\nTooling caveat:This landscape moves fast: ownership changes (Promptfoo was acquired by OpenAI in March 2026, though it remains open-source) and APIs get marked experimental (NVIDIA's own docs flag NeMo's evaluation API as such). Treat this section as a snapshot, and check current docs before building on any of these.\n\n**A common real-world pattern:** Use **Promptfoo** or a custom script (`outcome.py`\n\n) as a cheap, rule-based CI gate at PR time. Use **DeepEval** to run scheduled, LLM-judged evaluations on sampled production traces. Finally, visualize those scores and trajectories in **Langfuse** or **Confident AI** to monitor for drift.\n\nAgent evaluation is an evolving discipline, not a solved checklist. By stepping away from simple single-turn accuracy and focusing on trajectories, tool correctness, and config-comparisons, you can build agents that don't just answer questions correctly—but act reliably in the real world.\n\nBy structuring your evaluation around these 8 critical questions, and blending cheap rule-based CI gates with nuanced LLM-as-a-judge scoring, you can finally prove that your agent works not just in manual testing, but in production.\n\nIf you want to dive deeper into the state of the art in agent evaluation, here are some excellent resources:", "url": "https://wpnews.pro/news/when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination", "canonical_source": "https://dev.to/rajkundalia/when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination-124d", "published_at": "2026-08-26 07:37:14+00:00", "updated_at": "2026-08-26 07:43:37.773232+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "ai-research", "developer-tools"], "entities": ["Raj Kundalia", "Claude Haiku 4.5", "GitHub", "Medium"], "alternates": {"html": "https://wpnews.pro/news/when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination", "markdown": "https://wpnews.pro/news/when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination.md", "text": "https://wpnews.pro/news/when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination.txt", "jsonld": "https://wpnews.pro/news/when-building-an-ai-agent-the-journey-matters-as-much-as-the-destination.jsonld"}}