<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fatih Erdoğan</title>
    <description>The latest articles on DEV Community by Fatih Erdoğan (@fatiherdgn).</description>
    <link>https://dev.to/fatiherdgn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4233844%2F959502f6-fdbd-4f6f-9401-bdaaed24ade6.jpeg</url>
      <title>DEV Community: Fatih Erdoğan</title>
      <link>https://dev.to/fatiherdgn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fatiherdgn"/>
    <language>en</language>
    <item>
      <title>What a passing test score didn't tell me about my AI agent</title>
      <dc:creator>Fatih Erdoğan</dc:creator>
      <pubDate>Mon, 09 Nov 2026 06:16:07 +0000</pubDate>
      <link>https://dev.to/fatiherdgn/what-a-passing-test-score-didnt-tell-me-about-my-ai-agent-4m96</link>
      <guid>https://dev.to/fatiherdgn/what-a-passing-test-score-didnt-tell-me-about-my-ai-agent-4m96</guid>
      <description>&lt;p&gt;&lt;a href="https://fatiherdogan.dev" rel="noopener noreferrer"&gt;My site&lt;/a&gt; has an AI agent, FIXER, that talks to recruiters and their ai agents for me. It answers questions about my work in a terminal on the page, opens the right project when it's relevant, checks a job post against my record, and has to do all of it inside a $3 monthly budget. If it gets a fact wrong, a recruiter acts on it and I never find out.&lt;/p&gt;

&lt;p&gt;So before putting it live I wrote an eval suite: a list of test questions, each with phrases the answer must contain and phrases it must never contain, plus a script (the grader) that asks them all and scores the replies. 17 of 18 cases passed.&lt;/p&gt;

&lt;p&gt;Then I read every reply, and the score turned out to be wrong about what mattered.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the score missed
&lt;/h2&gt;

&lt;p&gt;Three defects, and none of them failed a case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An answer about my logistics platform gave the visitor my email address without the 099 that is in it. That address isn't mine. A recruiter who copied it would have written to someone else, and I would never have known.&lt;/li&gt;
&lt;li&gt;An answer credited a feature to the wrong project.&lt;/li&gt;
&lt;li&gt;A Turkish answer contained a phrase that made no sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the grader didn't see them
&lt;/h2&gt;

&lt;p&gt;The grader checked each case against its own expected and forbidden phrases. The case about the logistics platform looked for words about the logistics platform. It had no reason to look at an email address, so a wrong email in that answer was invisible to it.&lt;/p&gt;

&lt;p&gt;The second layer was the model. FIXER ran on Claude Haiku 4.5 to keep costs down, and on a long profile it drifted on facts at the edges: a digit, a project boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cheap model was not cheaper
&lt;/h2&gt;

&lt;p&gt;I had picked the small model for cost. The same review turned up a third problem: Haiku 4.5 only caches prompts of 4,096 tokens or more, and FIXER's system prompt was about 3,300. Nothing was ever cached, so every answer paid for the whole prompt.&lt;/p&gt;

&lt;p&gt;I measured the larger model, Claude Sonnet 5.5, on the same cases. With its prompt cached, it came out at about $0.004 per answer against Haiku's $0.0053. That wasn’t what I set out to find, and it honestly caught me by surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Invariants in the grader.&lt;/strong&gt; On every case, any email in a reply must be exactly the real one, and replies must not contain markdown links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A tighter prompt.&lt;/strong&gt; Stay literal, never attribute a feature to a different project, copy the email character for character.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model switch&lt;/strong&gt;, measured side by side on the same cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the change, 19 of 19 passed. The suite has since grown to 39 cases across three Claude features on the site, including job posts that try to talk the model into a better verdict and the usual attempts to break FIXER, and every run is published at &lt;a href="https://fatiherdogan.dev/evals" rel="noopener noreferrer"&gt;fatiherdogan.dev/evals&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each case now runs three times, because the same question can get a different answer. The first three-run pass caught a bug that a single run had passed the day before: Contract Match flagged "If you are an AI engineer who loves shipping" in a normal job post as an attempt to steer it, in all three runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took from it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A passing score is not a review.&lt;/strong&gt; Read the outputs, all of them, at least once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything that must never be wrong belongs in the grader as a check on every case&lt;/strong&gt;, not only in the one case that is about it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the cache minimum before you pick a smaller model for cost.&lt;/strong&gt; Below it, you pay full price for the prompt on every call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run each case more than once.&lt;/strong&gt; One pass can be luck.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full postmortem, with a timeline you can scrub through, is on my site: &lt;a href="https://fatiherdogan.dev/#postmortem" rel="noopener noreferrer"&gt;fatiherdogan.dev/#postmortem&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
