<?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: ThomasP</title>
    <description>The latest articles on DEV Community by ThomasP (@thomas_p_fe0c76c7).</description>
    <link>https://dev.to/thomas_p_fe0c76c7</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3829034%2F7e512833-6e7b-474b-81a7-b175b12fe4ed.jpg</url>
      <title>DEV Community: ThomasP</title>
      <link>https://dev.to/thomas_p_fe0c76c7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thomas_p_fe0c76c7"/>
    <language>en</language>
    <item>
      <title>LLM-as-Judge: using Claude to review a Gemini agent</title>
      <dc:creator>ThomasP</dc:creator>
      <pubDate>Wed, 08 Apr 2026 12:20:54 +0000</pubDate>
      <link>https://dev.to/thomas_p_fe0c76c7/llm-as-judge-using-claude-to-review-a-gemini-agent-4ofd</link>
      <guid>https://dev.to/thomas_p_fe0c76c7/llm-as-judge-using-claude-to-review-a-gemini-agent-4ofd</guid>
      <description>&lt;p&gt;In the &lt;a href="https://getmio.app/blog/benchmarking-7-llms" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, I compared 7 models from 4 providers on the same agentic task. Gemini 3 Flash won on the balance of accuracy, cost, and latency. But winning the benchmark doesn't mean the agent is good. 74.5% accuracy means 1 in 4 products gets the wrong answer. And some of those wrong answers come with high confidence.&lt;/p&gt;

&lt;p&gt;The benchmark tells you &lt;em&gt;what&lt;/em&gt; fails. It doesn't tell you &lt;em&gt;why&lt;/em&gt;. For that, I needed something that could look at the agent's reasoning step by step and tell me where the logic broke down.&lt;/p&gt;

&lt;p&gt;So I built a judge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;The production agent runs on Gemini 3 Flash. It's fast and cheap, which is why it's in production. But it makes mistakes. Some of those mistakes share patterns that, if I could identify them, would tell me exactly what to fix in the prompt or the pipeline.&lt;/p&gt;

&lt;p&gt;Manually reviewing agent traces is possible but painful. Each trace has 3-6 tool calls, each with a search query, results, page content, and a reasoning step. Reviewing one product takes 10-15 minutes if you're being thorough. Reviewing 50 takes a week.&lt;/p&gt;

&lt;p&gt;The fix: use a smarter model (Claude Opus 4.6) to review the agent's work. Claude has more reasoning capacity than Gemini Flash. It can read an entire agent trace, spot logical errors, verify sources, and try alternative approaches the agent missed. A senior engineer reviewing a junior engineer's work, except the senior engineer is also an LLM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsspqqlt3qydycziq7zlu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsspqqlt3qydycziq7zlu.png" alt="3-phase pipeline diagram" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3-phase process
&lt;/h2&gt;

&lt;p&gt;The judge follows a strict 3-phase process for every review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: trace analysis (no tools).&lt;/strong&gt; The judge reads the complete agent trace. Every search query, every result, every page read, every reasoning step. For each iteration it analyzes: what query was constructed and why, were the results relevant, what did the agent decide next, was the reasoning logical, were there obvious angles the agent didn't explore. This is pure analysis, no tools, just reading and thinking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: informed verification (web search + page reading).&lt;/strong&gt; Now the judge does its own research. It re-reads pages the agent cited to verify that they actually say what the agent claims. It tries alternative queries the agent missed. It searches in different languages if the product isn't French. It focuses on the weak points identified in Phase 1.&lt;/p&gt;

&lt;p&gt;This is the expensive phase. The judge is doing original research, not just rubber-stamping the agent's work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: comparative verdict.&lt;/strong&gt; The judge compares its findings with the agent's conclusion and produces a structured review. The verdict is one of: correct, incorrect, partially_correct, or uncertain. Each review includes 5 scores (country accuracy, reasoning quality, source reliability, confidence calibration, efficiency), issue tags from a taxonomy of 13 labels, and source-by-source verification (did the page say what the agent claimed?).&lt;/p&gt;

&lt;p&gt;The key rule: if the agent said "unknown" but the judge found the answer, that's "incorrect." The verdict is about whether the agent delivered the right answer, not whether it tried hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the judge found
&lt;/h2&gt;

&lt;p&gt;Across 75 production scans reviewed (20 in a first batch, 55 in a second), the average score is ~50/100. That sounds terrible, but there's an important caveat: I don't run the judge on easy wins. I specifically select cases that seem interesting: "probable" confidence results, scans where the GS1 prefix contradicts the found country, results that look surprising, or products where a user submitted a correction. The judge is a learning tool, not a representative sample.&lt;/p&gt;

&lt;p&gt;The value isn't in the aggregate score. It's in the patterns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagxm1dfs1bhujtf33dt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagxm1dfs1bhujtf33dt6.png" alt="top patterns bar chart" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three findings stood out, each with a lesson that applies beyond my specific use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agents take shortcuts.&lt;/strong&gt; The biggest pattern (28/55 scans in the second batch): the agent uses all of its tool calls on web searches and almost never reads the actual pages. It finds a search snippet saying "Made in France," treats it as fact, and moves on. But that snippet might be a navigation link, a category filter, or a statement about a different product. The answer was often &lt;em&gt;on the page&lt;/em&gt;, one click away.&lt;/p&gt;

&lt;p&gt;If you're building an agent with tools, check whether it's actually using them all. Ours had &lt;code&gt;read_webpage&lt;/code&gt; available but preferred to stay in the comfortable search-snippet loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Curated benchmarks have blind spots.&lt;/strong&gt; The agent never searched by barcode number directly (18/55 scans). It always searched by product name. But some products don't have a clean name in our database, and searching the EAN directly on retailers would have found structured origin fields immediately.&lt;/p&gt;

&lt;p&gt;This pattern was invisible in the benchmark. Every benchmark item had a clean name because I'd curated it that way. The benchmark tested "can the agent find origin for a known product." Production tested "can the agent handle whatever random barcode a user scans." Different question, different failure modes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Patterns evolve, and you need to track them over time.&lt;/strong&gt; Between the first batch (20 reviews) and the second (55 reviews), snippet misinterpretation dropped from 35% to 18%. But wasted tool calls went &lt;em&gt;up&lt;/em&gt; from 15% to 25%. The agent was getting better at some things and worse at others. Without running the analysis twice, I would have missed both trends.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost problem (and an ugly but effective solution)
&lt;/h2&gt;

&lt;p&gt;The judge runs Claude Opus 4.6 via the &lt;a href="https://docs.anthropic.com/en/docs/about-claude/models" rel="noopener noreferrer"&gt;Anthropic API&lt;/a&gt;, with web search and page reading tools. Phase 2 alone can involve 4-8 tool calls. Each review costs between $0.40 and $0.70.&lt;/p&gt;

&lt;p&gt;For 50 products, that's $20-35. Not catastrophic, but too expensive for regular QA. I wanted to review every interesting production scan, not just a sample.&lt;/p&gt;

&lt;p&gt;My solution was pragmatic: I rebuilt the exact same judge as a slash command in &lt;a href="https://docs.anthropic.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; (Anthropic's CLI tool). Same 3-phase process, same tools, same structured output. The difference is that the CLI version runs on my Claude Max subscription instead of the API. Marginal cost per review: $0.&lt;/p&gt;

&lt;p&gt;The API version still exists for automated use. But day-to-day, I run &lt;code&gt;/judge &amp;lt;EAN&amp;gt;&lt;/code&gt; from my terminal and get the same structured review without paying per call.&lt;/p&gt;

&lt;p&gt;Is this elegant? No. Is it a long-term solution? Probably not. But it let me go from "I can afford to review 20 products a month" to "I can review every product I want." And that volume is what makes pattern analysis useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The analysis layer
&lt;/h2&gt;

&lt;p&gt;Individual reviews are useful. Patterns across reviews are transformative.&lt;/p&gt;

&lt;p&gt;On top of the judge, I built an analysis command that reads the last N reviews and identifies recurring patterns: which issue tags appear most often, which failures cluster together, which recommendations keep coming up, which types of queries consistently fail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkkpedooxcklbd7xz552.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkkpedooxcklbd7xz552.png" alt="feedback loop diagram" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output is a prioritized report. Each pattern gets a frequency (X out of N reviews), an impact rating (does it cause wrong answers or just inefficiency?), and a scope (universal, market-specific, category-specific). The report ends with 3-5 ranked recommendations.&lt;/p&gt;

&lt;p&gt;This is where the judge system pays for itself. One review tells you "this product got the wrong answer because the agent trusted a misleading snippet." Seventy-five reviews tell you "the agent almost never reads pages, and imposing a minimum page-read ratio would address the root cause." The first is an anecdote. The second is a strategy.&lt;/p&gt;

&lt;p&gt;The benchmark and the judge complement each other. The benchmark measures aggregate performance and catches regressions. The judge explains &lt;em&gt;why&lt;/em&gt; things fail and surfaces patterns that curated test sets miss. I need both.&lt;/p&gt;

&lt;h2&gt;
  
  
  The feedback loop
&lt;/h2&gt;

&lt;p&gt;The whole point of the judge is to feed improvements back into the agent. Some judge recommendations translated directly into improvements. The EAN-first pattern became a prompt change. The snippet misinterpretation finding led to the anti-FC rules I described in the &lt;a href="https://getmio.app/blog/prompt-engineering-llm-agent" rel="noopener noreferrer"&gt;prompt engineering article&lt;/a&gt; (the ones that failed on Flash Lite but worked on 3 Flash).&lt;/p&gt;

&lt;p&gt;Other recommendations didn't work in practice. The language adaptation suggestion (search in Italian for Italian products) added noise without improving accuracy on the benchmark. Sometimes the judge identifies a problem but the fix doesn't exist yet, or the model can't handle the added complexity.&lt;/p&gt;

&lt;p&gt;The judge doesn't replace human judgment about what to change. It tells you where to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this worth building?
&lt;/h2&gt;

&lt;p&gt;Honestly, the judge system took real engineering effort. The 3-phase process, the structured review schema, the trace formatting, the CLI rebuild, the analysis layer.&lt;/p&gt;

&lt;p&gt;But looking back, the judge found the EAN-first pattern that no amount of benchmark staring would have revealed. It confirmed benchmark findings with production data. It gave me a structured vocabulary for agent failures (those 13 issue tags) that made it possible to track patterns over time.&lt;/p&gt;

&lt;p&gt;If you're building an agent that runs in production, you need some way to understand &lt;em&gt;why&lt;/em&gt; it fails, not just &lt;em&gt;how often&lt;/em&gt;. Manual review doesn't scale. A judge agent does.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next up: From 42% to 78%: the full iteration log of a production AI agent. 108 benchmark runs, 7 models, 6 prompt versions, 3 weeks. Every decision we made, and the timeline that connects it all.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is part of a series on building a production AI agent for &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;. Previous: &lt;a href="https://getmio.app/blog/benchmarking-7-llms" rel="noopener noreferrer"&gt;Benchmarking 7 LLMs from 4 providers on the same agentic task&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
      <category>evaluation</category>
    </item>
    <item>
      <title>GPT-5.1 scored 26%. Gemini 3 Flash scored 74%. Same prompt, same tools.</title>
      <dc:creator>ThomasP</dc:creator>
      <pubDate>Sat, 28 Mar 2026 09:16:18 +0000</pubDate>
      <link>https://dev.to/thomas_p_fe0c76c7/gpt-51-scored-26-gemini-3-flash-scored-74-same-prompt-same-tools-33mi</link>
      <guid>https://dev.to/thomas_p_fe0c76c7/gpt-51-scored-26-gemini-3-flash-scored-74-same-prompt-same-tools-33mi</guid>
      <description>&lt;p&gt;In the &lt;a href="https://getmio.app/blog/benchmark-before-prompt" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, I explained how we built the evaluation infrastructure for our AI agent: a hand-curated golden dataset, a 3-run minimum per config, and the discovery that 17% of items flip between identical runs. This article puts that infrastructure to use.&lt;/p&gt;

&lt;p&gt;I'm building &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;, an app where you scan a product barcode and get the manufacturing country. The AI agent searches the web, reads pages, cross-references sources, and returns a country with a confidence level. I built the same agent pipeline for 5 providers: Gemini, Anthropic, OpenAI, xAI, and Mistral. Same prompt. Same tools. Same scoring.&lt;/p&gt;

&lt;p&gt;Here's what happened when I ran them all against the same benchmark.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8psfyxag9c73y5qky9dr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8psfyxag9c73y5qky9dr.png" alt="7 models, same prompt, same tools. Gemini 3 Flash wins at 74.5%. GPT-5.1, despite strong public benchmarks, scored 26.5%. Amber bars were eliminated on cost or latency, not accuracy." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The four walls
&lt;/h2&gt;

&lt;p&gt;This isn't a "which model is smartest" comparison. It's an elimination tournament. My agent runs inside a consumer app where real people scan products in a store and wait for an answer. That sets hard constraints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency&lt;/strong&gt;: under 10 seconds ideally, 15 seconds max. At 20-30 seconds, users put their phone back in their pocket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;: under ~$0.01 per scan. At $0.02, the unit economics don't work at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accuracy&lt;/strong&gt;: above ~60% country match. Below that, the app feels broken. Users scan 3 products, get 2 wrong answers, and uninstall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False confidence&lt;/strong&gt;: as low as possible. The agent saying "verified: Made in France" when the product is made in China is worse than saying "I don't know." One confident wrong answer destroys trust faster than ten honest unknowns.&lt;/p&gt;

&lt;p&gt;If any single dimension is unacceptable, the model is out. Doesn't matter how good the other numbers are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t21zszmgrhkfgod1lws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t21zszmgrhkfgod1lws.png" alt="The elimination funnel. 7 models enter, 2 survive. Each wall disqualifies on a single dimension." width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The eliminations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistral: accuracy (50%)
&lt;/h3&gt;

&lt;p&gt;Tested on our early eval dataset (10 items). Country match: 50%. Cost was the lowest of anything I tested ($0.0006/trace), latency was fine (10.5s). But 50% accuracy means the agent is basically guessing. Didn't proceed to the gold-curated benchmark.&lt;/p&gt;

&lt;h3&gt;
  
  
  GPT-5.1: accuracy (26.5%)
&lt;/h3&gt;

&lt;p&gt;This was the most surprising result. GPT-5.1 is a strong model on public benchmarks. On our gold-curated dataset (34 items), it scored 26.5% country match. The model returned null/low confidence on almost everything. 20 out of 34 items were "other failures" where the agent never submitted an answer.&lt;/p&gt;

&lt;p&gt;I need to be honest here: I'm not 100% sure this is the model's fault. Our OpenAI integration uses the Responses API, and the way tool results get passed back might not work as effectively as Gemini's native function calling. It's possible that a different integration approach would get better results. But at 26.5% on the only run I got, I didn't invest more time debugging it. The other providers worked out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  GPT-4.1: accuracy (43%) + rate limits
&lt;/h3&gt;

&lt;p&gt;Tested on our eval dataset (90 items): 43% country match, $0.014/trace, 17.9s latency. Already below the accuracy threshold. When I tried to run it on the gold-curated dataset at concurrency 20, it immediately hit OpenAI's 30K tokens-per-minute rate limit. Unusable for benchmarking, let alone production.&lt;/p&gt;

&lt;h3&gt;
  
  
  xAI Grok 4 Fast: latency (22-35s)
&lt;/h3&gt;

&lt;p&gt;This one was interesting. Across multiple runs on 29-30 items, accuracy ranged from 40% to 72.4%. The best run (72.4%) was genuinely competitive. And the cost was the lowest I tested alongside Mistral, around $0.001/trace.&lt;/p&gt;

&lt;p&gt;But latency killed it. Every run came in between 22 and 35 seconds. At 33.6 seconds average on the best-accuracy run, a user would be staring at a loading screen for half a minute. In a grocery store. Not viable.&lt;/p&gt;

&lt;p&gt;If xAI gets the latency down, Grok would be worth retesting. The accuracy signal was real.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Haiku 4.5: cost ($0.019/trace)
&lt;/h3&gt;

&lt;p&gt;This was the hardest elimination. Haiku got 67.6% accuracy on gold-curated (34 items), with 7 false confidence cases. Not far from Gemini 3 Flash (74.5%). On easy items, it hit 100%. Solid model.&lt;/p&gt;

&lt;p&gt;But: $0.019 per trace. That's 4-5x what Gemini costs. And latency was 17.4 seconds on the gold-curated run, with some eval-dev runs hitting 20-29 seconds.&lt;/p&gt;

&lt;p&gt;I tested Haiku extensively during early development (before the gold-curated benchmark existed). Multiple prompt versions, different configurations. The accuracy was consistently decent. The cost was consistently too high. At $0.019/scan, 10,000 daily users doing 3 scans each means $570/day just in LLM costs. Gemini at $0.004/scan brings that to $120/day for better accuracy.&lt;/p&gt;

&lt;p&gt;Sometimes a good model just doesn't fit the economics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemini 2.5 Flash: accuracy (45.6%) + false confidence (10.5)
&lt;/h3&gt;

&lt;p&gt;The predecessor to the models I ended up using. 45.6% accuracy with the highest false confidence of any Gemini model (10.5 average across 2 runs). Also 2x more non-deterministic than Flash Lite: 37% of items flipped between identical runs, compared to 17% for Flash Lite.&lt;/p&gt;

&lt;p&gt;Bad accuracy, bad FC, unstable results. Out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The survivors
&lt;/h2&gt;

&lt;p&gt;Two Gemini models made it through all four walls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemini 3.1 Flash Lite
&lt;/h3&gt;

&lt;p&gt;54-60% accuracy (varies by run), FC around 4-7, latency 8.6s, cost ~$0.006/trace. This was my production model for a while. Low false confidence, decent cost, fast.&lt;/p&gt;

&lt;p&gt;But as I described in the &lt;a href="https://getmio.app/blog/prompt-engineering-llm-agent" rel="noopener noreferrer"&gt;prompt engineering article&lt;/a&gt;, it was stuck on a local optimum. Every prompt change I tried made things worse. The model was too simple to follow nuanced rules. It worked, but it couldn't get better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemini 3 Flash (the winner)
&lt;/h3&gt;

&lt;p&gt;74.5% accuracy (average of 3 runs: 73.5%, 73.5%, 76.5%), FC 7.7, latency 13.5s, cost ~$0.004/trace. With parallel tool dispatch, the best single run hit 82.6%.&lt;/p&gt;

&lt;p&gt;Gemini 3 Flash didn't win by being the best at any single dimension. Not the cheapest (Flash Lite was cheaper). Not the lowest FC (Flash Lite had lower FC at ~5). Not the fastest (Flash Lite at 8.6s beat it). But it had the best &lt;em&gt;balance&lt;/em&gt;: highest accuracy by a wide margin, within acceptable bounds on everything else.&lt;/p&gt;

&lt;p&gt;And unlike Flash Lite, it responded to prompt optimization. The anti-FC rules, the nudge and anti-looping tweaks, the parallel dispatch instruction, all of these worked on 3 Flash. The model was smart enough to follow nuanced instructions, which meant I could keep improving it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Public benchmarks don't predict agentic performance.&lt;/strong&gt; GPT-5.1 ranks high on MMLU, HumanEval, and other standard benchmarks. It scored 26.5% on our task. Gemini 3 Flash ranks lower on most public benchmarks. It scored 74.5%. The gap is enormous. Agentic tool-use tasks (search, read, reason, decide) test something completely different from the typical "answer this question" benchmarks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iibwuwz7e0nvdzdu3pv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iibwuwz7e0nvdzdu3pv.png" alt="08 benchmark runs in Langfuse. Latency drops as I move to faster models, cost stabilizes around $0.004, accuracy climbs from ~45% to ~75%, and false confidence stays volatile." width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most eliminations were about economics, not intelligence.&lt;/strong&gt; Haiku at 67.6% would have been a perfectly good agent. Grok at 72.4% was competitive with Gemini. Both were eliminated on cost or latency, not accuracy. If you're building a backend service with no latency constraint and a generous budget, your winner might be completely different from mine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mnb66aztei9mdq1gef2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mnb66aztei9mdq1gef2.png" alt="Cost vs accuracy. The viable zone (top-left) requires both high accuracy and low cost. Haiku and Grok had the accuracy but failed on cost or latency. Only Gemini 3 Flash sits comfortably inside." width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing depth should match viability.&lt;/strong&gt; I ran 100+ benchmarks on Gemini models and 1-5 on everything else. That sounds unfair. But it's the right approach. Once a model hits a disqualifying wall, spending more benchmark budget on it is waste. I invested deeply where it mattered (the Gemini family where prompt optimization was possible) and lightly where elimination was clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Same prompt ≠ same results.&lt;/strong&gt; All five providers got the exact same system prompt and tool definitions. The accuracy range was 26.5% to 74.5%. The prompt was designed for Gemini (it's where I iterated), which probably gives Gemini an advantage. A prompt optimized for Haiku or GPT might close some of the gap. But the cost/latency constraints would still eliminate them for my use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The unified architecture paid for itself.&lt;/strong&gt; Building the agent for 5 providers with the same interface was real engineering work. But it meant every comparison was apples-to-apples. Same prompt, same tools, same scoring, same dataset. No "well maybe the OpenAI version just has different tools." If a model underperformed, it was the model (or the API integration), not the setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest caveats
&lt;/h2&gt;

&lt;p&gt;I want to be clear about what this benchmark does and doesn't show.&lt;/p&gt;

&lt;p&gt;It shows how these models perform on &lt;em&gt;my specific task&lt;/em&gt; (manufacturing country lookup via web search), with &lt;em&gt;my specific prompt&lt;/em&gt; (optimized for Gemini), at &lt;em&gt;my specific scale&lt;/em&gt; (consumer app, real-time, cost-sensitive). A different task, a different prompt, or different constraints could produce a completely different ranking.&lt;/p&gt;

&lt;p&gt;The GPT-5.1 result in particular might not reflect the model's true capability. If I'd spent more time on the OpenAI integration, the results might improve. I made a pragmatic choice: other providers worked immediately, so I invested time there instead.&lt;/p&gt;

&lt;p&gt;And the testing depth is uneven. 3 runs on Haiku versus 20+ runs on Gemini 3 Flash means I have much more confidence in the Gemini numbers. The Haiku result (67.6%) could be an unlucky run. Or a lucky one. With 1 run, I don't know.&lt;/p&gt;

&lt;p&gt;What I &lt;em&gt;do&lt;/em&gt; know: Gemini 3 Flash at $0.004/trace and 13.5s gives me 74.5% accuracy. That's the combination I can build a product on. For now.&lt;/p&gt;

&lt;p&gt;Because this benchmark is a snapshot, not a verdict. Prices drop. Models improve. Latency gets optimized. And every week I spend building and iterating on this agent, I learn more about prompting, tool design, and what makes a model work well on agentic tasks. The prompt I have today, optimized through 108 runs on Gemini, is a much better starting point for retesting other providers than the generic prompt I started with. Haiku was eliminated on cost, but Anthropic's pricing changes regularly. Grok was eliminated on latency, but xAI is actively optimizing inference speed. GPT-5.1 might just need a different integration approach.&lt;/p&gt;

&lt;p&gt;The elimination results are real, but they're not permanent. The benchmark infrastructure stays. The logs of what worked and what didn't stay. When the context changes, I'll rerun. That's the whole point of having the eval framework in place.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next up: LLM-as-Judge: using Claude to review a Gemini agent. How I automated QA by having a smarter model review every agent trace, and the patterns it found that I never would have caught manually.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is part of a series on building a production AI agent for &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;. Previous: &lt;a href="https://getmio.app/blog/benchmark-before-prompt" rel="noopener noreferrer"&gt;Why your LLM agent needs a benchmark before it needs a prompt&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>benchmark</category>
      <category>agents</category>
    </item>
    <item>
      <title>Why your LLM agent needs a benchmark before it needs a prompt</title>
      <dc:creator>ThomasP</dc:creator>
      <pubDate>Fri, 27 Mar 2026 08:05:57 +0000</pubDate>
      <link>https://dev.to/thomas_p_fe0c76c7/why-your-llm-agent-needs-a-benchmark-before-it-needs-a-prompt-1mgn</link>
      <guid>https://dev.to/thomas_p_fe0c76c7/why-your-llm-agent-needs-a-benchmark-before-it-needs-a-prompt-1mgn</guid>
      <description>&lt;p&gt;In the &lt;a href="https://getmio.app/blog/prompt-engineering-llm-agent" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, I showed how prompt optimization played out across three dimensions: prompt rules, tooling, and model capability. Some changes failed on one model and worked on another. Some only made sense once the tooling changed. Every claim in that article came with a specific number. "Accuracy dropped from 60% to 43%." "13 false confidence cases, worst run ever." "The same anti-FC rules went from catastrophic on Flash Lite to +10.8% on 3 Flash."&lt;/p&gt;

&lt;p&gt;How did I know any of that?&lt;/p&gt;

&lt;p&gt;Because before I wrote a single prompt engineering rule, I built the evaluation infrastructure to measure whether it worked. And the single most important thing that infrastructure revealed had nothing to do with prompts at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The most dangerous sentence in AI engineering
&lt;/h2&gt;

&lt;p&gt;"It seems better on a few examples."&lt;/p&gt;

&lt;p&gt;I hear this constantly from devs building with LLMs. They change the prompt, try it on 3-4 inputs, the outputs look better, they ship it. I did the same thing at the start. I'd tweak a rule, test it on the 2-3 products I had memorized, see an improvement, and commit.&lt;/p&gt;

&lt;p&gt;This is how you ship broken changes without knowing.&lt;/p&gt;

&lt;p&gt;The anti-false-confidence rules I wrote about in the last article? On a smaller model, they fixed the exact items I was testing against but dropped accuracy from 60% to 43% on everything else. On a smarter model weeks later, the same rules improved accuracy by +10.8%. Without a benchmark, I would have either shipped the broken version or permanently discarded rules that later turned out to be valuable.&lt;/p&gt;

&lt;p&gt;You can't evaluate an AI agent by eyeballing outputs. You need a benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  The golden dataset
&lt;/h2&gt;

&lt;p&gt;A benchmark needs ground truth. For my agent (which finds manufacturing countries from product barcodes), that means: for each product, what's the actual country, and how confident should the system be?&lt;/p&gt;

&lt;p&gt;My first attempt: I grabbed random EANs from my 69 million product database and ran about 30 benchmarks against them. It was useful for comparing models and getting rough numbers. But I kept running into a problem: when a run showed a regression, I wasn't sure if the agent got worse or if my "ground truth" was wrong. The expected countries had been found by the agent itself in earlier runs, verified by me with quick manual searches. Some were solid. Others, I wasn't confident enough to bet on.&lt;/p&gt;

&lt;p&gt;I was building on sand. If your ground truth is shaky, your benchmark is noise. You can't tell an actual regression from a label error.&lt;/p&gt;

&lt;p&gt;That's what pushed me to build the golden dataset properly. Each item hand-curated with a verified manufacturing country, a confidence level, and a difficulty rating.&lt;/p&gt;

&lt;p&gt;The difficulty ratings matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easy&lt;/strong&gt;: a product database already has the manufacturing country, or the first web search finds it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium&lt;/strong&gt;: needs 2-3 searches, maybe reading a retailer page, cross-referencing sources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard&lt;/strong&gt;: 3+ searches, white-label products, ambiguous evidence, or genuinely untraceable from public sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29cc0hejfko1jflbccnh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29cc0hejfko1jflbccnh.png" alt="The golden dataset in Langfuse. Each item is hand-curated with a verified country, confidence level, and difficulty rating." width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dataset grew over time: 21, then 30, then 34, then 46, then 53, then 57 items. I kept adding harder cases as the easy ones stabilized. This matters. A benchmark that only tests easy cases will tell you everything is fine when it isn't.&lt;/p&gt;

&lt;p&gt;The curation pipeline works like this: a product gets scanned, the agent processes it, an LLM judge reviews the agent's trace, then I validate the review in an admin panel. If the ground truth is solid, I mark it as "gold" and it gets pushed to the benchmark dataset. It's slow. Each item takes serious verification work. But the whole point is that these labels are &lt;em&gt;trustworthy&lt;/em&gt;. A benchmark with wrong labels is worse than no benchmark at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugrrvdzpczokt3z7i7kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugrrvdzpczokt3z7i7kq.png" alt="Curation pipeline: Scan &amp;gt; Agent &amp;gt; LLM Judge &amp;gt; Admin validation &amp;gt; Golden Dataset. The feedback loop means benchmark results surface new items to curate." width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The metric that actually matters
&lt;/h2&gt;

&lt;p&gt;I track 5 metrics on every benchmark run:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;country_match&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Right country?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;false_confidence&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Confidently wrong?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;confidence_met&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Right confidence level?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;latency_s&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How fast?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;cost&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How expensive?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most people would optimize for &lt;code&gt;country_match&lt;/code&gt;. Get the right answer more often. That's the obvious goal.&lt;/p&gt;

&lt;p&gt;It's not the right goal. Or at least, it's not the &lt;em&gt;only&lt;/em&gt; goal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False confidence is the metric I optimize against.&lt;/strong&gt; The agent says "verified: Made in France" and the product is actually made in China. That's the failure mode that destroys user trust. One confidently wrong answer does more damage than ten "I don't know" results.&lt;/p&gt;

&lt;p&gt;A concrete example from my data: the brand-level fallback I described in the last article got ~33% accuracy with 13 false confidence cases. A different config got 60% accuracy with 4 false confidence cases. The 60% config is &lt;em&gt;obviously&lt;/em&gt; better for users, even though neither has great raw accuracy. Because 13 confident wrong answers means the user stops trusting the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But accuracy without cost/latency is meaningless.&lt;/strong&gt; This is a consumer app. Someone scans a product in a grocery store and waits. During early testing, I benchmarked models that hit 70%+ accuracy but cost $0.02 per scan and took 20-30 seconds. A model at $0.004 per scan and 10 seconds that hits 65% is more shippable. The benchmark tracks cost and latency on every run precisely so I can make that trade-off with actual numbers instead of gut feeling.&lt;/p&gt;

&lt;p&gt;The optimization target is a three-way balance: minimize false confidence, maximize accuracy, keep cost and latency within production-viable bounds. Not a single number. A region in a multi-dimensional space.&lt;/p&gt;

&lt;h2&gt;
  
  
  The day everything changed
&lt;/h2&gt;

&lt;p&gt;About a week in, I had a baseline: 60% accuracy, 4 false confidence cases, on a 30-item dataset. I'd tried several prompt changes and they all seemed to make things slightly worse (53%, 57%, 50%). But the deltas were small. 1-2 items. Maybe just bad luck?&lt;/p&gt;

&lt;p&gt;So I ran the exact same code a second time. Same model. Same prompt. Same config. Same dataset. Same everything.&lt;/p&gt;

&lt;p&gt;Country match came out identical: 18/30 (60%) both runs.&lt;/p&gt;

&lt;p&gt;But when I looked at the individual items, 5 out of 30 had flipped. Products that were correct in the first run were wrong in the second. Products that were wrong became correct. One item went from a correct answer to "unknown." Another went from "unknown" to the right answer with high confidence. False confidence went from 4 to 5.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;17% of items gave different results on identical runs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was the most important finding of the entire project. It meant any delta of +/-1-2 items was noise, not signal. Several "neutral" iterations I'd already tested were probably indistinguishable from the baseline. I needed at least +/-4 items of difference on 30 items to have any confidence that a change was real.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nvx63bnquujv30j7o83.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nvx63bnquujv30j7o83.png" alt="Two identical runs on 30 items. Same 60% accuracy, but 5 items flipped between correct and wrong." width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I confirmed this later on a larger 46-item dataset. Two identical runs: 71.7% and 78.3%. That's a 7 percentage point gap on the &lt;em&gt;same code&lt;/em&gt;. With 46 items, I needed a &amp;gt;=10pp difference to call something significant.&lt;/p&gt;

&lt;p&gt;This one finding changed how I evaluate everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3-run minimum
&lt;/h2&gt;

&lt;p&gt;After the variance discovery, I established a rule: minimum 3 runs per configuration. No exceptions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1a84lb1r6svn60x8pcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1a84lb1r6svn60x8pcm.png" alt="108 benchmark runs in Langfuse. Each row is a full evaluation against the golden dataset." width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The big comparative session where I tested multiple models and prompt versions ran 3 models x 4 prompt versions, 3 runs each. The config I ended up shipping (Gemini 3 Flash with prompt v4) showed: 73.5%, 73.5%, 76.5% across its 3 runs. False confidence: 7, 9, 7.&lt;/p&gt;

&lt;p&gt;That variance in FC (7 to 9 on identical code) is exactly why single runs are dangerous. If I'd only run it once and gotten the 9-FC run, I might have rejected a config that was actually the best option. If I'd only gotten the 7-FC run, I might have been overconfident about how good it was.&lt;/p&gt;

&lt;p&gt;3 runs gives you the range. The average tells you where you probably are. The spread tells you how much to trust it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt versioning
&lt;/h2&gt;

&lt;p&gt;Every benchmark trace in &lt;a href="https://langfuse.com" rel="noopener noreferrer"&gt;Langfuse&lt;/a&gt; records two things in its metadata: the &lt;code&gt;PROMPT_VERSION&lt;/code&gt; constant and the git SHA. I bump the version on every prompt or pipeline change.&lt;/p&gt;

&lt;p&gt;This means I can always trace back. "This run used prompt v4 at commit &lt;code&gt;36c5871&lt;/code&gt;." If a result looks weird, I can check out that exact commit and re-run it. Full reproducibility.&lt;/p&gt;

&lt;p&gt;It sounds trivial. It's not. Without this, after 50+ runs, you lose track of what was tested when. "Was that the run with the anti-FC rules or without them?" is a question you never want to be asking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Growing the dataset
&lt;/h2&gt;

&lt;p&gt;The dataset started at 21 items, grew to 57, and sits at 69 as I write this. I kept adding harder cases as the easy ones stabilized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zf2mbuof9g58al53qrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zf2mbuof9g58al53qrg.png" alt="Dataset growth over time. Easy items plateau early. Most new additions are medium and hard cases." width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is important and also painful, because it means raw accuracy numbers across different dataset sizes aren't directly comparable.&lt;/p&gt;

&lt;p&gt;My first baseline was 42% on 21 items. My production config hits around 78% on 46 items. But the 46-item dataset is &lt;em&gt;harder&lt;/em&gt; than the 21-item one. I deliberately added products that are difficult to trace: white-label goods, brands that manufacture in multiple countries, products where "Made in France" on a retailer page actually refers to something else entirely.&lt;/p&gt;

&lt;p&gt;A dataset that only gets easier over time is useless. If your benchmark accuracy goes up just because you're adding easy cases, you're measuring your dataset curation skills, not your agent.&lt;/p&gt;

&lt;p&gt;The flip side: a dataset that gets harder over time means you can't compare run #1 to run #108 directly. You need to compare runs on the &lt;em&gt;same&lt;/em&gt; dataset version. This is where the git SHA tracking pays for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;If I started over, two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with 30+ items from day one.&lt;/strong&gt; My first benchmark had 21 items. That's too few. At 21 items, the variance is so high that almost nothing is statistically distinguishable from anything else. I wasted several days testing changes that were probably just noise. 30 items is the minimum where you start seeing actual signals. 50+ is where you get comfortable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track difficulty breakdown from the start.&lt;/strong&gt; I added difficulty labels (easy/medium/hard) early, and it turned out to be one of the most useful dimensions. A change that improves easy items by 10% but destroys medium items is not a win, even if the overall number looks similar. The breakdown shows you where the change actually helps and where it hurts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters beyond my project
&lt;/h2&gt;

&lt;p&gt;If you're building an AI agent that does anything more complex than "turn this text into JSON," you need eval infrastructure before you need prompt engineering. The pattern is always the same:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You change something&lt;/li&gt;
&lt;li&gt;It looks better on 3 examples&lt;/li&gt;
&lt;li&gt;You ship it&lt;/li&gt;
&lt;li&gt;It's actually worse on 80% of cases you didn't test&lt;/li&gt;
&lt;li&gt;You don't find out until users complain&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The benchmark breaks this cycle. Instead of "it seems better," you get "accuracy went from 60% to 43%, with 5 new regressions on items that used to work." That's a sentence that changes your decision.&lt;/p&gt;

&lt;p&gt;And the variance discovery applies to every LLM-based system, not just mine. If you're evaluating prompts with single runs, you're probably shipping noise. Run it 3 times. Look at the spread. You might find that your "10% improvement" was within the margin of error all along.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next up: &lt;a href="https://getmio.app/blog/benchmarking-7-llms" rel="noopener noreferrer"&gt;Benchmarking 7 LLMs from 4 providers on the same task&lt;/a&gt;. Same prompt, same tools, same dataset. GPT-5.1 scored 26%. Gemini 3 Flash scored 74.5%. The results were not what I expected.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is part of a series on building a production AI agent for &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;. Previous: &lt;a href="https://getmio.app/blog/prompt-engineering-llm-agent" rel="noopener noreferrer"&gt;The prompt engineering that didn't work (and what did)&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
      <category>testing</category>
    </item>
    <item>
      <title>The prompt engineering that didn't work (and what did)</title>
      <dc:creator>ThomasP</dc:creator>
      <pubDate>Mon, 23 Mar 2026 17:33:08 +0000</pubDate>
      <link>https://dev.to/thomas_p_fe0c76c7/the-prompt-engineering-that-didnt-work-and-what-did-4il3</link>
      <guid>https://dev.to/thomas_p_fe0c76c7/the-prompt-engineering-that-didnt-work-and-what-did-4il3</guid>
      <description>&lt;p&gt;In the &lt;a href="https://getmio.app/blog/why-finding-origin-is-ai-problem" rel="noopener noreferrer"&gt;first article of this series&lt;/a&gt;, I explained why finding a product's manufacturing country from its barcode is genuinely an AI problem. The data is scattered, misleading, and requires multi-step reasoning to untangle. I'm building &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;, an app where you scan a barcode and get the manufacturing country, powered by an AI agent that searches the web, reads pages, and cross-references sources.&lt;/p&gt;

&lt;p&gt;This article is about what happened when I tried to make that agent better.&lt;/p&gt;

&lt;p&gt;Over three weeks, I ran 108 benchmarks against a hand-curated golden dataset. Tested 7 models from 4 providers. Iterated through 6 major prompt versions with dozens of sub-variants. And what I learned is this: optimization is three-dimensional, and a change that fails in one context can succeed in another.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scoreboard
&lt;/h2&gt;

&lt;p&gt;Before I get into specifics, here's the summary. Every line is a real benchmark run, measured against ground-truth labels. "False Confidence" (FC) is our worst failure mode: the agent says it's confident and it's &lt;em&gt;wrong&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Gemini 3.1 Flash Lite&lt;/strong&gt; (our first production model):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;FC&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Baseline (v2)&lt;/td&gt;
&lt;td&gt;60%&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PRODUCTION&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anti-false-confidence rules&lt;/td&gt;
&lt;td&gt;43%&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;REVERTED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brand-level fallback search&lt;/td&gt;
&lt;td&gt;~33%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CATASTROPHIC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confidence calibration rules&lt;/td&gt;
&lt;td&gt;53%&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;REVERTED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thinking budget to 2048&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;REVERTED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temperature 1.0 (was 0)&lt;/td&gt;
&lt;td&gt;52%&lt;/td&gt;
&lt;td&gt;~5&lt;/td&gt;
&lt;td&gt;REVERTED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Double search results&lt;/td&gt;
&lt;td&gt;53%&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;REVERTED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ambiguity guard rule&lt;/td&gt;
&lt;td&gt;57%&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;REVERTED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 changes at once&lt;/td&gt;
&lt;td&gt;47%&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;REVERTED&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;On Gemini 3 Flash&lt;/strong&gt; (after model switch):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;FC&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Baseline (v2, same prompt)&lt;/td&gt;
&lt;td&gt;57.8%&lt;/td&gt;
&lt;td&gt;8.7&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anti-FC rules (v3)&lt;/td&gt;
&lt;td&gt;68.6%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;+10.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nudge + anti-looping (v4)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;74.5%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7.7&lt;/td&gt;
&lt;td&gt;SHIPPED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variant guard (v5)&lt;/td&gt;
&lt;td&gt;71.6%&lt;/td&gt;
&lt;td&gt;7.0&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocklist (v6a)&lt;/td&gt;
&lt;td&gt;71.6%&lt;/td&gt;
&lt;td&gt;6.3&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same anti-FC rules. Flash Lite: 60% -&amp;gt; 43%. Flash 3: 57.8% -&amp;gt; 68.6%. The rules weren't wrong. The model was too simple to follow them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What moved the needle across the whole project:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model switch (2.5 Flash -&amp;gt; 3.1 Flash Lite)&lt;/td&gt;
&lt;td&gt;+13.3% match, -3 FC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model switch (Flash Lite -&amp;gt; 3 Flash) + prompt recalibration&lt;/td&gt;
&lt;td&gt;+20% match vs original prod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parallel tool dispatch&lt;/td&gt;
&lt;td&gt;+8.2% match, -3.1 FC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The failures (and why they weren't really failures)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The brand-level fallback (worst run in project history)
&lt;/h3&gt;

&lt;p&gt;The idea seemed great: when the agent can't find where a &lt;em&gt;product&lt;/em&gt; is made, search for where the &lt;em&gt;brand&lt;/em&gt; manufactures. Unilever makes stuff in 50+ countries, but a small French brand probably has one factory.&lt;/p&gt;

&lt;p&gt;Result: 13 false confidence cases. The worst run I ever recorded. Not even close to second place.&lt;/p&gt;

&lt;p&gt;What happened: the model found "Brand X has a factory in Y" and immediately applied that to the specific product with full confidence. Every brand search returned &lt;em&gt;some&lt;/em&gt; country, and the model treated it as a verified answer.&lt;/p&gt;

&lt;p&gt;The lesson I keep coming back to: never add fallback strategies that give the model an alternative path to low-quality answers. It will use them eagerly to justify confident wrong answers. &lt;strong&gt;The model &lt;em&gt;wants&lt;/em&gt; to give you an answer.&lt;/strong&gt; Your job is to make the lazy path (giving up) easier than the wrong path (guessing from brand-level data).&lt;/p&gt;

&lt;p&gt;This one I haven't retried on a smarter model. It might work better on a model that can distinguish "brand manufactures in X" from "this specific product is made in X." But the failure was so spectacular that I moved on.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Anti-false-confidence rules (the plot twist)
&lt;/h3&gt;

&lt;p&gt;My agent had 4 items it was consistently wrong about, all with the same pattern: it trusted "Made in France" search snippets that actually referred to a different product on the same page. So I added 3 targeted rules. Things like "catalogue page snippets are unreliable, only trust product-specific pages" and "verified confidence requires reading the actual page, not just a search snippet."&lt;/p&gt;

&lt;p&gt;On Flash Lite, it was a disaster. Accuracy dropped from 60% to 43%. I tested 3 times: 43%, 53%, 50%. All worse. The model couldn't distinguish "this snippet from a catalogue page is unreliable" from "this snippet from a manufacturer's website is reliable." So it distrusted everything.&lt;/p&gt;

&lt;p&gt;I reverted and moved on. The rules were too nuanced for this model.&lt;/p&gt;

&lt;p&gt;Weeks later, I switched to Gemini 3 Flash and tested the same anti-FC rules. Accuracy went from 57.8% to 68.6%. False confidence dropped from 8.7 to 5.7. The exact same rules that broke Flash Lite were a massive improvement on a smarter model. 3 Flash could actually tell the difference between a catalogue page and a product page.&lt;/p&gt;

&lt;p&gt;This was the moment I understood that &lt;strong&gt;prompt optimization isn't one-dimensional&lt;/strong&gt;. A rule that fails on model A can succeed on model B. You don't discard the idea, you log it and revisit it when the context changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. More thinking, worse results
&lt;/h3&gt;

&lt;p&gt;Gemini has a "thinking budget" parameter that controls how many tokens the model can use for internal reasoning before responding. My baseline was 1024 tokens. I tried 2048. Then I tried the API's "medium" thinking level.&lt;/p&gt;

&lt;p&gt;Both were worse. 2048 tokens: -3 match, the model started overthinking simple items. Medium: -2 match, +1 false confidence, and average iterations jumped from 3.3 to 4.2. The model used the extra thinking budget to second-guess itself, not to make better decisions. It would find a clear "Made in Germany" statement, then spend 500 extra tokens wondering if it was &lt;em&gt;really&lt;/em&gt; Germany, and end up submitting "unknown, low confidence."&lt;/p&gt;

&lt;p&gt;1024 tokens forces concise, decisive reasoning. More thinking budget just means more hesitation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accuracy by thinking budget:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;thinkingBudget = 1024&lt;/code&gt;: &lt;strong&gt;60%&lt;/strong&gt; (optimal)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thinkingLevel = "medium"&lt;/code&gt;: 53%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thinkingBudget = 2048&lt;/code&gt;: 50%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More thinking = more hesitation, not better decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Temperature 1.0 (going against the docs)
&lt;/h3&gt;

&lt;p&gt;Gemini's documentation explicitly says that temperature below 1.0 causes "looping or degraded performance, particularly in complex reasoning tasks." So I tested temperature 1.0 against my baseline of 0.&lt;/p&gt;

&lt;p&gt;Result: -7.8% accuracy, +1.9% false confidence. Every metric worse. Higher temperature means more randomness, which means more hallucination, which means more confidently wrong answers.&lt;/p&gt;

&lt;p&gt;Temperature 0 is the right choice for structured tool calling. The official docs are talking about open-ended generation, not agentic workflows where you need deterministic, focused behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. More search results = more noise
&lt;/h3&gt;

&lt;p&gt;I doubled the number of search results per query from 5 to 10. More data should help the model find the right answer, right?&lt;/p&gt;

&lt;p&gt;Accuracy dropped. The model couldn't separate signal from noise in 10 results. Contradictory snippets from different products on different sites confused it. 5 focused results outperformed 10 noisy ones.&lt;/p&gt;

&lt;p&gt;There's a context quality threshold, and it depends on the model. A smaller model saturates faster. This is another change I'd want to retest on a bigger model.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Multiple changes at once (the compounding problem)
&lt;/h3&gt;

&lt;p&gt;I once shipped 3 prompt changes together: a new search strategy, retailer-specific instructions, and multilingual query templates. Accuracy dropped from 60% to 47%. Medium-difficulty items went from 29% to 0%. Complete collapse.&lt;/p&gt;

&lt;p&gt;I couldn't tell which change caused the regression. Maybe all three. Maybe just one. Doesn't matter. I reverted the whole thing and never made that mistake again.&lt;/p&gt;

&lt;p&gt;One change at a time. Always. If you can't measure the individual impact, you can't learn from it.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. The EU trap (when the model just says no)
&lt;/h3&gt;

&lt;p&gt;I added a &lt;code&gt;region&lt;/code&gt; field to the agent's output so products labeled "Made in EU" would return "EU" instead of "unknown." Seemed like a small, clean addition.&lt;/p&gt;

&lt;p&gt;The model ignored it completely. Across 6 benchmark runs. I reinforced the instruction in the prompt. Still ignored. I added it to the tool description. Still ignored. Country match and false confidence stayed exactly the same whether the field existed or not.&lt;/p&gt;

&lt;p&gt;Prompt engineering cannot force a model to fill a field it doesn't understand the purpose of. After a week of trying, I moved the logic to post-processing code. Deterministic. Works every time.&lt;/p&gt;

&lt;p&gt;Sometimes the answer isn't a better prompt. It's code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually worked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Switching models (the unlock for everything else)
&lt;/h3&gt;

&lt;p&gt;My first production model was Gemini 2.5 Flash. Switching to 3.1 Flash Lite gained +13.3% match and -3 false confidence. Better model, same prompt, instantly better. But Flash Lite turned out to be a ceiling, not a foundation. Every prompt change I tried on it made things worse.&lt;/p&gt;

&lt;p&gt;A note on why I stayed within the Gemini family: cost and latency. This is a consumer app where users scan products in a store and expect an answer in seconds. Before the gold-curated benchmark, I tested Haiku (Claude) extensively. It got comparable or slightly better accuracy on some runs, but at $0.01-0.02 per scan and 20-29 seconds latency. Gemini Flash was $0.002-0.004 per scan and 8-13 seconds. At 4-5x the cost and 2-3x the latency, Haiku wasn't viable for production, no matter how good the accuracy. GPT-4.1 had similar cost issues and wildly variable latency.&lt;/p&gt;

&lt;p&gt;The real unlock was switching to Gemini 3 Flash. On its first run with the same v2 prompt, it got the best raw accuracy I'd ever seen (66.7%) but also the worst false confidence (7 FC). It answered more questions, but also hallucinated more.&lt;/p&gt;

&lt;p&gt;Here's the thing though: once I was on 3 Flash, prompt engineering &lt;em&gt;started working again&lt;/em&gt;. The anti-FC rules that failed on Flash Lite? +10.8% on 3 Flash. The nudge and anti-looping rules? Pushed it to 74.5%. I tested 4 prompt variants on 3 Flash, running each 3 times to account for variance, and each showed meaningful differences.&lt;/p&gt;

&lt;p&gt;The model switch didn't just improve accuracy. It unlocked an entire dimension of optimization that was previously walled off.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Parallel tool dispatch (+8.2% accuracy)
&lt;/h3&gt;

&lt;p&gt;This was the change I expected to matter least.&lt;/p&gt;

&lt;p&gt;My agent was executing tool calls sequentially. Search, wait, read page, wait, search again, wait. I changed two things: the code now runs tool calls in parallel (&lt;code&gt;Promise.all&lt;/code&gt; instead of sequential &lt;code&gt;await&lt;/code&gt;), and I added one line to the prompt telling the model it &lt;em&gt;can&lt;/em&gt; batch multiple tool calls in a single turn.&lt;/p&gt;

&lt;p&gt;Result: +8.2% match, -3.1 false confidence. The best single improvement across the entire project. And it was remarkably stable: the worst run with this change (76.1%) still beat the average of the previous version (70.1%).&lt;/p&gt;

&lt;p&gt;The model started making two searches with different angles in the same turn instead of doing them sequentially. More coverage within the same tool budget. The improvement came from both sides: the prompt change (model batches more) and the infrastructure change (batched calls run in parallel).&lt;/p&gt;

&lt;p&gt;This is a good example of the three dimensions working together. The prompt told the model it &lt;em&gt;could&lt;/em&gt; batch. The tooling made batching &lt;em&gt;fast&lt;/em&gt;. And the model (3 Flash) was smart enough to actually &lt;em&gt;do&lt;/em&gt; it effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The config that held
&lt;/h3&gt;

&lt;p&gt;Temperature 0 and thinking budget 1024. Not glamorous. But these were the baseline settings that every experiment on every model failed to beat. I tested temperature 1.0 (worse), thinking budget 2048 (worse), thinking level "medium" (worse). The benchmarks don't lie.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real lesson: optimization is three-dimensional
&lt;/h2&gt;

&lt;p&gt;After 108 runs, I don't think about "prompt engineering" as a standalone activity anymore. The optimization space has three axes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt&lt;/strong&gt; (what you tell the model), &lt;strong&gt;Tooling&lt;/strong&gt; (what the model can do), and &lt;strong&gt;Model&lt;/strong&gt; (how capable the model is at following your instructions). And all three are constrained by a fourth dimension: &lt;strong&gt;cost and latency&lt;/strong&gt;. A brilliant model that costs $0.02/scan and takes 25 seconds isn't an option for a consumer app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy76v10d4hb1urecc6ol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy76v10d4hb1urecc6ol.png" alt="Three-axis optimization diagram: Prompt, Model, and Tooling axes with production config positioned at the intersection, constrained by cost and latency" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The anti-FC rules failed on Flash Lite and worked on 3 Flash. That's prompt x model. Parallel dispatch worked because of a prompt change AND an infrastructure change. That's prompt x tooling. And model switches unlocked prompt optimizations that were previously impossible. That's model x prompt. Staying within the Gemini family despite testing other providers? That's the cost/latency constraint eliminating otherwise viable options.&lt;/p&gt;

&lt;p&gt;You have to explore all three dimensions. And critically, you have to keep good logs. The anti-FC rules I "abandoned" on Flash Lite became one of my best improvements when I revisited them on 3 Flash weeks later. If I hadn't logged the iteration, I would have assumed "anti-FC rules don't work" and never tried them again.&lt;/p&gt;

&lt;p&gt;Don't discard an optimization because it failed in one context. Log it, understand &lt;em&gt;why&lt;/em&gt; it failed (model too simple? tooling bottleneck? wrong config?), and revisit it when the context changes.&lt;/p&gt;

&lt;p&gt;The seven consecutive failures on Flash Lite weren't wasted work. They were a map of what this model couldn't do, which made it obvious when to switch models, and which ideas to retry on the new one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need a benchmark to know any of this.&lt;/strong&gt; Most of these changes "felt" like improvements when I tested them on 3-4 examples. The anti-FC rules fixed exactly the items I was targeting. The brand-level pivot found correct answers for some products. Without measuring against 30+ items with ground-truth labels, I would have shipped broken changes and never known.&lt;/p&gt;

&lt;p&gt;That's actually the subject of the next article in this series.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next up: Why your LLM agent needs a benchmark before it needs a prompt. How we built the evaluation framework, why we measure false confidence instead of accuracy, and the day we discovered that 17% of items flip between identical runs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is part of a series on building a production AI agent for &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;. Previous: &lt;a href="https://getmio.app/blog/why-finding-origin-is-ai-problem" rel="noopener noreferrer"&gt;Why finding where a product is made is an AI problem&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>promptengineering</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Why finding where a product is made is an AI problem</title>
      <dc:creator>ThomasP</dc:creator>
      <pubDate>Tue, 17 Mar 2026 09:58:36 +0000</pubDate>
      <link>https://dev.to/thomas_p_fe0c76c7/why-finding-where-a-product-is-made-is-an-ai-problem-5d1e</link>
      <guid>https://dev.to/thomas_p_fe0c76c7/why-finding-where-a-product-is-made-is-an-ai-problem-5d1e</guid>
      <description>&lt;p&gt;&lt;em&gt;A barcode tells you where a product was registered. Not where it was made.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Pick up any product at the grocery store. Flip it over. See that barcode? The first three digits tell you which country it was &lt;em&gt;registered&lt;/em&gt; in. Starts with 300-379? France. 400-440? Germany. 890? India.&lt;/p&gt;

&lt;p&gt;Most people (including me, before I started working on this) assume that's where the product is manufactured.&lt;/p&gt;

&lt;p&gt;It's not. Not even close.&lt;/p&gt;

&lt;p&gt;A French brand can register barcodes in France and make everything in China. A German company can produce in Poland. That 3-digit GS1 prefix matches the actual manufacturing country about 40% of the time. Basically a coin flip.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsqfxc8ynr5rjqln5unl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsqfxc8ynr5rjqln5unl.png" alt="The GS1 barcode prefix indicates where the brand is registered, not where the product is manufactured. It matches the actual manufacturing country about 40% of the time." width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm building &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;, an app that lets you scan a barcode and find where a product is actually made. What I thought would be a fun database project turned into one of the most interesting AI engineering challenges I've worked on. Here's why.&lt;/p&gt;

&lt;p&gt;Fair warning: I'm a developer, not a data scientist. Some of what I'll share in this series (variance exists! you need more than 3 test cases!) will make ML engineers smile. But most devs building with LLMs right now don't have a stats background, and I suspect they're running into the same walls I did. If my trial-and-error saves someone a few days, that's enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data exists. Good luck finding it.
&lt;/h2&gt;

&lt;p&gt;Here's the thing that makes this problem so sneaky: the manufacturing origin of most products &lt;em&gt;is&lt;/em&gt; available somewhere. It's buried in a retailer's product page. It's in an open database. It's on the packaging in 6pt font. It's implied by a quality label that legally requires a specific region.&lt;/p&gt;

&lt;p&gt;But "somewhere" is doing a lot of heavy lifting in that sentence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's fragmented.&lt;/strong&gt; One database has the product name but no origin. A retailer page has "Country of manufacture: Germany" buried in the specs tab nobody clicks. An open food database has a sanitary registration code that &lt;em&gt;implies&lt;/em&gt; a packaging location, which may or may not match where it was made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's inconsistent.&lt;/strong&gt; "Made in France." "Fabriqué en France." "Pays de fabrication : FR." "Lieu de production : Normandie." Same information, a dozen different formats across languages and conventions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's actively misleading.&lt;/strong&gt; And this is where it gets really fun. A retailer might display "Fabriqué en France" as a site-wide promotional banner , not a statement about the specific product you're looking at. Amazon might show "Country of Origin: China" for the &lt;em&gt;seller's account&lt;/em&gt;, not the product. A brand's website proudly states "French since 1921" while manufacturing in Italy through a parent company nobody's heard of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjo2ktghtn8vmx2vwy2c2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjo2ktghtn8vmx2vwy2c2.png" alt="The same product's origin information scattered across five different sources, each with a different (and sometimes contradictory) fragment of data." width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not a database lookup problem. This is a reasoning problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The obvious approaches. We tried them all.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Just build a database."&lt;/strong&gt; We did. We integrated a product database covering 69 million items. It has names, brands, categories, labels, and for some products, manufacturing origin. When that field is populated, it's rock solid. Problem: it's populated for maybe 15-20% of products. The other 80% give you a name and brand, but no origin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Just scrape retailer sites."&lt;/strong&gt; Tried that too. Some retailers do list manufacturing origin in product specs. But not all products, not all retailers, and the HTML structure varies wildly. A static scraping pipeline breaks every time someone redesigns a product page. Which is constantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Isn't this regulated?"&lt;/strong&gt; In the EU, manufacturing country isn't mandatory on most product labels. Food is somewhat better covered, but even food has exceptions. And regulatory databases, when they exist, are rarely machine-readable.&lt;/p&gt;

&lt;p&gt;Each approach alone tops out at ~20-30% coverage. And none of them can tell you &lt;em&gt;how confident&lt;/em&gt; to be. A direct "Made in Germany" statement on the manufacturer's website is a completely different signal than inferring "probably Germany" because the brand is German and the barcode prefix is 400.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is actually a reasoning task
&lt;/h2&gt;

&lt;p&gt;Here's the insight that changed everything for us: finding where a product is manufactured is a multi-step reasoning task with uncertain evidence.&lt;/p&gt;

&lt;p&gt;A real example from our benchmark: a toothbrush with a French barcode, sold by a brand founded in France in 1921, now owned by an Italian conglomerate. First web search returns a retailer page showing "Fabriqué en France", but is that about this product, or a promotional banner for the retailer's French-made product line? A second result shows the parent company runs factories in Italy, Poland, and France. An open database has no manufacturing data but lists a sanitary code starting with "IT", suggesting Italian packaging.&lt;/p&gt;

&lt;p&gt;To actually figure this out, you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search across multiple sources, in multiple languages&lt;/li&gt;
&lt;li&gt;Actually &lt;em&gt;read&lt;/em&gt; the pages, not just search snippets, to verify that "Made in X" refers to this specific product&lt;/li&gt;
&lt;li&gt;Cross-reference: does the sanitary code match the web sources? Does the corporate ownership explain the discrepancy?&lt;/li&gt;
&lt;li&gt;Calibrate confidence. Is this verified or an educated guess?&lt;/li&gt;
&lt;li&gt;Know when to stop. Some products can't be traced from public sources. "Unknown" beats a wrong answer every time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is textbook AI agent territory. Not a single LLM call. Not RAG. An &lt;em&gt;agent&lt;/em&gt; that decides what to do next based on what it's found so far.&lt;/p&gt;

&lt;p&gt;In practice, this is what it looks like: you scan a product in a store, and within a few seconds you get the manufacturing country, a confidence level, the reasoning behind it, and links to the sources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frein0e071pmxrm18fris.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frein0e071pmxrm18fris.jpg" alt="A scan in Mio: manufacturing country, confidence level, reasoning and sources." width="800" height="1734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture (high level)
&lt;/h2&gt;

&lt;p&gt;The system follows a simple priority chain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database first&lt;/strong&gt;: when a structured database has the origin with high confidence, return it instantly. No LLM needed. This handles ~15-20% of queries in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent for the rest&lt;/strong&gt;: an LLM agent with access to web search and page reading, tasked with finding and verifying the manufacturing country. It searches, reads pages, cross-references, and submits an answer with a confidence level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confidence as a first-class output&lt;/strong&gt;: every result comes with "verified" (explicit source), "probable" (indirect evidence), or "low" (couldn't find much). This distinction matters more than the country itself for user trust.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent can dynamically decide: search with different keywords, read a promising page, try a different language, or bail and report low confidence. That adaptive loop is the whole point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkiz58kbcw42q6qgizch4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkiz58kbcw42q6qgizch4.png" alt="When the database has the answer, no AI needed. For the other 80%, an agent searches, reads, and cross-references public sources before submitting a result with a confidence level." width="800" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's an important constraint though: this runs in real time. A user scans a product in a store and expects an answer in seconds, not minutes. And every web search, every page read costs money. So the system needs to be accurate, fast, &lt;em&gt;and&lt;/em&gt; cheap. A model that gets 5% more answers right but costs 5x more per scan and takes 30 seconds instead of 10 isn't viable for a consumer app. Finding the right balance between accuracy, cost, and latency turned out to be as hard as the accuracy problem itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five traps that will wreck your accuracy
&lt;/h2&gt;

&lt;p&gt;Building this system taught me things no tutorial or documentation covers. Here are the failure modes that cost us the most time:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The GS1 prefix trap
&lt;/h3&gt;

&lt;p&gt;The agent sees a barcode starting with 300 (France) and subconsciously anchors on France, even when the evidence points elsewhere. We had to explicitly break this: "The barcode prefix is where the brand is &lt;em&gt;registered&lt;/em&gt;. It is NOT evidence of manufacturing origin." Without this, the agent has a strong France bias. 5 out of 7 false confidence cases in our first benchmark were the agent saying "France" when it was wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The brand ≠ factory trap
&lt;/h3&gt;

&lt;p&gt;Moulinex is a French brand. It manufactures in China, Poland, and France depending on the product line. Our agent confidently said "Made in France" for products manufactured on a different continent, because the brand's Wikipedia page says "French company." "French brand" is not "French product." Obvious in hindsight. Not obvious to an LLM.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The retailer badge trap
&lt;/h3&gt;

&lt;p&gt;This was our number one source of false confidence. Some retail websites show origin-related badges ("Made in France," "Produit local") as promotional elements across their entire site. These show up in search snippets &lt;em&gt;right next to&lt;/em&gt; the product listing. The agent can't distinguish a product-specific statement from a marketing banner without actually reading the full page.&lt;/p&gt;

&lt;p&gt;We had cases where the agent stated "verified: Made in France" based on a badge that applied to a completely different product line on the same retailer site. Brutal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1x20qfpx2h0oy1xoyoui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1x20qfpx2h0oy1xoyoui.png" alt="A real retailer page where " width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The "EU" trap
&lt;/h3&gt;

&lt;p&gt;Many products say "Made in EU." Technically correct, practically useless. 27 member states. We spent a week trying to handle this at the model level. The model completely ignored our instructions across every prompt version we tried. Sometimes the right answer is to accept the limitation.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Packaging ≠ manufacturing
&lt;/h3&gt;

&lt;p&gt;Sanitary registration codes (EMB codes) tell you where a product was &lt;em&gt;packaged&lt;/em&gt;, not where it was &lt;em&gt;manufactured&lt;/em&gt;. A product made in Spain can be packaged in France and carry a French code. The data &lt;em&gt;looks&lt;/em&gt; authoritative, which is exactly what makes it dangerous.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually matters (after 108 benchmark runs)
&lt;/h2&gt;

&lt;p&gt;We ran 108 benchmarks over three weeks. Seven models from four providers. Six major prompt versions with dozens of sub-variants. A golden dataset we hand-curated from 21 items to 57, adding harder cases as the easy ones stabilized. Every single run was measured against ground-truth labels, with the prompt version and git SHA recorded on each trace in &lt;a href="https://langfuse.com" rel="noopener noreferrer"&gt;Langfuse&lt;/a&gt; for full reproducibility.&lt;/p&gt;

&lt;p&gt;We went from 42% accuracy to 78%. Here's what crystallized:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False confidence is the metric that matters.&lt;/strong&gt; Not accuracy. A system that says "I don't know" when it doesn't know is infinitely more trustworthy than one that answers everything but is wrong 15% of the time. We call it "false confidence": the agent says "verified" and it's &lt;em&gt;wrong&lt;/em&gt;. That's the number we optimize against above all others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The information quality hierarchy is steep.&lt;/strong&gt; A structured database field is gold. An explicit "Made in X" on the manufacturer's website is silver. A retailer listing with origin in the specs is bronze. A search snippet mentioning a country near a product name is &lt;em&gt;lead&lt;/em&gt;, heavy and potentially toxic. We learned this the hard way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimization is three-dimensional.&lt;/strong&gt; We iterated on prompts, tooling, and models, and the interactions between the three are what matter. Prompt rules that failed on a smaller model worked perfectly on a smarter one. Parallel tool execution only helped because the model was smart enough to batch calls and the prompt told it to. We doubled search results from 5 to 10 per query and accuracy dropped, not because "more is bad" but because that model couldn't handle the noise. The best results came from finding the right combination across all three axes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intellectual honesty is non-negotiable.&lt;/strong&gt; We're not auditing factories. We're not certifying supply chains. We're aggregating publicly available information, assigning a confidence level, and presenting it transparently. If a brand lies on its website, we'll relay that lie, and the confidence system will reflect how many independent sources confirmed it. Being clear about what the system can and cannot do is both the ethical choice and the one that builds the most trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  This pattern is everywhere
&lt;/h2&gt;

&lt;p&gt;The reason I'm writing this up is that this problem structure is way more common than people realize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The answer exists &lt;em&gt;somewhere&lt;/em&gt; in public sources&lt;/li&gt;
&lt;li&gt;No single source is reliable on its own&lt;/li&gt;
&lt;li&gt;The reasoning path depends on what you find at each step&lt;/li&gt;
&lt;li&gt;Confidence calibration is as important as the answer itself&lt;/li&gt;
&lt;li&gt;The problem looks trivially solvable until you actually try to automate it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the problems where AI agents genuinely earn their keep. Not because any individual step is hard (searching, reading a webpage, comparing two strings) but because orchestrating those steps requires judgment. When to search again, when to read the full page, when to accept the evidence, when to give up.&lt;/p&gt;

&lt;p&gt;108 benchmark runs, 7 models, 6 prompt versions, 3 weeks. The journey from "this kind of works" to "this is reliable enough to ship" was far more interesting, and far more counterintuitive, than I expected. Prompt rules that failed on one model worked on another. Changes I'd written off as failures turned into wins in a different context. The biggest gains came from places I didn't expect.&lt;/p&gt;

&lt;p&gt;That's what I gonna tell in the rest of this series.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next up: Why we built the evaluation framework before writing a single line of prompt. And why "it seems better on a few examples" is the most dangerous sentence in AI engineering.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm building &lt;a href="https://getmio.app" rel="noopener noreferrer"&gt;Mio&lt;/a&gt;, an app that surfaces manufacturing origin from product barcodes. 108 runs, 7 models, a hand-curated golden dataset, and an LLM-as-judge system reviewing the agent's work. If you've built evaluation pipelines for AI agents or dealt with similar multi-source reasoning problems, I'd love to hear about your experience.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
