<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Eval Notes]]></title><description><![CDATA[Eval Notes]]></description><link>https://eval-notes.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 28 Oct 2026 14:25:33 GMT</lastBuildDate><atom:link href="https://eval-notes.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Structured Output Beats Prompt Tweaking: A Practical Eval Loop]]></title><description><![CDATA[We spent two weeks rewriting prompts. Then we changed the response format, and the failure rate dropped by half. That is not a story about prompt…]]></description><link>https://eval-notes.hashnode.dev/structured-output-beats-prompt-tweaking-a-practical-eval-loop</link><guid isPermaLink="true">https://eval-notes.hashnode.dev/structured-output-beats-prompt-tweaking-a-practical-eval-loop</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Priya Raman]]></dc:creator><pubDate>Wed, 28 Oct 2026 05:17:16 GMT</pubDate><content:encoded><![CDATA[<p>We spent two weeks rewriting prompts. Then we changed the response format, and the failure rate dropped by half.</p>
<p>That is not a story about prompt engineering being useless. It is a story about measuring the wrong thing.</p>
<h2>The problem</h2>
<p>Our extraction endpoint returned prose that we parsed with a regex. When the model changed its phrasing, the regex broke quietly, and the pipeline wrote empty fields into the database.</p>
<p>We counted those as prompt problems for ten days. They were parsing problems wearing a costume.</p>
<h2>The change</h2>
<p>We switched the endpoint to structured output and made the schema the contract:</p>
<pre><code class="language-python">schema = {
    "type": "object",
    "properties": {
        "vendor": {"type": "string"},
        "amount": {"type": "number"},
        "currency": {"type": "string", "enum": ["USD", "EUR", "GBP"]},
    },
    "required": ["vendor", "amount", "currency"],
}
</code></pre>
<p>Now a malformed response is a validation error we can count, instead of a silent empty field.</p>
<h2>The eval loop</h2>
<p>The part that actually mattered was the loop around it:</p>
<ol>
<li>Fifty hand-labelled examples, including the ugly ones.</li>
<li>Run the new prompt and the old prompt over the same set.</li>
<li>Compare field-level accuracy, not exact string match.</li>
</ol>
<p>A human judged the first pass, because the disagreements were the interesting part.</p>
<h2>What I would do differently</h2>
<p>I would build the eval set before touching the prompt. Two weeks of prompt work is cheap to run and expensive to interpret, especially when you have no baseline to interpret it against.</p>
]]></content:encoded></item></channel></rss>