<script data-pm-proxy="intercept"></script><?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" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Hands On "AI Engineering"]]></title><description><![CDATA[Hands On "AI Engineering Course' With "AI Powered Quiz" Implementation, Learn How to Build from Scratch.]]></description><link>https://aieworks.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!1tXM!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe98c103-2b00-43fb-a63d-781f7bd77735_1024x1024.png</url><title>Hands On &quot;AI Engineering&quot;</title><link>https://aieworks.substack.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 02 Sep 2026 20:15:14 GMT</lastBuildDate><atom:link href="/__u/aieworks.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Systemdr, Inc.]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[aieworks@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[aieworks@substack.com]]></itunes:email><itunes:name><![CDATA[AI Engineering]]></itunes:name></itunes:owner><itunes:author><![CDATA[AI Engineering]]></itunes:author><googleplay:owner><![CDATA[aieworks@substack.com]]></googleplay:owner><googleplay:email><![CDATA[aieworks@substack.com]]></googleplay:email><googleplay:author><![CDATA[AI Engineering]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Lesson 9: Building the ResponseFormatter]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-9-building-the-responseformatter</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-9-building-the-responseformatter</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Mon, 31 Aug 2026 08:30:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Nyk6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>The last of the four agent classes &#8212; the formatter that shapes what actually reaches a user</p></li><li><p>A hard guarantee: a rejected answer&#8217;s raw text never appears in the delivered response</p></li><li><p>Distinct, honest messaging for all four outcomes a query can reach</p></li></ul><h2>Why This Matters</h2><blockquote><p>Day 8&#8217;s critic can reject an answer, but rejecting something internally doesn&#8217;t automatically stop it from being shown &#8212; that only happens if whatever renders the final response actually respects the verdict. A pipeline can do everything right up through the critic and still leak a bad answer if the formatting layer doesn&#8217;t check the verdict before displaying anything. Today&#8217;s component is where that check either happens or doesn&#8217;t.</p></blockquote><h2>Core Concept</h2><p><code>ResponseFormatter</code> branches on the critic&#8217;s verdict label, not on the answer&#8217;s content. A <code>rejected</code> verdict never reaches the branch that formats <code>answer.text</code> at all &#8212; it&#8217;s routed to a fixed, generic safe message before the raw text is ever touched. The failure reason (which check failed) is preserved in an <code>internal_debug</code> field for troubleshooting, but that field lives only in the structured response, never in <code>display_text</code>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This is verified directly, not just asserted: running the corrupted answer from Day 8 through today&#8217;s formatter and checking the output confirms <code>"90 days"</code> &#8212; the fabricated detail &#8212; appears nowhere in either <code>display_text</code> or the full <code>channel_json</code> response. The only thing that survives into the delivered response is <code>grounded_in_source</code>, the name of the check that failed, useful for debugging and unreadable as an answer.</p><p>Four distinct outcomes exist, and each gets its own honest message: a clean <code>approved</code> answer is delivered with its caveat appended if one exists; <code>approved_fallback</code> (retrieval found nothing) is delivered as the honest &#8220;no information found&#8221; message it already was; <code>rejected</code> gets the safe generic message with debug info hidden from the user; and <code>out_of_scope</code> gets a message that tells the user what the system <em>can</em> help with, not just what it can&#8217;t.</p><h2>Architecture</h2><p><code>ResponseFormatter</code> is the final stop before delivery &#8212; everything upstream (Days 6 through 8) can be wrong in some way, and this component is where those failures either get correctly contained or don&#8217;t. It returns a <code>FormattedResponse</code> with both a <code>display_text</code> (what a chat interface would show) and a <code>channel_json</code> (what an API caller would receive), so the same verdict can serve multiple delivery surfaces consistently.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Nyk6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 424w, /__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 848w, /__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Nyk6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png" width="670" height="300.9478021978022" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:654,&quot;width&quot;:1456,&quot;resizeWidth&quot;:670,&quot;bytes&quot;:655817,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601327?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 424w, /__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 848w, /__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Nyk6!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1450871d-ebd7-4a69-b913-930c77e7a405_4900x2200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ffef7265-4ec6-4c9d-bc62-0a69aa38b784&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def format(self, answer: DraftAnswer, verdict: CriticVerdict) -&gt; FormattedResponse:
    if verdict.verdict_label == "rejected":
        failed_checks = [c.name for c in verdict.checks if not c.passed]
        return FormattedResponse(
            status="rejected",
            display_text=SAFE_REJECTION_MESSAGE,  # answer.text is never referenced below this line
            channel_json={"status": "rejected", "text": SAFE_REJECTION_MESSAGE,
                          "sources": [], "internal_debug": {"failed_checks": failed_checks}},
        )
    ...
</code></pre></div><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;cddb4450-0770-404b-89d9-a928b2efcd12&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p><code>start.sh</code> installs dependencies, builds the Docker image if available, runs the lesson, runs the tests, and generates <code>dashboard.html</code>. Each card shows the internal raw draft next to what was actually delivered &#8212; for the corrupted answer, these are visibly, confirmedly different. Tests: <code>6 passed</code>, including two that check the corrupted text directly against both <code>display_text</code> and the full JSON response.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;60544527-ded1-4f74-b9b8-af3ae860ac0b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh
</code></pre></div><h1>Implementation Guide</h1><h2><strong>GitHub Link</strong></h2><p><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_09_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_09_package</a></p><h2>Package contents</h2><pre><code><code>lesson_09_package/
&#9500;&#9472;&#9472; lesson_code.py    # ResponseFormatter, FormattedResponse, generate_dashboard (+ Days 6-8's agents, Days 3-5's retrieval stack)
&#9500;&#9472;&#9472; test_lesson.py    # 6 tests verifying all four outcome paths
&#9500;&#9472;&#9472; requirements.txt  # pytest only
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, dashboard.html, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference
</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;e59e923f-4fe9-457a-8869-b5a985b00d3f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_09_package
chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p>You should see <code>dashboard.html</code> generated, and <code>6 tests passed</code>. Open the dashboard &#8212; each card shows a &#8220;RAW DRAFT&#8221; box (internal-only) next to a &#8220;DELIVERED TO USER&#8221; box; for the corrupted-answer card, these are visibly different.</p><p>Cleanup: <code>./stop.sh</code> &#8212; also removes the generated <code>dashboard.html</code>.</p><h2>Architecture, in more detail</h2><p><code>ResponseFormatter</code> has two entry points: <code>format_out_of_scope()</code> for queries Day 6 already routed away, and <code>format()</code> for everything that reached the critic. <code>format()</code> branches on <code>verdict.verdict_label</code> first, before touching <code>answer.text</code> at all in the rejected case &#8212; this ordering is deliberate. The rejected branch constructs its entire <code>FormattedResponse</code> from <code>SAFE_REJECTION_MESSAGE</code> and a list of failed check <em>names</em> (not their reasons, and never the answer text), so there&#8217;s no code path inside that branch where the raw answer could leak in.</p><p><code>FormattedResponse</code> carries both <code>display_text</code> (a single string, what a chat UI would render) and <code>channel_json</code> (a structured dict, what an API response would return) so the same verdict produces consistent messaging across delivery surfaces &#8212; a chat interface and an API caller should never disagree about whether an answer was safe to show.</p><h2>Why this guarantee is tested directly, not just implied by the code structure</h2><p>Code that &#8220;looks like&#8221; it can&#8217;t leak the raw answer can still leak it after a well-intentioned edit &#8212; someone adds a debug line, a logging statement, or a &#8220;just show the original text so support can see it&#8221; shortcut, and the guarantee quietly breaks. <code>test_rejected_answer_raw_text_never_appears_in_display_text</code> and <code>test_rejected_answer_raw_text_never_appears_in_channel_json</code> check the actual string content of the output, not the code path that produced it &#8212; they&#8217;d fail immediately if a future change reintroduced the leak, regardless of how that change was written.</p><h2>Reading the four outcomes</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!b0GW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 424w, /__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 848w, /__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 1272w, /__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!b0GW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png" width="650" height="291.30434782608694" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:402,&quot;width&quot;:897,&quot;resizeWidth&quot;:650,&quot;bytes&quot;:36641,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601327?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 424w, /__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 848w, /__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 1272w, /__u/substackcdn.com/image/fetch/$s_!b0GW!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4902d4c-0c53-46ec-9409-2f80814f4ccd_897x402.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!zQ_y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!zQ_y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png" width="658" height="322.22115384615387" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:713,&quot;width&quot;:1456,&quot;resizeWidth&quot;:658,&quot;bytes&quot;:828574,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601327?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zQ_y!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ba0a761-07ca-4d1d-9a44-8671682bb309_4900x2400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!LO7x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 424w, /__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 848w, /__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!LO7x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png" width="882" height="510" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:510,&quot;width&quot;:882,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:51343,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601327?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 424w, /__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 848w, /__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LO7x!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ea9eb8b-8033-4d95-a860-633c847f282f_882x510.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Working Demo Link:</h3><div id="youtube2-gkxUvZ747Ow" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;gkxUvZ747Ow&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/gkxUvZ747Ow?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside <code>lesson_09_package</code>.</p></li><li><p><strong>Wondering why the dashboard itself shows the raw draft</strong> &#8212; the dashboard is an internal debugging tool built for this lesson, not the actual delivery surface; showing both views side by side is what makes the guarantee visible and checkable. The guarantee applies to <code>display_text</code> and <code>channel_json</code> &#8212; what a real user or API caller would receive &#8212; not to this lesson&#8217;s own diagnostic output.</p></li><li><p><code>dashboard.html</code><strong> doesn&#8217;t open automatically</strong> &#8212; <code>start.sh</code>&#8216;s auto-open is best-effort and silently skipped in headless environments; open the file manually.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>This pattern &#8212; separating &#8220;what a component produced internally&#8221; from &#8220;what crosses the trust boundary to a user&#8221; &#8212; is standard practice anywhere a system needs to fail safely rather than just fail. It&#8217;s also exactly the kind of guarantee that&#8217;s easy to claim and easy to accidentally break with an innocent refactor; having a test that checks the corrupted text isn&#8217;t present in the output, rather than trusting the code path looks right, is what keeps the guarantee real over time.</p></blockquote><h2>Next Steps</h2><p>Days 10 through 12 wire all four agents into one working <code>Pipeline</code>, run it end to end, and establish the baseline measurement every later phase &#8212; starting with evaluation engineering &#8212; will measure improvement against.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lesson 8: Building the CriticAgent]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-8-building-the-criticagent</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-8-building-the-criticagent</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Thu, 27 Aug 2026 08:30:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!T7Yx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>A grounding check that verifies an answer&#8217;s text actually appears in the source it cites &#8212; not just trusts that it does</p></li><li><p>A caveat-surfaced check catching cases where an entity mismatch was flagged but the warning never made it into the final draft</p></li><li><p>A deliberately broken answer, injected on purpose, to prove the critic actually rejects something</p></li></ul><h2>Why This Matters</h2><blockquote><p>Day 7&#8217;s synthesis promises extractive answers: text taken directly from a source, never invented. A promise enforced by one component&#8217;s code isn&#8217;t the same as a promise verified by another. If a future change to synthesis introduces a bug &#8212; a truncation error, an off-by-one in string slicing, a well-intentioned &#8220;let me just clean this up&#8221; edit &#8212; nothing catches it unless something downstream actually checks. That&#8217;s what a critic is for: not trusting that upstream code does what it says, confirming it.</p></blockquote><h2>Core Concept</h2><p><code>CriticAgent.review()</code> runs three checks against every draft answer: the text isn&#8217;t empty, the text is verifiably grounded in whatever passage it cites, and if synthesis flagged an entity mismatch, the caveat explaining it actually survived into the final draft. All three must pass for approval.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>The grounding check is the one worth sitting with: it doesn&#8217;t ask &#8220;did synthesis say this was extractive&#8221; &#8212; it directly checks whether <code>answer.text</code> appears verbatim inside the cited passage&#8217;s text. To prove this isn&#8217;t a rubber stamp, today&#8217;s code deliberately constructs a corrupted answer &#8212; text edited to say &#8220;90 days&#8221; and &#8220;guaranteed&#8221; where the real source says &#8220;30 days&#8221; and &#8220;no questions asked&#8221; &#8212; and runs it through the same critic. Verified by actually running it: the critic catches it, flags <code>grounded_in_source</code> as failed, and rejects the answer outright.</p><p>There&#8217;s also an honest middle case: when retrieval finds nothing relevant, synthesis returns a fallback message with no citations at all. That&#8217;s not a failure to catch &#8212; it&#8217;s the system correctly admitting it doesn&#8217;t know. The critic labels this <code>approved_fallback</code>, distinct from both a normal approval and a rejection, so the dashboard can show the difference between &#8220;this is a good answer&#8221; and &#8220;this is an honest non-answer.&#8221;</p><h2>Architecture</h2><p><code>CriticAgent</code> sits after <code>SynthesisAgent</code> and before formatting, taking a <code>DraftAnswer</code> and a lookup of passage ID to full text. It returns a <code>CriticVerdict</code>: an overall approval boolean, a list of individual <code>CheckResult</code> objects (one per check, each with its own pass/fail and reason), and a verdict label distinguishing normal approval, honest fallback, and outright rejection.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!T7Yx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 424w, /__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 848w, /__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 1272w, /__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!T7Yx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png" width="636" height="298.3434065934066" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:683,&quot;width&quot;:1456,&quot;resizeWidth&quot;:636,&quot;bytes&quot;:635632,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601305?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 424w, /__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 848w, /__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 1272w, /__u/substackcdn.com/image/fetch/$s_!T7Yx!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c2e65dd-1016-4839-8ff4-47860c86e096_4900x2300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;e8fa4339-2674-41a6-9b48-75952e92c05c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def review(self, answer: DraftAnswer, passage_lookup: dict) -&gt; CriticVerdict:
    checks = []
    checks.append(CheckResult("non_empty", bool(answer.text.strip())))

    is_fallback = len(answer.source_passage_ids) == 0
    grounded = True
    if not is_fallback:
        for passage_id in answer.source_passage_ids:
            if answer.text not in passage_lookup.get(passage_id, ""):
                grounded = False
    checks.append(CheckResult("grounded_in_source", grounded))

    caveat_ok = answer.entity_match or bool(answer.caveat.strip())
    checks.append(CheckResult("caveat_surfaced", caveat_ok))

    approved = all(c.passed for c in checks)
    ...
</code></pre></div><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;cdee4255-744c-464f-a82a-cbbfded3ae6e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p><code>start.sh</code> installs dependencies, builds the Docker image if available, runs the lesson, runs the tests, and generates <code>dashboard.html</code>. Four cards render: two clean approvals, one out-of-scope query skipped entirely, and one deliberately corrupted answer showing a red <code>REJECTED</code> badge with the specific failed check visible. Tests: <code>6 passed</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;85b14400-6831-4f0c-a9a2-8d7045421c56&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh
</code></pre></div><h1>Implementation Guide</h1><h2><strong>GitHub Link</strong></h2><p><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_08_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_08_package</a></p><h2>Package contents</h2><pre><code><code>lesson_08_package/
&#9500;&#9472;&#9472; lesson_code.py    # CriticAgent, CheckResult, CriticVerdict, generate_dashboard (+ Days 6-7's agents, Days 3-5's retrieval stack)
&#9500;&#9472;&#9472; test_lesson.py    # 6 tests verifying each check independently
&#9500;&#9472;&#9472; requirements.txt  # pytest only
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, dashboard.html, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference
</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;08b34386-655f-4686-8506-8f9c1d8a0b3e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_08_package
chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p>You should see <code>dashboard.html</code> generated, and <code>6 tests passed</code>. Open the dashboard &#8212; two green &#8220;approved&#8221; cards, one skipped out-of-scope card, and one red &#8220;REJECTED&#8221; card showing exactly which check failed.</p><p>Cleanup: <code>./stop.sh</code> &#8212; also removes the generated <code>dashboard.html</code>.</p><h2>Architecture, in more detail</h2><p><code>CriticAgent.review()</code> runs three independent checks and combines them with <code>all()</code> &#8212; every check must pass for <code>approved</code> to be <code>True</code>. Each check is recorded as its own <code>CheckResult</code>, not collapsed into a single boolean, specifically so the dashboard (and any future audit log) can show which check failed, not just that something did.</p><p>The <code>is_fallback</code> branch matters: when <code>source_passage_ids</code> is empty, there&#8217;s nothing to ground the answer against, so the grounding check is skipped rather than automatically failed. An honest &#8220;I don&#8217;t know&#8221; shouldn&#8217;t be penalized the same way a wrong answer is &#8212; this is a real design decision, not an oversight, reflected in the distinct <code>approved_fallback</code> label.</p><h2>Why the corrupted-answer example matters more than the two clean approvals</h2><p>Two clean approvals prove the critic doesn&#8217;t reject good answers. They don&#8217;t prove it catches bad ones &#8212; a critic that approves everything would pass those same two tests. The corrupted answer exists specifically to close that gap: it&#8217;s constructed by hand (<code>"90 days, guaranteed, no matter what"</code> in place of the real <code>"30 days ... no questions asked"</code>), cites a real passage ID, and is run through the exact same <code>CriticAgent.review()</code> call as everything else. The rejection isn&#8217;t scripted into the dashboard &#8212; it&#8217;s the actual, verified output of running the grounding check against text that genuinely doesn&#8217;t match its cited source.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!cV4d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 424w, /__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 848w, /__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!cV4d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png" width="658" height="295.5576923076923" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:654,&quot;width&quot;:1456,&quot;resizeWidth&quot;:658,&quot;bytes&quot;:754059,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601305?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 424w, /__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 848w, /__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cV4d!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39960c6a-7bd2-480d-8b8c-d34cb1d5ba5a_4900x2200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!c1hr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 424w, /__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 848w, /__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 1272w, /__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!c1hr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png" width="942" height="486" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:486,&quot;width&quot;:942,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:46305,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601305?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 424w, /__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 848w, /__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 1272w, /__u/substackcdn.com/image/fetch/$s_!c1hr!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b499d3d-1059-4300-a53f-36d2ec9635cf_942x486.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Working Demo Link:</h3><div id="youtube2-YBEekmRWvyI" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;YBEekmRWvyI&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/YBEekmRWvyI?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside <code>lesson_08_package</code>.</p></li><li><p><strong>Wondering why the corrupted answer isn&#8217;t constructed via a synthesis bug</strong> &#8212; it&#8217;s built directly as a <code>DraftAnswer</code> in <code>lesson_code.py</code>&#8216;s main block, simulating what a future bug&#8217;s <em>output</em> would look like, without needing to actually break <code>SynthesisAgent</code>&#8216;s working code to demonstrate it.</p></li><li><p><code>dashboard.html</code><strong> doesn&#8217;t open automatically</strong> &#8212; <code>start.sh</code>&#8216;s auto-open is best-effort and silently skipped in headless environments; open the file manually.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>This is the difference between a system that trusts its own components and one that verifies them &#8212; and it&#8217;s exactly the kind of check that catches problems no single-component test would, because it only fails when two components disagree about what actually happened. A production incident where an answer contradicts its own cited source is a genuinely hard failure to detect from the outside; verifying grounding at the point of generation is far cheaper than discovering it from a user complaint.</p></blockquote><h2>Next Steps</h2><p>Day 9 builds the <code>ResponseFormatter</code>, which takes an approved answer and shapes it for delivery &#8212; the last component before Days 10 through 12 wire the full five-piece pipeline together.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lesson 7: Building the SynthesisAgent]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-7-building-the-synthesisagent</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-7-building-the-synthesisagent</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Mon, 24 Aug 2026 08:30:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sPby!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>An extractive synthesis agent &#8212; answers built directly from retrieved text, never invented</p></li><li><p>Source citations on every answer, traceable back to a specific passage ID</p></li><li><p>An entity-mismatch check that adds a visible caveat instead of silently answering the wrong question</p></li></ul><h2>Why This Matters</h2><blockquote><p>Retrieval finds relevant passages; something still has to turn them into an answer. The obvious approach is to hand the passages to a model and let it write a response &#8212; but that reintroduces the exact problem Day 6 solved for classification: a fluent answer with no way to verify where any part of it came from. Today&#8217;s synthesis stays extractive on purpose: the answer <em>is</em> retrieved text, not a paraphrase of it, so grounding isn&#8217;t something to check later &#8212; it&#8217;s structurally guaranteed.</p></blockquote><h2>Core Concept</h2><p><code>SynthesisAgent.synthesize()</code> takes the top-ranked passage from retrieval and returns it as the answer, tagged with the passage ID it came from. No rewriting, no summarizing, no risk of the answer saying something the source passage doesn&#8217;t. That&#8217;s a real trade-off &#8212; extractive answers can read less naturally than generated ones &#8212; accepted deliberately for the traceability it buys.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>The agent also checks something classification alone can&#8217;t: whether the entity Day 6 extracted from the query actually appears in what got retrieved. Ask about the refund policy for a &#8220;lifetime&#8221; plan, and retrieval correctly finds the closest passage about refunds &#8212; but that passage is about annual plans, because no passage mentions lifetime plans at all. Verified by running the code, not assumed: the agent catches this and adds an explicit caveat, <code>"Requested plan type 'lifetime' was not found in the top result; showing the closest available match instead."</code> The answer still ships, but it ships honestly.</p><p>Queries Day 6 already routed <code>out_of_scope</code> never reach this agent at all &#8212; synthesis is skipped entirely, visible on the dashboard as a distinct, deliberate state rather than an empty result.</p><h2>Architecture</h2><p><code>SynthesisAgent</code> sits directly after the retrieval stack (Days 2&#8211;5) and consumes both its output and Day 6&#8217;s <code>Intent</code> object &#8212; specifically the extracted entities, which is the only piece of Day 6&#8217;s work this agent actually needs. The result is a <code>DraftAnswer</code>: text, source passage IDs, an entity-match boolean, and an optional caveat string.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!sPby!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 424w, /__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 848w, /__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!sPby!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png" width="630" height="295.52884615384613" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:683,&quot;width&quot;:1456,&quot;resizeWidth&quot;:630,&quot;bytes&quot;:668561,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601280?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 424w, /__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 848w, /__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sPby!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff882cf70-a747-47e2-84ef-65e1f74dd713_4900x2300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;42677863-2490-4f6e-8ebf-1ab2afbb8630&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def synthesize(self, intent: Intent, retrieved: list[RetrievedPassage]) -&gt; DraftAnswer:
    top = retrieved[0]
    plan_type = intent.entities.get("plan_type")

    entity_match = True
    caveat = ""
    if plan_type and plan_type not in top.text.lower():
        entity_match = False
        caveat = f"Requested plan type '{plan_type}' was not found in the top result; ..."

    return DraftAnswer(text=top.text, source_passage_ids=[top.passage_id],
                        entity_match=entity_match, caveat=caveat)
</code></pre></div><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;24bfd1a0-9ddd-4bab-a9a2-70e2f5267e2d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh</code></pre></div><p><code>start.sh</code> installs dependencies, builds the Docker image if available, runs the lesson, runs the tests, and generates <code>dashboard.html</code>. Five query cards render: three clean extractive answers with source citations, one visible entity-mismatch caveat, and one out-of-scope query shown as skipped rather than answered. Tests: <code>6 passed</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;745d2362-e4ec-4f0b-8dfd-357542f838f3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh</code></pre></div><h1>Implementation Guide</h1><h2><strong>GitHub Link</strong></h2><p><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_07_package/lesson_07_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_07_package/lesson_07_package</a></p><h2>Package contents</h2><pre><code><code>lesson_07_package/
&#9500;&#9472;&#9472; lesson_code.py    # SynthesisAgent, DraftAnswer, generate_dashboard (+ Day 6's QueryUnderstandingAgent, Days 3-5's retrieval stack)
&#9500;&#9472;&#9472; test_lesson.py    # 6 tests verifying extraction, citations, and caveats
&#9500;&#9472;&#9472; requirements.txt  # pytest only
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, dashboard.html, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference
</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;82505f78-805f-46aa-9f8e-8e8145ca2ed4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_07_package
chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p>You should see <code>dashboard.html</code> generated, and <code>6 tests passed</code>. Open the dashboard &#8212; five query cards, three with clean matched answers, one showing a visible entity-mismatch caveat, one shown as skipped (out of scope).</p><p>Cleanup: <code>./stop.sh</code> &#8212; also removes the generated <code>dashboard.html</code>.</p><h2>Architecture, in more detail</h2><p><code>SynthesisAgent.synthesize()</code> takes exactly two inputs: an <code>Intent</code> (from Day 6) and a list of <code>RetrievedPassage</code> objects (from the Days 3&#8211;5 retrieval stack). It doesn&#8217;t call either component directly &#8212; the pipeline wiring in <code>if __name__ == "__main__":</code> is responsible for calling <code>QueryUnderstandingAgent</code>, checking <code>in_scope</code>, and only calling <code>HybridSearcher</code> and <code>SynthesisAgent</code> when the query passed Day 6&#8217;s gate. This keeps each agent testable in isolation, the same discipline established on Day 1.</p><p>The entity-match check only fires when <code>intent.entities</code> contains a <code>plan_type</code> &#8212; if the query has no extracted entity (like the plain &#8220;How do I cancel my subscription?&#8221; query), there&#8217;s nothing to check against, and <code>entity_match</code> defaults to <code>True</code> with no caveat.</p><h2>Why extractive synthesis, specifically, and what it costs</h2><p>An extractive answer is bounded to exactly what a source passage says &#8212; there is no code path in <code>synthesize()</code> that can produce text absent from <code>retrieved</code>. That&#8217;s the entire safety argument: verify the source passages are correct once, and every answer built from them inherits that correctness automatically.</p><p>The real cost: extractive answers can read awkwardly, especially when a query would ideally combine facts from two different passages into one sentence. Today&#8217;s agent uses only the single top-ranked passage &#8212; it doesn&#8217;t attempt to combine multiple passages into a smoother answer. That&#8217;s a genuine limitation, not hidden here: a more capable synthesis approach (bullet points from multiple sources, or eventually model-generated text with citations) is a real next step, deliberately deferred until Phase 1&#8217;s eval harness exists to measure whether a more complex approach is actually more correct, not just more fluent.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!FEsA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!FEsA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png" width="636" height="272.57142857142856" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1456,&quot;resizeWidth&quot;:636,&quot;bytes&quot;:572117,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601280?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!FEsA!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb264c80-07a2-4c1d-b19d-e6526adb3d41_4900x2100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Reading the entity-mismatch example honestly</h2><p>The &#8220;lifetime plan&#8221; query wasn&#8217;t picked because it was guaranteed to fail &#8212; several queries were tested first, and most matched cleanly. This one triggers the caveat because the mock passage set genuinely contains no passage mentioning &#8220;lifetime,&#8221; so retrieval correctly falls back to the closest related passage (an annual-plan refund policy) rather than returning nothing. The caveat exists precisely for this situation: retrieval did its job &#8212; it found the most similar available passage &#8212; but similarity isn&#8217;t the same as correctness, and the agent says so instead of hiding the gap.</p><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!1Ikm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 424w, /__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 848w, /__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!1Ikm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png" width="910" height="507" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:507,&quot;width&quot;:910,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:49757,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209601280?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 424w, /__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 848w, /__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1Ikm!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dacfb8a-feb6-4b95-89da-7d1d220942d9_910x507.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Working Demo Link:</h3><div id="youtube2-Y3s7_enzT38" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;Y3s7_enzT38&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/Y3s7_enzT38?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside <code>lesson_07_package</code>.</p></li><li><p><strong>A query you try yourself doesn&#8217;t trigger a caveat you expected</strong> &#8212; the entity-match check only compares against the <em>top</em> retrieved passage; if a lower-ranked passage happens to mention the entity, that&#8217;s not currently checked. That&#8217;s a real, acknowledged limitation, not a bug.</p></li><li><p><code>dashboard.html</code><strong> doesn&#8217;t open automatically</strong> &#8212; <code>start.sh</code>&#8216;s auto-open is best-effort and silently skipped in headless environments; open the file manually.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>Extractive-first synthesis, with generation reserved for later once evaluation and guardrails exist, is a common production pattern for exactly this reason &#8212; it&#8217;s far easier to audit a system that can only say what its sources say than one that can say anything at all. The caveat behavior mirrors a real support pattern too: a good agent, human or automated, says &#8220;I found something related but not exactly what you asked&#8221; instead of quietly answering an adjacent question as if it were the one asked.</p></blockquote><h2>Next Steps</h2><p>Day 8 builds the <code>CriticAgent</code>, which reviews this draft answer before it reaches a user &#8212; the last checkpoint in the pipeline before formatting and delivery.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lesson 6: Building the QueryUnderstandingAgent]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-6-building-the-queryunderstandingagent</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-6-building-the-queryunderstandingagent</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Thu, 20 Aug 2026 08:30:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!qL43!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>A weighted, multi-category classifier replacing Day 1&#8217;s single-keyword stub</p></li><li><p>Entity extraction that pulls structured facts out of a raw question</p></li><li><p>An explicit confidence threshold that routes uncertain queries away from retrieval instead of guessing</p></li></ul><h2>Why This Matters</h2><blockquote><p>Day 1&#8217;s <code>QueryUnderstandingStage</code> picked the first keyword substring it found and called it done &#8212; no confidence, no way to say &#8220;I&#8217;m not sure.&#8221; That&#8217;s fine for a proof of concept; it&#8217;s a liability in production, where a system that confidently misclassifies a query has already made its first mistake before retrieval even runs. Today&#8217;s agent is the first component in the pipeline built to know the difference between &#8220;confident&#8221; and &#8220;guessing.&#8221;</p></blockquote><h2>Core Concept</h2><p>Instead of a first-match rule, the agent scores every category against the query using weighted keywords, then normalizes those scores into a probability-like distribution that sums to 1.0. The category with the highest share wins &#8212; but only if that share clears a confidence threshold. Below the threshold, the agent doesn&#8217;t guess; it returns <code>out_of_scope</code> and the query never reaches retrieval at all.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><blockquote><p>This matters because ambiguity is real, not a bug to eliminate. Run today&#8217;s code on &#8220;Can I get a refund if I cancel my annual plan?&#8221; and the agent produces a genuine tie: <code>refund_policy</code> and <code>cancellation</code> score exactly 0.5 each. A more nuanced case &#8212; &#8220;What is the cost to cancel and get a refund on my purchase?&#8221; &#8212; splits three ways: 0.44 / 0.33 / 0.22. The confidence score isn&#8217;t a made-up number; it&#8217;s a direct, honest readout of how much the query actually leans toward one category over the others.</p></blockquote><p>Entity extraction runs alongside classification, pulling out details like plan type (&#8221;annual,&#8221; &#8220;monthly,&#8221; &#8220;lifetime&#8221;) whenever they&#8217;re mentioned &#8212; structured facts the retrieval stage can use later, instead of re-parsing the raw text.</p><h2>Architecture</h2><p><code>QueryUnderstandingAgent.process()</code> is the pipeline&#8217;s first real seam: raw text goes in, a structured <code>Intent</code> object comes out, carrying the winning category, its confidence, the full score breakdown across every category, extracted entities, and an explicit <code>in_scope</code> boolean. Everything downstream &#8212; retrieval, synthesis, the critic &#8212; can trust that boolean instead of re-deriving it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!qL43!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!qL43!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png" width="618" height="302.63324175824175" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:713,&quot;width&quot;:1456,&quot;resizeWidth&quot;:618,&quot;bytes&quot;:675607,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209350324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!qL43!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9059ffbe-6c44-4ea5-8eda-1bd779e1edfc_4900x2400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;54e414bd-373c-445b-95a3-1185bc078c7e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def process(self, query_text: str) -&gt; Intent:
    category_scores = self._score_categories(query_text.lower())
    best_category = max(category_scores, key=category_scores.get)
    best_confidence = category_scores[best_category]

    if best_confidence &lt; CONFIDENCE_THRESHOLD:
        return Intent(query_text=query_text, intent_label="out_of_scope",
                       confidence=best_confidence, in_scope=False, ...)

    return Intent(query_text=query_text, intent_label=best_category,
                   confidence=best_confidence, in_scope=True, ...)
</code></pre></div><p>The threshold (<code>0.34</code>) isn&#8217;t tuned against real data yet &#8212; there&#8217;s no eval harness to tune it against until Phase 1. Today it&#8217;s a reasonable default, made visible and adjustable in one place rather than buried in scattered if-statements.</p><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;a58e85b3-574d-4b30-b076-8c2f4794bb63&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p><code>start.sh</code> installs dependencies, builds the Docker image if available, runs the lesson, runs the tests, and generates <code>dashboard.html</code>. Five test queries render as cards &#8212; three clean classifications, one genuine tie, and one out-of-scope weather question correctly routed away from retrieval. Tests: <code>6 passed</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;e9b51a4f-a16f-435b-ad82-ae7cecf10300&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh
</code></pre></div><h1>Implementation Guide </h1><h2>GitHub Link</h2><p><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_06_package/lesson_06_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_06_package/lesson_06_package</a></p><h2>Package contents</h2><pre><code><code>lesson_06_package/
&#9500;&#9472;&#9472; lesson_code.py    # QueryUnderstandingAgent, Intent, generate_dashboard
&#9500;&#9472;&#9472; test_lesson.py    # 6 tests verifying classification, entities, and scope routing
&#9500;&#9472;&#9472; requirements.txt  # pytest only
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, dashboard.html, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference
</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;999f8c50-02f2-4971-afe2-b90602506960&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_06_package
chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p>You should see <code>dashboard.html</code> generated, and <code>6 tests passed</code>. Open the dashboard &#8212; five query cards, each with category confidence bars, extracted plan-type entities where relevant, and a scope badge.</p><p>Cleanup: <code>./stop.sh</code> &#8212; also removes the generated <code>dashboard.html</code>.</p><h2>Architecture, in more detail</h2><p><code>_score_categories()</code> sums keyword weights per category, then divides each category&#8217;s raw score by the total across all categories &#8212; this normalization is what makes <code>category_scores</code> sum to 1.0 and behave like a confidence distribution rather than an arbitrary number.</p><p><code>_extract_entities()</code> runs independently of classification &#8212; a query can be <code>out_of_scope</code> and still have an entity extracted, or <code>in_scope</code> with no entities found at all. The two operations don&#8217;t depend on each other.</p><p><code>process()</code> combines both, then makes the single most consequential decision in the agent: compare the winning category&#8217;s confidence against <code>CONFIDENCE_THRESHOLD</code> (0.34) and decide whether the query proceeds to retrieval or gets labeled <code>out_of_scope</code> and stops here.</p><h2>Why the confidence threshold matters more than the classification itself</h2><p>Anyone can write a classifier that always returns its best guess. The harder, more valuable behavior is knowing when <em>not</em> to guess. With only three categories, a genuinely ambiguous query can score as low as 0.33 per category in a perfect three-way split &#8212; which is why the threshold sits at 0.34, just above an even three-way tie. A query that&#8217;s genuinely torn between all three categories gets routed to <code>out_of_scope</code> rather than picking one arbitrarily; a query that clearly leans toward one category, even without total certainty, still gets through.</p><p>This threshold is a placeholder default, not a tuned value &#8212; there&#8217;s no eval harness yet (that starts in Phase 1) to measure whether 0.34 is actually the right cutoff for real queries. Making it a single named constant, rather than scattering the logic across the codebase, is what makes it possible to tune later without a rewrite.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-LOv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-LOv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png" width="644" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1456,&quot;resizeWidth&quot;:644,&quot;bytes&quot;:722420,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209350324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-LOv!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cd5c730-7424-4ef2-911e-aef48b3e948f_4900x2100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!CzUE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 424w, /__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 848w, /__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!CzUE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png" width="694" height="392.2608695652174" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:507,&quot;width&quot;:897,&quot;resizeWidth&quot;:694,&quot;bytes&quot;:47117,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209350324?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 424w, /__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 848w, /__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CzUE!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F270e975c-40a6-4e6e-8503-38957d8f1096_897x507.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Working demo link:</h3><div id="youtube2-uKT8nJjCkAI" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;uKT8nJjCkAI&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/uKT8nJjCkAI?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside <code>lesson_06_package</code>.</p></li><li><p><strong>A query you try yourself classifies unexpectedly</strong> &#8212; check <code>CATEGORY_KEYWORDS</code> directly; this is a small, hand-written keyword list, not a trained model, so it only recognizes what&#8217;s explicitly listed.</p></li><li><p><code>dashboard.html</code><strong> doesn&#8217;t open automatically</strong> &#8212; <code>start.sh</code>&#8216;s auto-open is best-effort and silently skipped in headless environments; open the file manually.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>Systems that skip explicit out-of-scope handling tend to fail the same way: a user asks something the system was never built to answer, and instead of saying so, it confidently retrieves the closest-sounding passage and answers anyway. That&#8217;s a harder failure to catch than an outright error, because nothing in the response signals anything went wrong. An explicit confidence floor is one of the cheapest reliability wins available, and it&#8217;s why it comes this early in the pipeline.</p></blockquote><h2>Next Steps</h2><p>Day 7 builds the <code>SynthesisAgent</code>, which takes the retrieval results this agent&#8217;s <code>in_scope</code> decision permits and turns them into a drafted answer.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lesson 5: Reranking — Improving on Raw Retrieval Order]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-5-reranking-improving-on-raw</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-5-reranking-improving-on-raw</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Mon, 17 Aug 2026 08:31:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!9Qz-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>A bigram-overlap reranker &#8212; a phrase-level signal neither BM25 nor vector similarity can see</p></li><li><p>A second pass over Day 4&#8217;s hybrid search candidates</p></li><li><p>A visual dashboard showing exactly which passages moved, and why</p></li></ul><h2>Why This Matters</h2><blockquote><p>Both of Day 4&#8217;s scorers &#8212; BM25 and vector similarity &#8212; treat a passage as a bag of independent words. Neither can tell the difference between &#8220;cancel at any time&#8221; and &#8220;at any point you may cancel,&#8221; even though only one of those actually contains the phrase a user asked about. Reranking exists because word order carries real information, and it&#8217;s cheap to check once the candidate pool is already small.</p></blockquote><h2>Core Concept</h2><p><strong>Bigram overlap</strong> scores a query against a passage by comparing their two-word sequences, not their individual words. &#8220;Cancel at any time&#8221; produces the bigrams <code>(cancel, at)</code>, <code>(at, any)</code>, <code>(any, time)</code> &#8212; a passage containing that exact phrase shares all three; a passage with the same words scrambled shares none.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This is a real, order-sensitive reranking signal, distinct from anything in the retrieval stage. It&#8217;s also deliberately cheap enough to run on only the top handful of candidates &#8212; reranking every passage in a large corpus this way wouldn&#8217;t scale, but reranking Day 4&#8217;s top 3 costs nothing.</p><p>Running today&#8217;s code on the query &#8220;cancel at any time&#8221; produces a genuine, verified rank change: hybrid search ranks <code>doc_cancel_p0</code> first and <code>doc_refund_p1</code> second, because <code>doc_cancel_p0</code> shares more individual words overall. But <code>doc_refund_p1</code> contains the exact phrase &#8220;cancel at any time&#8221; verbatim, while <code>doc_cancel_p0</code> only has &#8220;cancel <em>your subscription</em> at any time&#8221; &#8212; the phrase is broken up. Reranking correctly promotes <code>doc_refund_p1</code> to first place. That&#8217;s not a scripted outcome; it&#8217;s what the bigram math actually produces on this query.</p><h2>Architecture</h2><p><code>Reranker.rerank()</code> takes Day 4&#8217;s <code>HybridSearcher</code> output directly &#8212; it doesn&#8217;t touch the passage store or recompute anything from scratch. It scores each candidate&#8217;s bigram overlap against the query, re-sorts by that score, and records both the original hybrid rank and the new final rank, so the dashboard can show the movement explicitly.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!9Qz-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!9Qz-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png" width="552" height="270.3131868131868" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:713,&quot;width&quot;:1456,&quot;resizeWidth&quot;:552,&quot;bytes&quot;:733089,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209346335?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9Qz-!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cf1d59a-3ae3-4fb8-98d2-433647053020_4900x2400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;fcf543bf-82d0-4dad-92a9-c613a863c130&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def bigram_overlap_score(query: str, passage_text: str) -&gt; float:
    query_bigrams = bigrams(tokenize_ordered(query))
    passage_bigrams = bigrams(tokenize_ordered(passage_text))
    intersection = query_bigrams &amp; passage_bigrams
    union = query_bigrams | passage_bigrams
    return len(intersection) / len(union)</code></pre></div><p>Note <code>tokenize_ordered()</code> deliberately keeps stopwords that Day 3 and Day 4&#8217;s tokenizer dropped &#8212; bigrams need real word adjacency to mean anything; filtering out &#8220;at&#8221; before forming pairs would destroy the exact phrase this lesson depends on.</p><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;3292fbe1-78c8-42e5-9ed9-2b77f48fbaaf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh</code></pre></div><p><code>start.sh</code> installs dependencies, builds the Docker image if available, runs the lesson, runs the tests, and generates <code>dashboard.html</code>. Each passage renders with a rank badge &#8212; moved up, moved down, or unchanged &#8212; plus its bigram and hybrid scores side by side. Tests: <code>6 passed</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;b114d55a-e8d4-4051-91c3-725123700583&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh</code></pre></div><h1>Implementation Guide</h1><h2><strong>GitHub Link</strong></h2><p><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_05_package/lesson_05_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_05_package/lesson_05_package</a></p><h2>Package contents</h2><pre><code><code>lesson_05_package/
&#9500;&#9472;&#9472; lesson_code.py    # bigram scoring, Reranker, generate_dashboard (+ Day 4's HybridSearcher, BM25, embeddings)
&#9500;&#9472;&#9472; test_lesson.py    # 6 tests verifying bigram scoring and rank changes
&#9500;&#9472;&#9472; requirements.txt  # pytest only
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, dashboard.html, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference
</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;1615770a-f7ce-4bc0-bf95-3185a44cfa22&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_05_package
chmod +x start.sh stop.sh
./start.sh</code></pre></div><p>You should see <code>dashboard.html</code> generated, and <code>6 tests passed</code>. Open the dashboard &#8212; <code>doc_refund_p1</code> should show a &#8220;moved up 1&#8221; badge, <code>doc_cancel_p0</code> a &#8220;moved down 1&#8221; badge.</p><p>Cleanup: <code>./stop.sh</code> &#8212; also removes the generated <code>dashboard.html</code>.</p><h2>Architecture, in more detail</h2><p>Two tokenizers exist in this lesson for a specific reason:</p><ul><li><p><code>tokenize()</code> (from Day 3/Day 4) strips stopwords, used for BM25 and embeddings, where individual word presence is what matters.</p></li><li><p><code>tokenize_ordered()</code> (new today) keeps every word, including stopwords, because bigrams need genuine adjacency &#8212; dropping &#8220;at&#8221; from &#8220;cancel at any time&#8221; would turn it into &#8220;cancel any time,&#8221; destroying the exact phrase match this lesson depends on.</p></li></ul><p><code>Reranker.rerank()</code> takes <code>HybridResult</code> objects directly from <code>HybridSearcher.search()</code>, computes <code>bigram_overlap_score()</code> for each, and re-sorts. It records <code>hybrid_rank</code> (the incoming order) and <code>final_rank</code> (the outgoing order) on every result, which is what lets the dashboard show movement rather than just a final list.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!20K_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!20K_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png" width="670" height="287.14285714285717" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1456,&quot;resizeWidth&quot;:670,&quot;bytes&quot;:542038,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209346335?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!20K_!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df076a-7c4d-4912-8349-358de1edbb02_4900x2100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Why this specific query demonstrates the point honestly</h2><p>The query &#8220;cancel at any time&#8221; was chosen after testing several candidate queries and checking the actual output &#8212; not written first and assumed to work. Most queries tested left the hybrid order unchanged after reranking; this one produces a genuine flip because it&#8217;s constructed from an exact phrase that appears verbatim in one passage (<code>doc_refund_p1</code>: &#8220;...but you can cancel at any time&#8221;) and only in scrambled form in another (<code>doc_cancel_p0</code>: &#8220;...cancel your subscription at any time&#8221;). Reranking doesn&#8217;t guarantee a rank change on every query &#8212; it only matters when phrase structure and bag-of-words agreement genuinely diverge, which is exactly what happened here.</p><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Kezw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 424w, /__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 848w, /__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Kezw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png" width="912" height="426" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:426,&quot;width&quot;:912,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35420,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/209346335?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 424w, /__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 848w, /__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Kezw!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F510adffb-a7d8-41d0-8175-e352e4c81bb7_912x426.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Working Demo Link:</h3><div id="youtube2-76-N78zOmis" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;76-N78zOmis&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/76-N78zOmis?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside <code>lesson_05_package</code>.</p></li><li><p><strong>No rank change visible for a query you try yourself</strong> &#8212; expected for most queries; the demo query was specifically chosen to show a genuine flip, not because reranking always changes the order.</p></li><li><p><code>dashboard.html</code><strong> doesn&#8217;t open automatically</strong> &#8212; <code>start.sh</code>&#8216;s auto-open is best-effort and silently skipped in headless environments; open the file manually.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>This two-stage pattern &#8212; a cheap, broad first pass followed by an expensive, narrow second pass &#8212; is the standard shape of production retrieval systems, usually with a trained cross-encoder model instead of today&#8217;s bigram heuristic. The principle is identical either way: don&#8217;t pay the cost of precise scoring on every passage in the corpus, only on the handful that already made it through the first cut.</p></blockquote><h2>Next Steps</h2><p>Day 6 begins Phase 0&#8217;s agent build: the <code>QueryUnderstandingAgent</code>, the first of the five components that will call this retrieval stack as part of a complete pipeline.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Week 25-26: Natural Language Processing (NLP) ]]></title><description><![CDATA[NLP Intelligence Platform: From Fragmented NLP Scripts to a Unified Production Stack]]></description><link>https://aieworks.substack.com/p/week-25-26-natural-language-processing</link><guid isPermaLink="false">https://aieworks.substack.com/p/week-25-26-natural-language-processing</guid><dc:creator><![CDATA[sdr]]></dc:creator><pubDate>Fri, 14 Aug 2026 08:30:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hw3Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e47a1ed-f52b-4cc9-adb7-ec74dc78fef2_2880x2240.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Introduction</strong></h2><blockquote><p>Most NLP tutorials stop at isolated notebooks: one file for tokenization, another for sentiment, a third for intent, none wired to HTTP, persistence, or observability. <code>week_25_26_aiml_integrated_project</code> is a self-contained capstone that consolidates classical text processing, transformer tokenization, embeddings, sequence modeling, sentiment analysis, intent routing, and response selection into one runnable system:</p></blockquote><ul><li><p>An installable Python package (<code>week2526_python</code>) with a <strong>core</strong> engine, <strong>learning</strong> lab wrappers, and a <strong>product</strong> NLP pipeline</p></li><li><p>A single FastAPI backend exposing a <strong>lab path</strong> for component exploration and a <strong>product path</strong> for analyze, predict, and experiment training</p></li><li><p>A React dashboard with live telemetry, a support-assistant workflow, a pipeline studio, and an operations console</p></li><li><p>Postgres-backed experiment metadata, PyTorch artifacts on disk, and production hardening (timeouts, structured logging, graceful ML degradation)</p></li></ul><blockquote><p>The engineer payoff is operational clarity: one <code>/analyze</code> call runs preprocess &#8594; intent &#8594; sentiment &#8594; response; training jobs become <code>Run</code> rows you poll from the dashboard; missing models fall back to lexicon and keyword rules instead of crashing the API.</p></blockquote><div><hr></div><h2><strong>Core Components</strong></h2><p>Each block below is a distinct capability inside this repository&#8212;not an external dependency.</p><h3><strong>Classical text pipeline</strong></h3><p><code>week2526_python.core.text_pipeline</code> provides NLTK-backed normalization, tokenization, stemming, lemmatization, POS tagging, and TF-IDF feature extraction. <code>nltk_bootstrap.py</code> lazily downloads punkt and stopwords only when classical processing runs, keeping cold starts fast.</p><h3><strong>Subword tokenization</strong></h3><p><code>core.tokenization.distilbert</code> and <code>core.tokenization.subword</code> wrap Hugging Face tokenizers (DistilBERT, BERT, GPT-2). Lab endpoints expose side-by-side token comparisons without loading full models on every health check.</p><h3><strong>Word embeddings</strong></h3><p><code>core.embeddings.glove</code> loads GloVe vectors or falls back to a deterministic synthetic vocabulary for offline demos. <code>glove_bootstrap.py</code> caches vector files under <code>GLOVE_CACHE_DIR</code> on startup when <code>WEEK2526_BOOTSTRAP_MISSING=true</code>.</p><h3><strong>Character sequence model</strong></h3><p><code>core.sequence_models.char_lstm</code> implements a character-level LSTM for text generation. Checkpoints live under <code>artifacts/sequence/char_lstm.pt</code> after a lab train call.</p><h3><strong>Sentiment stack</strong></h3><p>The sentiment module spans vocabulary building (<code>vocabulary.py</code>), Bi-LSTM architecture (<code>model.py</code>), training (<code>training.py</code>), lexicon scoring (<code>lexicon.py</code>), and inference (<code>inference.py</code>). Artifacts: <code>artifacts/sentiment/best_model.pt</code>, <code>vocab.json</code>, and <code>metrics.json</code>.</p><h3><strong>Intent classification</strong></h3><p><code>core.intent.classifier</code> trains a DistilBERT head over six support intents. <code>core.intent.lightweight</code> provides keyword-based routing when no checkpoint exists or when <code>WEEK2526_LIGHTWEIGHT_MODE=true</code>. Checkpoint path: <code>artifacts/intent/intent_classifier.pt</code>.</p><h3><strong>Response selection</strong></h3><p><code>core.response_selection.selector</code> maps intent + sentiment score to template replies, optionally reranking with embedding similarity when ML artifacts are present.</p><h3><strong>Product NLP engine</strong></h3><p><code>core.nlp_engine.pipeline</code> implements <code>analyze_text</code>, <code>predict_sentiment_product</code>, <code>system_status</code>, and <code>bootstrap_missing_assets</code>. This is the orchestration layer the product API calls through <code>app.services.model_loader</code>.</p><h3><strong>Model registry loader</strong></h3><p><code>backend/app/services/model_loader.py</code> lazy-loads sentiment and intent models behind a thread-safe registry, exposes <code>readiness()</code>, and never raises on missing artifacts&#8212;lexicon and lightweight intent are used instead.</p><h3><strong>Experiment and training orchestration</strong></h3><p>SQLAlchemy models (<code>Experiment</code>, <code>Run</code>, <code>Artifact</code>) plus async repositories manage product training. <code>nlp_train_job.py</code> schedules background jobs with <code>asyncio.create_task</code> and supports synchronous training via <code>?sync=true</code>.</p><h3><strong>FastAPI surface and middleware</strong></h3><p><code>backend/app/main.py</code> defines the only <code>FastAPI()</code> instance. Middleware includes request timeouts (30s default, 600s for long train routes), upload size limits (<code>WEEK2526_MAX_UPLOAD_MB</code>), JSON request logging with <code>request_id</code> and <code>latency_ms</code>, and environment-aware CORS.</p><h3><strong>React dashboard</strong></h3><p>The frontend (<code>frontend/src/ui/</code>) ships four views&#8212;Overview, Support Assistant, Pipeline Studio, and Operations&#8212;with sidebar navigation, live metric polling (4s global, 3s during active training runs), animated pipeline progress on analyze, and module cards grouped by topic (text processing, embeddings, sentiment, intent and response).</p><div><hr></div><h2><strong>Architecture Overview</strong></h2><h3><strong>Surface table</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!dXqi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 424w, /__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 848w, /__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 1272w, /__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!dXqi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png" width="1295" height="292" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:292,&quot;width&quot;:1295,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:48722,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/201552720?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 424w, /__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 848w, /__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 1272w, /__u/substackcdn.com/image/fetch/$s_!dXqi!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60df32eb-b37a-40e7-9d82-7fc569a37175_1295x292.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>http://localhost:3000</p><p>OperatorsMulti-panel NLP console proxied through Nginx</p><h3><strong>Architecture diagram</strong></h3><p>The diagram below shows how the dashboard, API layer, Python package tiers, and persistence volumes connect. External caches (NLTK, Hugging Face) feed the core package without blocking the product path in lightweight mode.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xW4P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 424w, /__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 848w, /__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xW4P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png" width="1456" height="823" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:823,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:431244,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/201552720?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 424w, /__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 848w, /__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xW4P!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46c30e80-47ba-43d0-bb2f-3900ff14569b_3680x2080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="/__u/aieworks.substack.com/p/week-25-26-natural-language-processing">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Lesson 4: Hybrid Search — Combining Keyword and Vector Retrieval]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-4-hybrid-search-combining</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-4-hybrid-search-combining</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Thu, 13 Aug 2026 08:30:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Hc53!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>A BM25 keyword scorer &#8212; the same ranking function real search engines use</p></li><li><p>A hybrid ranker that blends it with Day 3&#8217;s vector similarity</p></li><li><p>The fix for misranking, and an honest look at where hybrid search has its own blind spot</p></li></ul><h2>Why This Matters</h2><blockquote><p>Vector similarity alone ranked an unrelated passage above the actual refund policy for a refund question &#8212; a real failure, not a contrived one. Vectors capture fuzzy semantic overlap; they don&#8217;t guarantee the passage containing the exact term you asked about ranks first. Keyword search has the opposite problem: it can&#8217;t tell &#8220;cancel&#8221; and &#8220;terminate&#8221; mean the same thing. Today we stop choosing between them.</p></blockquote><h2>Core Concept</h2><p><strong>BM25</strong> scores a passage against a query using term frequency, weighted by how rare that term is across the whole passage set &#8212; a term every passage contains tells you nothing; a term only one passage contains is a strong signal. This is the same core algorithm behind most production keyword search, not a simplified stand-in for it.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><blockquote><p>Blending BM25 with vector similarity isn&#8217;t as simple as adding the two scores &#8212; they live on different scales. So both get <strong>min-max normalized</strong> into a 0&#8211;1 range first, then combined with a weighted average controlled by <code>alpha</code> (0.5 splits the weight evenly). The result: a passage that wins decisively on either signal still ranks well, and a passage that&#8217;s mediocre on both doesn&#8217;t sneak to the top the way it could with vectors alone.</p></blockquote><blockquote><p>Run today&#8217;s code against the refund-policy query from Day 3, and the fix is visible directly: <code>doc_refund_p0</code> &#8212; the actual refund policy &#8212; now ranks first, winning on both keyword match and vector similarity. That&#8217;s the win. But hybrid search isn&#8217;t a universal fix &#8212; it&#8217;s two imperfect signals combined, not one perfect one. A query built around a single rare word that happens to appear in an unrelated passage can still pull BM25 (and therefore the blend) in the wrong direction. Today&#8217;s code makes that failure mode visible too, not just the success.</p></blockquote><h2>Architecture</h2><p><code>HybridSearcher</code> owns both scorers: a <code>BM25</code> index built once over the passage set, and Day 3&#8217;s <code>embed_text()</code> reused unchanged. <code>search()</code> scores a query against both, normalizes each score list independently, then blends them into one ranked list.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Hc53!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Hc53!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png" width="588" height="287.9423076923077" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:713,&quot;width&quot;:1456,&quot;resizeWidth&quot;:588,&quot;bytes&quot;:715090,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208933037?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Hc53!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5170f0c-4208-49e3-bcf6-8820f76e4ef5_4900x2400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><h2><strong>GitHub Link</strong></h2><p><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_04_package/lesson_04_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_04_package/lesson_04_package</a></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;bb5be358-ab3e-46d2-ae69-ae7a82af7905&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def search(self, query: str, top_k: int = 3) -&gt; list[HybridResult]:
    keyword_scores = self.bm25.score(query)
    vector_scores = [cosine_similarity(embed_text(query), v) for v in self.passage_vectors]

    norm_keyword = normalize(keyword_scores)
    norm_vector = normalize(vector_scores)

    combined = [
        self.alpha * norm_vector[i] + (1 - self.alpha) * norm_keyword[i]
        for i in range(len(self.passages))
    ]
    ...</code></pre></div><p><code>alpha</code> is a real tuning knob, not a formality &#8212; Phase 1&#8217;s eval harness (starting Day 13) is what eventually tells you the right value for your own corpus, rather than guessing.</p><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;002c9dd8-6048-49b5-9569-24c057fc8d57&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh</code></pre></div><p><code>start.sh</code> installs dependencies, builds the Docker image if available, runs the lesson, runs the tests, and generates <code>dashboard.html</code> &#8212; open it in your browser to see the result, not a terminal log. Each passage renders as a card with three color-coded score bars: combined (green), keyword/BM25 (orange), and vector (blue), ranked top to bottom.</p><p>For this lesson&#8217;s query, <code>doc_refund_p0</code> &#8212; the actual refund policy &#8212; renders at the top with all three bars near full, while the two unrelated passages visibly trail behind it. Tests: <code>8 passed</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;9f2a20cc-1a0e-41a2-93bf-7e49fe327026&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh</code></pre></div><h1>Implementation Guide</h1><h2>Package contents</h2><pre><code><code>lesson_04_package/
&#9500;&#9472;&#9472; lesson_code.py    # BM25, HybridSearcher, normalize, generate_dashboard (+ Day 3's embed_text, cosine_similarity)
&#9500;&#9472;&#9472; test_lesson.py    # 8 tests verifying BM25, hybrid blending, and dashboard output
&#9500;&#9472;&#9472; requirements.txt  # pytest only
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, dashboard.html, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;be4e3d29-7e1f-4a4d-954d-9cf0367b6fe8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_04_package
chmod +x start.sh stop.sh
./start.sh</code></pre></div><p>You should see <code>dashboard.html</code> generated in the directory, and <code>8 tests passed</code>. Open the dashboard in your browser &#8212; <code>doc_refund_p0</code> should render at the top with the fullest bars across all three scores for the refund-policy query.</p><p>When you&#8217;re done: <code>./stop.sh</code> &#8212; also removes the generated <code>dashboard.html</code>.</p><h2>Architecture, in more detail</h2><p><code>BM25.__init__()</code> builds a document-frequency index once, over the whole passage set &#8212; how many passages contain each term. <code>BM25.score(query)</code> uses that index to score every passage against a query&#8217;s tokens, using the standard BM25 formula (<code>k1=1.5</code>, <code>b=0.75</code> &#8212; the commonly used defaults).</p><p><code>HybridSearcher</code> wraps a <code>BM25</code> instance and a set of Day 3 embeddings computed once at construction time. <code>search()</code> scores both, min-max normalizes each list independently (so neither signal dominates just because its raw numbers happen to be larger), and blends them with <code>alpha</code>.</p><h2>Why normalize before blending, specifically</h2><p>BM25 scores and cosine similarities live on completely different numeric ranges &#8212; BM25 can produce values like <code>3.7</code>, cosine similarity is bounded to <code>[-1, 1]</code>. Adding them directly would let BM25&#8217;s larger raw numbers dominate the blend regardless of <code>alpha</code>. Min-max normalization rescales each list to <code>[0, 1]</code> independently first, so <code>alpha</code> actually controls the blend the way it&#8217;s meant to.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!5yJI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!5yJI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png" width="574" height="246" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1456,&quot;resizeWidth&quot;:574,&quot;bytes&quot;:627863,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208933037?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5yJI!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff07a4773-76b5-47ac-a939-96e96bb2a3f1_4900x2100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Reading the real results honestly</h2><p>For the refund-policy query, hybrid search works cleanly &#8212; <code>doc_refund_p0</code> wins on both signals and ranks first. That&#8217;s a genuine fix for Day 3&#8217;s misranking, verified by running the code, not asserted.</p><p>It&#8217;s worth testing other queries yourself, because hybrid search isn&#8217;t a universal fix. BM25&#8217;s IDF weighting means a single rare shared term can still dominate a score the way hash collisions dominated Day 3&#8217;s vector similarity &#8212; the failure mode changes shape, it doesn&#8217;t disappear. This is exactly why Phase 1 (starting Day 13) builds a real eval harness instead of eyeballing a handful of example queries: with only 4 passages, any single example can look great or look bad by chance. A golden dataset and real pass-rate tracking is what actually tells you whether <code>alpha=0.5</code> is the right choice for your corpus.</p><h3>Working Demo Link</h3><div id="youtube2-jrcLZjieO-Y" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;jrcLZjieO-Y&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/jrcLZjieO-Y?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!hdiB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 424w, /__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 848w, /__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!hdiB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png" width="728" height="482.2366589327146" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:571,&quot;width&quot;:862,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:46680,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208933037?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 424w, /__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 848w, /__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hdiB!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe310e000-a427-47f0-bdf7-21519f2f3ff8_862x571.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside <code>lesson_04_package</code>.</p></li><li><p><code>dashboard.html</code><strong> doesn&#8217;t open automatically</strong> &#8212; <code>start.sh</code> tries <code>open</code>/<code>xdg-open</code> as a best-effort convenience and silently skips if neither is available (common in headless/sandboxed environments); just open the file manually in any browser.</p></li><li><p><strong>Different rankings than expected for a query you try yourself</strong> &#8212; expected; this is a 4-passage toy corpus, and small corpora amplify exactly the quirks described above. Don&#8217;t read too much into any single query&#8217;s result at this scale.</p></li><li><p><strong>Wondering why </strong><code>alpha</code><strong> isn&#8217;t tuned automatically</strong> &#8212; that&#8217;s intentional. Automatic tuning needs an eval harness to measure against, which doesn&#8217;t exist until Phase 1.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>This is the default retrieval architecture in most production search-and-answer systems for exactly this reason: neither signal alone is trustworthy enough to ship. A support system relying on vectors alone will occasionally miss the passage with the exact policy number a user asked about; one relying on keywords alone will miss a passage that says the same thing in different words. Combining them is standard practice, not an advanced technique.</p></blockquote><h2>Next Steps</h2><p>Day 5, we add reranking &#8212; a second pass over the top candidates that can use more expensive, more accurate scoring now that hybrid search has already narrowed the field.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lesson 3: Embeddings — Turning Text Into Searchable Vectors]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-3-embeddings-turning-text</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-3-embeddings-turning-text</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Mon, 10 Aug 2026 08:30:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!tBpa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>A deterministic text embedder &#8212; no API call, no training, no cost</p></li><li><p>A 256-dimension vector for every passage from Day 2</p></li><li><p>The similarity search that Day 4&#8217;s retrieval stage will build on &#8212; including an honest look at where it falls short</p></li></ul><h2>Why This Matters</h2><blockquote><p>Yesterday&#8217;s passages are just text &#8212; comparing a question against them character by character finds nothing unless the words match exactly. To search by <em>meaning</em>, text has to become numbers first: a vector, positioned so that similar meaning means similar position. Today we build the simplest version of that idea that still works, using a technique real production systems actually use &#8212; not a toy simplification of one.</p></blockquote><h2>Core Concept</h2><p>The <strong>hashing trick</strong> turns each word in a passage into a position in a fixed-length vector, using a hash function instead of a learned vocabulary. Every occurrence of a word nudges the same vector position by the same amount, so two passages that share vocabulary end up with vectors pointing in a similar direction &#8212; measurable with <strong>cosine similarity</strong>, a score from -1 (opposite) to 1 (identical).</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><blockquote><p>This is a real technique, used at scale specifically because it needs no training step and no vocabulary file &#8212; you can embed text the moment you see it. It&#8217;s also genuinely weaker than a learned embedding model, and today&#8217;s run shows exactly why: with only a handful of passages and short text, shared common words and hash collisions can outweigh the one word that actually matters for relevance. Stripping obvious stopwords (&#8221;the,&#8221; &#8220;a,&#8221; &#8220;can&#8221;) helps, but doesn&#8217;t eliminate the problem &#8212; it&#8217;s a structural limit of the technique, not a bug in today&#8217;s code.</p></blockquote><h2>Architecture</h2><p>Text goes in, a fixed-length vector comes out &#8212; deterministically, so the same passage always produces the same vector. <code>Embedder.embed_passages()</code> runs this over every passage from Day 2&#8217;s passage store and writes the result to <code>embeddings.json</code>. <code>rank_by_similarity()</code> previews what Day 4&#8217;s retrieval stage will do: embed a query the same way, then rank passages by cosine similarity.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!tBpa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!tBpa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png" width="562" height="298.3695054945055" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:773,&quot;width&quot;:1456,&quot;resizeWidth&quot;:562,&quot;bytes&quot;:650091,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208787621?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!tBpa!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e91802d-f910-4477-994d-26283e7521dd_4900x2600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><h2><strong>GitHub Link</strong></h2><p><strong><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_03_package/lesson_03_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_03_package/lesson_03_package</a></strong></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;d127eede-1a26-4e35-b727-4d9b4319e831&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def embed_text(text: str, dim: int = 256) -&gt; list[float]:
    vector = [0.0] * dim
    for token in tokenize(text):
        digest = hashlib.md5(token.encode()).hexdigest()
        hash_int = int(digest, 16)
        index = hash_int % dim
        sign = 1.0 if (hash_int // dim) % 2 == 0 else -1.0
        vector[index] += sign
    # L2-normalize so cosine similarity behaves correctly
    ...</code></pre></div><p>Two properties are worth testing directly, because they&#8217;re provably true regardless of vocabulary quirks: identical text always produces similarity 1.0, and text with zero shared vocabulary always produces similarity 0.0.</p><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;cd044a5b-6343-4e0c-bf7c-05534fd766ba&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh</code></pre></div><p>Expected output:</p><pre><code><code>Embedding 4 passages (dim=256)...
  doc_refund_p0 -&gt; vector with 9 non-zero dims
  doc_refund_p1 -&gt; vector with 10 non-zero dims
  doc_cancel_p0 -&gt; vector with 7 non-zero dims
  doc_pricing_p0 -&gt; vector with 10 non-zero dims
Embeddings saved -&gt; embeddings.json

Query: "Can I get my money back on an annual subscription?"
  doc_cancel_p0  similarity=0.169
  doc_refund_p0  similarity=0.149
  doc_pricing_p0  similarity=0.124

== Running tests ==
6 passed</code></code></pre><p>Notice the top result, <code>doc_cancel_p0</code>, isn&#8217;t obviously the best answer to a refund question &#8212; <code>doc_refund_p0</code> (the actual refund policy) ranks second. That&#8217;s not an error; it&#8217;s the hashing trick&#8217;s real behavior on a small, short passage set, and it&#8217;s genuine motivation for Day 4.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;754033e1-6307-4a76-97a5-3beb29aaa6dd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh</code></pre></div><h1>Implementation Guide</h1><pre><code><code>lesson_03_package/
&#9500;&#9472;&#9472; lesson_code.py    # tokenize, embed_text, cosine_similarity, Embedder
&#9500;&#9472;&#9472; test_lesson.py    # 6 tests verifying embedding correctness properties
&#9500;&#9472;&#9472; requirements.txt  # pytest only &#8212; no API keys needed
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, embeddings.json, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference
</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;1be1c523-626f-4d5a-b061-9941bd087f92&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_03_package
chmod +x start.sh stop.sh
./start.sh
</code></pre></div><p>You should see 4 passages embedded into 256-dimension vectors, an <code>embeddings.json</code> file written, a similarity ranking for a sample query, and <code>6 passed</code>.</p><p>When you&#8217;re done: <code>./stop.sh</code>.</p><h2>Architecture</h2><p><code>embed_text()</code> is the core function &#8212; everything else calls it:</p><ol><li><p><code>tokenize()</code> lowercases the text, strips punctuation, splits on whitespace, and drops stopwords.</p></li><li><p>For each remaining token, MD5-hash it, use the hash to pick a vector index (<code>hash % dim</code>) and a sign (<code>+1</code> or <code>-1</code>, from a different bit of the same hash).</p></li><li><p>Add that signed value into the vector at that index.</p></li><li><p>L2-normalize the final vector &#8212; divide every value by the vector&#8217;s magnitude &#8212; so cosine similarity is comparing direction, not raw word count.</p></li></ol><p><code>Embedder.embed_passages()</code> runs this over a list of passages and returns one <code>PassageEmbedding</code> per passage. <code>rank_by_similarity()</code> embeds a query with the same function and sorts passages by <code>cosine_similarity()</code> against it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!hB-s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!hB-s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png" width="590" height="252.85714285714286" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1456,&quot;resizeWidth&quot;:590,&quot;bytes&quot;:622678,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208787621?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hB-s!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78c2f68b-6ed5-48f7-ac7d-4865abbf0980_4900x2100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Reading the actual similarity results honestly</h2><p>Running this lesson produces:</p><pre><code><code>doc_cancel_p0  similarity=0.169
doc_refund_p0  similarity=0.149
doc_pricing_p0  similarity=0.124</code></code></pre><p>for the query <em>&#8220;Can I get my money back on an annual subscription?&#8221;</em> &#8212; even though <code>doc_refund_p0</code> is the passage that actually answers a refund question. This is real, reproducible behavior, not a bug to fix quietly. Two things are happening:</p><ul><li><p>With only 4 passages of 7&#8211;11 tokens each, a single shared word (<code>annual</code>, <code>subscription</code>) carries a lot of weight in either direction &#8212; there isn&#8217;t enough text for chance overlaps to average out.</p></li><li><p>MD5-hashing into a 256-dimension space still has occasional collisions between unrelated tokens, and at this passage count, one lucky or unlucky collision can flip the ranking.</p></li></ul><p>Both effects shrink as passage count and text length grow, and disappear entirely with a learned embedding model (which this lesson deliberately doesn&#8217;t use yet, to stay free and dependency-light). The two tests that matter for correctness &#8212; identical text scores 1.0, completely disjoint vocabulary scores 0.0 &#8212; hold exactly, because those are mathematical properties of cosine similarity and L2 normalization, not properties that depend on passage count.</p><h3>Working Demo Link:</h3><div id="youtube2-FbSp1u0mdsQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;FbSp1u0mdsQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/FbSp1u0mdsQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!6Ka8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 424w, /__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 848w, /__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 1272w, /__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!6Ka8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png" width="692" height="387.9694364851958" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:587,&quot;width&quot;:1047,&quot;resizeWidth&quot;:692,&quot;bytes&quot;:51923,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208787621?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 424w, /__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 848w, /__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 1272w, /__u/substackcdn.com/image/fetch/$s_!6Ka8!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a4ed34e-2ad2-493c-8831-9453f6ba5961_1047x587.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Troubleshooting</h2><ul><li><p><strong>Different similarity scores than the article</strong> &#8212; if you change <code>MOCK_PASSAGES</code>, <code>EMBEDDING_DIM</code>, or <code>STOPWORDS</code>, expect different rankings; the hashing trick is deterministic per configuration, not across configurations.</p></li><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside <code>lesson_03_package</code>.</p></li><li><p><strong>Wondering why we didn&#8217;t just use a real embedding API today</strong> &#8212; that&#8217;s intentional. Day 3 stays free and dependency-light on purpose; later phases introduce real API-backed embeddings once the eval harness (Phase 1) exists to catch regressions when you do.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>This is exactly why production retrieval systems rarely trust a single vector-similarity score by itself. A system that only ranks by embedding similarity will occasionally surface a plausible-looking wrong answer with high confidence &#8212; and nothing in the score itself signals the mistake. That gap is a large part of why hybrid approaches exist.</p></blockquote><h2>Next Steps</h2><p>Day 4, we add hybrid search: combining today&#8217;s vector similarity with keyword-based scoring, so a passage that shares the exact right word ranks correctly even when the embedding alone gets it wrong.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lesson 2: Ingestion — Getting Real Data Into the System]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-2-ingestion-getting-real-data</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-2-ingestion-getting-real-data</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Thu, 06 Aug 2026 08:30:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fzXN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>A document ingestor that turns raw text into uniform, retrievable passages</p></li><li><p>A fixed-size chunking strategy with overlap, so context doesn&#8217;t get cut mid-thought</p></li><li><p>The passage store that Day 4&#8217;s retrieval stage will search over</p></li></ul><h2>Why This Matters</h2><blockquote><p>Yesterday&#8217;s <code>RetrievalStage</code> was a hardcoded dictionary &#8212; real answers to three fake questions. Today it becomes real, because real documents don&#8217;t arrive in question-answer pairs. They arrive as long, unstructured text: policy docs, support articles, transcripts. Before any search can work, that raw text has to become a set of small, consistent units a retrieval system can actually compare against a query. Get chunking wrong here, and every later phase inherits the mistake &#8212; no eval harness or LLM judge can fix an answer built from a badly cut passage.</p></blockquote><h2>Core Concept</h2><p>A document is too big to search directly. If you embed and compare a 4,000-word support article against a 12-word question, the match is diluted &#8212; the one relevant sentence gets buried in three thousand irrelevant ones. So we split documents into <strong>passages</strong>: smaller chunks a retrieval system can score individually.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><blockquote><p>The chunking decision has a real trade-off. Chunks too large re-introduce the dilution problem. Chunks too small lose context &#8212; a passage that ends mid-sentence, or separates a term from its definition, can&#8217;t be understood on its own even if it&#8217;s retrieved correctly. Today we use fixed-size character chunking with <strong>overlap</strong>: each passage shares a small window of text with its neighbor, so an idea that spans a chunk boundary still appears whole in at least one passage.</p></blockquote><p>Every passage also carries <strong>metadata</strong>: which document it came from, and where in that document it sits. This isn&#8217;t optional bookkeeping &#8212; Day 4&#8217;s retrieval stage needs it to return results a user can trust, and Day 3&#8217;s embeddings need a stable ID to attach a vector to.</p><h2>Architecture</h2><p>Ingestion sits before the pipeline, not inside it: it&#8217;s a one-time (or periodically re-run) process that produces the passage store Stage 2 (Retrieval) will query. Raw documents go in one end; a flat list of <code>Passage</code> objects &#8212; each with an ID, source document, character offsets, and text &#8212; comes out the other.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!fzXN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 424w, /__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 848w, /__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 1272w, /__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!fzXN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png" width="638" height="364.57142857142856" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:832,&quot;width&quot;:1456,&quot;resizeWidth&quot;:638,&quot;bytes&quot;:803914,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208785978?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 424w, /__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 848w, /__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 1272w, /__u/substackcdn.com/image/fetch/$s_!fzXN!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2e0cb9-dd9a-4cb7-afa4-af1512a5207e_4900x2800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><h2><strong>GitHub Link</strong></h2><p><strong><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_02_package/lesson_02_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_02_package/lesson_02_package</a></strong></p><p>Two data contracts, matching the discipline from Day 1:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;dcb5bc7f-d169-459e-b7f0-158466a09425&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">@dataclass
class Document:
    doc_id: str
    source: str
    raw_text: str

@dataclass
class Passage:
    passage_id: str
    doc_id: str
    text: str
    char_start: int
    char_end: int</code></pre></div><p>The ingestor does one job per method: <code>load_documents()</code> reads raw text in, <code>chunk_document()</code> splits one document into overlapping passages, and <code>ingest_all()</code> runs both across every document and returns the full passage store.</p><h2>Build, Test, Verify</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;fa69b8d6-7367-4bf7-90ad-9470e1486eae&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh</code></pre></div><p>Expected output:</p><pre><code><code>Loaded 3 documents
Document 'refund_policy.txt' -&gt; 3 passages
Document 'cancellation.txt' -&gt; 1 passages
Document 'pricing.txt' -&gt; 1 passages
Total passages ingested: 5
Passage store saved -&gt; passages.json

== Running tests ==
5 passed</code></code></pre><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;13d3e7f6-8b8d-4733-9287-6d39c5975034&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh</code></pre></div><h1>Implementation Guide</h1><h1>Package contents</h1><pre><code><code>lesson_02_package/
&#9500;&#9472;&#9472; lesson_code.py    # Document, Passage contracts + DocumentIngestor
&#9500;&#9472;&#9472; test_lesson.py    # 5 tests verifying chunking behavior
&#9500;&#9472;&#9472; requirements.txt  # pytest only
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, passages.json, caches, Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference
</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;57631c52-be9c-4a45-8156-e455ca7f58c4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_02_package
chmod +x start.sh stop.sh
./start.sh</code></pre></div><p><code>start.sh</code> creates a virtual environment, installs <code>requirements.txt</code>, builds the Docker image if Docker is present, runs <code>lesson_code.py</code>, then runs <code>test_lesson.py</code>. You should see 3 documents ingested into 5 total passages, a <code>passages.json</code> file written to disk, and <code>5 passed</code>.</p><p>When you&#8217;re done: <code>./stop.sh</code> &#8212; removes the venv, the generated <code>passages.json</code>, all caches, and the Docker image if one was built.</p><h2>Architecture</h2><p>Two dataclasses carry the data:</p><p>Type Fields Produced by <code>Document</code> doc_id, source, raw_text <code>load_documents()</code> <code>Passage</code> passage_id, doc_id, text, char_start, char_end <code>chunk_document()</code></p><p><code>DocumentIngestor.ingest_all()</code> is the entry point: it calls <code>load_documents()</code> once, then <code>chunk_document()</code> once per document, and returns a flat list of <code>Passage</code> objects &#8212; the passage store.</p><h2>Why fixed-size chunking with overlap, specifically</h2><ul><li><p><strong>chunk_size=220</strong> characters keeps each passage small enough that a single topic dominates it, without cutting it down to a fragment.</p></li><li><p><strong>overlap=40</strong> characters means each new chunk starts 40 characters before the previous one ended. If a sentence spans the boundary between chunk N and chunk N+1, it appears complete in chunk N+1 even though it was cut off in chunk N.</p></li><li><p>Short documents (shorter than <code>chunk_size</code>) produce exactly one passage &#8212; there&#8217;s nothing to split, and the loop exits after the first pass since <code>end == len(text)</code>.</p></li></ul><p>This is deliberately the simplest correct chunking strategy. Production systems often chunk on sentence or paragraph boundaries instead of raw character counts &#8212; that&#8217;s a refinement worth making later, but it changes <code>chunk_document()</code>&#8216;s internals only. The <code>Passage</code> contract and everything downstream stays the same, which is the entire point of the typed-boundary discipline from Day 1.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!qcaG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!qcaG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png" width="672" height="288" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:624,&quot;width&quot;:1456,&quot;resizeWidth&quot;:672,&quot;bytes&quot;:602706,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208785978?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 424w, /__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 848w, /__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!qcaG!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f6ece4d-dfc9-42d4-a99f-40dd60feff07_4900x2100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Working Demo Link:</h3><div id="youtube2-YzT8nBljRZ8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;YzT8nBljRZ8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/YzT8nBljRZ8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!O_bj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 424w, /__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 848w, /__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 1272w, /__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!O_bj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png" width="698" height="339.95039458850056" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/006e573a-92cd-478f-9491-a21b2a774f98_887x432.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:432,&quot;width&quot;:887,&quot;resizeWidth&quot;:698,&quot;bytes&quot;:34264,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208785978?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 424w, /__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 848w, /__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 1272w, /__u/substackcdn.com/image/fetch/$s_!O_bj!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006e573a-92cd-478f-9491-a21b2a774f98_887x432.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run tests from inside the <code>lesson_02_package</code> directory.</p></li><li><p><strong>Different passage counts than expected</strong> &#8212; passage counts depend on the exact <code>chunk_size</code> and <code>overlap</code> values in <code>DocumentIngestor()</code>; if you change them, the counts in the article and README will no longer match your run &#8212; that&#8217;s expected, not a bug.</p></li><li><p><code>passages.json</code><strong> already exists from a previous run</strong> &#8212; <code>start.sh</code> overwrites it each run; <code>stop.sh</code> removes it during cleanup.</p></li></ul><h2>Real-World Connection</h2><blockquote><p>This is the step most teams skip past without thinking about &#8212; and the one that quietly determines whether retrieval works at all. A support-triage system that chunks a refund policy document badly will confidently retrieve a passage that&#8217;s missing the one sentence that mattered, and no amount of prompt tuning downstream fixes that. Chunking strategy is infrastructure, not a detail.</p></blockquote><h2>Next Steps</h2><p>Day 3, we turn these passages into embeddings &#8212; numeric vectors that let the retrieval stage compare a question against thousands of passages by meaning, not just keyword overlap.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lesson 1: Architecture — Designing the Five-Component Pipeline]]></title><description><![CDATA[Today we build:]]></description><link>https://aieworks.substack.com/p/lesson-1-architecture-designing-the</link><guid isPermaLink="false">https://aieworks.substack.com/p/lesson-1-architecture-designing-the</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Mon, 03 Aug 2026 08:30:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Xfj3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Today we build:</strong></p><ul><li><p>The five-stage contract every later phase of this course plugs into</p></li><li><p>A working, testable pipeline skeleton &#8212; no real model calls required</p></li><li><p>The one architectural decision that determines whether you can add evals, audits, and cost controls later without a rewrite</p></li></ul><h2>Why This Matters</h2><blockquote><p>Most AI features start as one function: take a question, call a model, return the answer. It works in the demo. It becomes unmaintainable the first time something goes wrong, because there&#8217;s no place to insert a check, log a decision, or measure a cost &#8212; everything happens inside one opaque call. Today&#8217;s architecture decision is the one choice you can&#8217;t easily undo later, so we make it deliberately, on Day 1, before writing a line of logic.</p></blockquote><h2>Core Concept</h2><p>A single-call AI feature has exactly one seam: the API call itself. If the output is wrong, you have one place to look and no way to tell whether the problem was understanding the question, finding the right information, or writing the answer. You&#8217;re debugging a black box.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><blockquote><p>The fix isn&#8217;t a bigger model &#8212; it&#8217;s more seams. We split the work into five stages, each with a narrow, explicit responsibility: <strong>Query Understanding</strong> turns a raw question into a structured intent. <strong>Retrieval</strong> finds the information needed to answer it. <strong>Synthesis</strong> drafts an answer from that information. A <strong>Critic</strong> checks the draft against the original intent before anyone sees it. <strong>Formatting</strong> shapes the final response for its destination &#8212; API, chat window, or app.</p></blockquote><blockquote><p>Five stages sounds like more surface area to break. It&#8217;s the opposite. Each seam is a place where next week&#8217;s eval harness can score an output, where a future audit log can record a decision, and where a cost tracker can attribute a dollar. A monolithic call gives you none of that. The stages don&#8217;t make the system more complex &#8212; they make the complexity visible, one boundary at a time.</p></blockquote><p>The rule for today: every stage takes a typed input and returns a typed output. Nothing passes a raw string or a loose dictionary between stages. That discipline is what lets Phase 1 through Phase 5 attach reliability tooling to this pipeline without touching the pipeline itself.</p><h2>Architecture</h2><p>Data flows in one direction: a <code>Query</code> enters, and a <code>FinalResponse</code> leaves. Each stage owns one transformation and knows nothing about the stages before or after it beyond the shape of the data it receives and returns. The <code>Pipeline</code> object is the only piece that knows the full sequence &#8212; swap any one stage&#8217;s implementation later, and nothing else needs to change.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Xfj3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 424w, /__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 848w, /__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Xfj3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png" width="534" height="348.7870879120879" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:951,&quot;width&quot;:1456,&quot;resizeWidth&quot;:534,&quot;bytes&quot;:935287,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208648047?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 424w, /__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 848w, /__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Xfj3!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb7cd22b-7216-4ab8-9ee3-06c1b089d159_4900x3200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Implementation</h2><h2><strong>GitHub Link</strong></h2><p><strong><a href="https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_01_package%20(1)/lesson_01_package">https://github.com/sysdr/ai-reliability-engineering/tree/main/lesson_01_package%20(1)/lesson_01_package</a></strong></p><p>Start by defining the data contracts &#8212; these are the types every stage will pass along:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b9f41e5a-1436-4387-a6eb-560ce0ebe4c2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">@dataclass
class Query:
    text: str
    session_id: str

@dataclass
class RetrievedContext:
    passages: list[str]

@dataclass
class DraftAnswer:
    text: str

@dataclass
class CriticVerdict:
    approved: bool
    reason: str

@dataclass
class FinalResponse:
    text: str
    approved: bool</code></pre></div><p>Each stage is a class with one method &#8212; <code>run()</code> &#8212; that takes the previous stage&#8217;s output type and returns its own. Today, each stage&#8217;s internal logic is intentionally simple (deterministic, no API calls) &#8212; you&#8217;re proving the seams work before you make any single stage smarter. The <code>Pipeline</code> class calls all five in sequence and returns the final response.</p><h2>Build, Test, Verify</h2><p>Everything &#8212; install, build, run, test, verify &#8212; is one command:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;ad9e4f88-120f-449f-8793-5b2cd7eaa69b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">chmod +x start.sh stop.sh
./start.sh</code></pre></div><p><code>start.sh</code> installs dependencies, builds the Docker image if Docker is available (skips gracefully if not), runs the pipeline, runs the tests, and prints a verification summary:</p><pre><code><code>Query received: "What's the refund policy for annual plans?"
Stage 1 (Query Understanding) -&gt; intent: refund_policy
Stage 2 (Retrieval) -&gt; 2 passages found
Stage 3 (Synthesis) -&gt; draft answer generated
Stage 4 (Critic) -&gt; approved: True
Stage 5 (Formatting) -&gt; response ready
Final: "Annual plans can be refunded within 30 days of purchase."

== Running tests ==
4 passed</code></code></pre><p>When you&#8217;re done, clean everything up:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;907156f1-4a8c-40d1-b829-7ad8ef3cf210&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">./stop.sh</code></pre></div><h1>Implementation Guide </h1><h2>Package contents</h2><pre><code><code>lesson_01_package/
&#9500;&#9472;&#9472; lesson_code.py    # the five stages + Pipeline class
&#9500;&#9472;&#9472; test_lesson.py    # 4 tests verifying the pipeline is wired correctly
&#9500;&#9472;&#9472; requirements.txt  # pytest only &#8212; no API keys needed today
&#9500;&#9472;&#9472; Dockerfile         # minimal python:3.11-slim image
&#9500;&#9472;&#9472; start.sh           # install deps, build, run, test, verify &#8212; one command
&#9500;&#9472;&#9472; stop.sh            # cleanup &#8212; removes .venv, caches, and the Docker image
&#9492;&#9472;&#9472; README.md          # quick-start reference</code></code></pre><h2>Run it</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;df683f97-d473-4f52-9ec3-e1ad103301ef&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">cd lesson_01_package
chmod +x start.sh stop.sh
./start.sh</code></pre></div><p><code>start.sh</code> does five things in order: creates a virtual environment, installs <code>requirements.txt</code>, builds the Docker image if Docker is present (skips cleanly if not), runs <code>lesson_code.py</code>, then runs <code>test_lesson.py</code>. You should see 5 stage log lines, a final answer, and <code>4 passed</code>.</p><p>When you&#8217;re done: <code>./stop.sh</code> &#8212; removes the venv, <code>__pycache__</code>, <code>.pytest_cache</code>, and the Docker image if one was built.</p><h3>Working Demo Link</h3><div id="youtube2-a7Uz9on9G98" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;a7Uz9on9G98&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/a7Uz9on9G98?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Architecture</h2><p>Five typed dataclasses define every handoff between stages:</p><p>From To Type caller Query Understanding <code>Query</code> (text, session_id) Query Understanding Retrieval <code>Intent</code> (query, intent_label) Retrieval Synthesis <code>RetrievedContext</code> (intent, passages) Synthesis Critic <code>DraftAnswer</code> (context, text) Critic Formatting <code>CriticVerdict</code> (draft, approved, reason) Formatting caller <code>FinalResponse</code> (text, approved)</p><p>Each stage class exposes exactly one public method, <code>run()</code>, that takes the previous type and returns the next. The <code>Pipeline</code> class is the only object that knows the full call order &#8212; this is what makes it possible to replace, say, <code>RetrievalStage</code> in Day 2 without touching <code>SynthesisStage</code>, <code>CriticStage</code>, <code>Pipeline</code>, or any test outside <code>test_lesson.py</code>.</p><h2>Why each stage exists</h2><ul><li><p><strong>QueryUnderstandingStage</strong> &#8212; classifies intent with a simple keyword match today. This is the seam Phase 1&#8217;s eval harness will score against a golden dataset of real questions.</p></li><li><p><strong>RetrievalStage</strong> &#8212; looks up from a hardcoded dict today. Day 2 replaces the internals with real ingestion and search; the <code>run(intent) -&gt; RetrievedContext</code> contract doesn&#8217;t change.</p></li><li><p><strong>SynthesisStage</strong> &#8212; picks the first matching passage today. This becomes the seam where Phase 1&#8217;s LLM-as-judge later scores answer quality.</p></li><li><p><strong>CriticStage</strong> &#8212; the seam Phase 1&#8217;s CI gate eventually wraps: nothing ships past this point without passing a check.</p></li><li><p><strong>FormattingStage</strong> &#8212; the seam Phase 3&#8217;s audit trail will log from &#8212; every <code>FinalResponse</code> becomes a queryable, replayable event.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!zCAR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 424w, /__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 848w, /__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!zCAR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png" width="554" height="361.85027472527474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:951,&quot;width&quot;:1456,&quot;resizeWidth&quot;:554,&quot;bytes&quot;:921828,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208648047?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 424w, /__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 848w, /__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zCAR!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90955247-77e6-4a28-8f9f-346c89bcacb7_4900x3200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Test coverage</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!jSFe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 424w, /__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 848w, /__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!jSFe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png" width="871" height="340" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:340,&quot;width&quot;:871,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26638,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208648047?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 424w, /__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 848w, /__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jSFe!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd14fba6d-4f6d-4099-a7a7-90f59481568e_871x340.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Troubleshooting</h2><ul><li><p><code>ModuleNotFoundError: No module named 'lesson_code'</code> &#8212; run <code>test_lesson.py</code> from inside the <code>lesson_01_package</code> directory, not from a parent folder.</p></li><li><p><strong>Docker build step skipped</strong> &#8212; expected if Docker isn&#8217;t installed locally; <code>start.sh</code> falls back to a plain Python run automatically.</p></li><li><p><strong>Python version errors</strong> &#8212; this lesson uses <code>list[str]</code> generic syntax, which requires Python 3.9+; the Dockerfile pins 3.11 to avoid this entirely.</p></li></ul><h2>Real-World Connection</h2><p>This pattern is why a well-run support-escalation system can tell you exactly which step misfired when a customer gets a wrong answer &#8212; was it a retrieval miss, a synthesis error, or a critic that should have caught it? Systems built as one opaque call can only tell you that something, somewhere, went wrong. That difference is the entire reason this course spends 90 days on it instead of shipping the one-call version and hoping.</p><h2>Next Steps</h2><p>Day 2, we build the first real stage: ingestion. You&#8217;ll take unstructured source documents and turn them into the retrievable units that Stage 2 will search over &#8212; the raw material the rest of the pipeline depends on.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Final Capstone: Build a Production NLP Intelligence System]]></title><description><![CDATA[What We&#8217;re Building Today]]></description><link>https://aieworks.substack.com/p/final-capstone-build-a-production</link><guid isPermaLink="false">https://aieworks.substack.com/p/final-capstone-build-a-production</guid><dc:creator><![CDATA[sysdai]]></dc:creator><pubDate>Sat, 01 Aug 2026 08:30:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!EAU8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p>A multi-capability NLP Intelligence API that classifies intent, scores sentiment, and generates contextual responses &#8212; all served through a single REST endpoint.</p></li><li><p>A modular PyTorch-based pipeline where each NLP component is independently testable and swappable without touching the rest of the system.</p></li><li><p>A production-style deployment with Docker, a health-check endpoint, structured logging, and a minimal web demo interface.</p></li></ul><div><hr></div><h2>Why This Matters</h2><blockquote><p>You&#8217;ve spent 176 days learning how individual pieces work &#8212; tensors, layers, training loops, embeddings, sequence models. What separates a student from a practitioner is the ability to compose those pieces into a system that solves a real problem end-to-end. Every major NLP product at companies like OpenAI, Cohere, or Hugging Face is not one model &#8212; it&#8217;s a pipeline of coordinated components: input normalization, classification, generation, post-processing, and serving. This capstone is your first time building that pipeline yourself, which means by the end of Day 180, you&#8217;ll have produced something you can legitimately show in a job interview or deploy for real users.</p></blockquote><div><hr></div><h2>Core Concepts</h2><h3>1. The NLP Pipeline as a System, Not a Script</h3><p>A pipeline is a directed graph of transformations. Each node takes in a well-defined input and produces a well-defined output. The moment you start building pipelines this way, your code becomes composable. You can swap a rule-based intent classifier for a neural one without touching the sentiment module. You can add a new component &#8212; say, entity extraction &#8212; without modifying the response generator.</p><p>In production systems at Stripe or Shopify, the ML inference path looks exactly like this: an API gateway hands the raw text to a normalization service, which passes the cleaned text to a classification model, which produces a structured prediction object, which routes to a generation or retrieval system, which formats the final response. Each handoff is a typed contract. Today you&#8217;re building that exact pattern.</p><h3>2. Intent Classification with Lightweight Transformers</h3><p>The dominant pattern for intent classification in production is fine-tuning a pre-trained transformer on a small labeled dataset. DistilBERT &#8212; a compressed version of BERT &#8212; gives you 97% of the accuracy at 40% of the size. This is the same trade-off that powers intent routing in voice assistants at Amazon and Google.</p><p>The key insight here is that the pre-trained model already understands language at a deep level. Your fine-tuning job is not to teach it language &#8212; it&#8217;s to teach it your categories. When you have only a few hundred labeled examples, this transfer learning approach outperforms training from scratch by a wide margin. Your final classifier is really just a linear layer sitting on top of a frozen or lightly-unfrozen transformer backbone.</p><h3>3. Sentiment-Aware Response Generation</h3><p>The response generation in this system is a hybrid: a retrieval layer picks a candidate response template based on intent and sentiment score, and a neural reranker scores each candidate against the original query context. This avoids the hallucination risk of pure generation while still producing contextually relevant responses.</p><p>This pattern &#8212; retrieval-augmented generation with a reranker &#8212; is exactly what systems like Perplexity AI and Bing Chat use at scale. The reranker is a cross-encoder transformer that takes (query, candidate) pairs as input and outputs a relevance score. You&#8217;re not building the full RAG stack today, but the architecture you implement mirrors the production pattern precisely.</p><h3>4. API Design for ML Systems</h3><p>Serving an ML model is fundamentally different from serving a database. Model inference has unpredictable latency, GPU resource contention, and serialization overhead. Three patterns matter most:</p><blockquote><p><strong>Request batching:</strong> Group multiple incoming requests together before sending to the model. A batch of 32 requests takes only 1.5&#215; the time of a single request on a GPU, so throughput multiplies dramatically. Your system implements a simple synchronous batch endpoint.</p><p><strong>Response caching:</strong> Identical or near-identical queries should return cached predictions. A simple Redis-backed LRU cache cuts GPU load significantly in production. The architecture includes a cache layer even if you implement it in-memory for the capstone.</p><p><strong>Structured error envelopes:</strong> Every response &#8212; success or failure &#8212; should follow the same JSON schema. Clients must be able to rely on the response structure regardless of what happened internally. Your API will always return <code>{status, data, error, latency_ms}</code>.</p></blockquote><h3>5. Component Architecture</h3><p>The preprocessor runs first and its output is shared by both the intent classifier and the sentiment scorer &#8212; they run in parallel, then their outputs converge at the response selector. This parallel fan-out pattern keeps latency bounded to <code>max(t_intent, t_sentiment)</code> rather than <code>t_intent + t_sentiment</code>.</p><pre><code><code>                   NLP Intelligence API

   Input Text
       |
       v
   [Preprocessor]  --&gt;  normalize, tokenize, truncate
       |
       +-----------------------------+
       v                             v
   [Intent Classifier]        [Sentiment Scorer]
   (DistilBERT + linear)      (LSTM / Day 176)
       |                             |
       +-------------+---------------+
                     v
            [Response Selector]
            (retrieve + rerank)
                     |
                     v
              [JSON Response]
</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!EAU8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!EAU8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png" width="1456" height="996" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:996,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EAU8!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74cde9ba-652d-4efe-bb53-d03cc64b2217_3800x2600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="/__u/aieworks.substack.com/p/final-capstone-build-a-production">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 23-24 : Computer Vision ]]></title><description><![CDATA[VisionClassifier Platform: From Fragmented CV Scripts to a Unified Computer Vision Stack]]></description><link>https://aieworks.substack.com/p/week-23-24-computer-vision</link><guid isPermaLink="false">https://aieworks.substack.com/p/week-23-24-computer-vision</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Fri, 31 Jul 2026 02:10:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!u3In!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc345f7ec-0d83-48a7-96ef-e0c90cff42cc_3680x1920.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Introduction</strong></h2><p>Computer vision prototypes usually die in notebooks: one script for preprocessing, another for training, a third for evaluation, and nothing that tracks experiments or serves inference behind a stable HTTP contract. <code>week_23_24_aiml_integrated_project</code> is a self-contained capstone that consolidates the full vision pipeline into one runnable system:</p><ul><li><p>An installable Python package (<code>week2324_python</code>) with concept-named modules for preprocessing, CNNs, pooling, datasets, training loops, evaluation, export, and deployment</p></li><li><p>A FastAPI backend exposing two API surfaces: a <strong>lab path</strong> for topic exploration and a <strong>product path</strong> for experiment lifecycle management</p></li><li><p><strong>VisionClassifier Platform</strong> &#8212; a React dashboard with a guided pipeline, component studio, live training monitor, and multipart image inference</p></li><li><p>PostgreSQL-backed experiment metadata (async SQLAlchemy + Alembic), named Docker volumes for artifacts and dataset caches, and offline-safe synthetic fallbacks when CIFAR archives cannot be downloaded</p></li></ul><p>The engineer payoff is traceability and boundaries. A quick CNN smoke test is not a stray script&#8212;it is <code>train_simple_cnn</code> behind a lab route with capped batches. Production training is not a blocking HTTP call&#8212;it is <code>POST /api/v1/cv/experiments/{id}/train</code> returning HTTP 202 while <code>asyncio.create_task</code> runs ResNet fine-tuning in a worker thread, with the dashboard polling run status until completion or failure.</p><div><hr></div><h2><strong>Core Components</strong></h2><p>The stack is layered. Each topic below is a distinct building block; together they form one coherent image-classification platform.</p><h3><strong>Image preprocessing and normalization</strong></h3><p><code>image_pipeline.py</code> handles OpenCV/PIL ingestion, ImageNet-style normalization profiles (<code>CIFAR10_MEAN</code> / <code>CIFAR100_MEAN</code>), and <code>build_inference_transform</code> for 224&#215;224 upload tensors. <code>preprocess_upload_bytes</code> powers multipart inference on the product API.</p><h3><strong>Convolutional network architectures</strong></h3><p><code>SimpleCNN</code> provides a pedagogical three-block classifier for CIFAR-10 smoke tests. <code>ImageClassifier</code> is a deeper three-block production-style CNN with checkpoint saving. <code>build_resnet18_classifier</code> wraps torchvision ResNet18 with a replaceable FC head for 100-way CIFAR-100 classification. <code>inspect_feature_maps</code> in <code>cnn/debug.py</code> exposes spatial activations for architecture demos.</p><h3><strong>Pooling strategies</strong></h3>
      <p>
          <a href="/__u/aieworks.substack.com/p/week-23-24-computer-vision">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Days 173–176: Build a Sentiment Analyzer]]></title><description><![CDATA[Agenda]]></description><link>https://aieworks.substack.com/p/days-173176-build-a-sentiment-analyzer</link><guid isPermaLink="false">https://aieworks.substack.com/p/days-173176-build-a-sentiment-analyzer</guid><dc:creator><![CDATA[sysdai]]></dc:creator><pubDate>Wed, 29 Jul 2026 08:30:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!I-2p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Agenda</h2><ul><li><p>Wire together everything from Days 169&#8211;172 into a single end-to-end pipeline</p></li><li><p>Build a production-style sentiment classifier on the IMDB movie reviews dataset</p></li><li><p>Train, evaluate, and serve the model through a lightweight REST inference endpoint</p></li><li><p>Measure real metrics &#8212; accuracy, F1 score, and confusion matrix &#8212; not just training loss</p></li></ul><div><hr></div><h2>Why This Matters</h2><blockquote><p>Every product that handles user-generated text has a sentiment layer somewhere. Amazon uses it to surface trending complaints before they become PR crises. Airbnb uses it to flag toxic reviews in real time. Spotify uses it on social data to figure out which artists are gaining buzz before the charts reflect it. The pattern is always the same: preprocess text, encode meaning, classify intent, act on the signal. You have all three of those pieces now &#8212; tokenization from Day 169, embeddings from Day 170, and sequence modeling from Day 172. This project stitches them into a deployable system and teaches you the engineering discipline of closing the loop from raw data to a live prediction endpoint.</p></blockquote><div><hr></div><h2>Core Concepts</h2><h3>1. The Full NLP Pipeline as One Unit</h3><p>Up to now you have built each stage in isolation. A sentiment analyzer forces you to reason about the pipeline as a single contract: garbage in one stage poisons every stage after it. The input to your model is not text &#8212; it is a fixed-length integer sequence produced by a specific tokenizer trained on a specific vocabulary. That tokenizer must be saved alongside the model weights or the model is useless at inference time. This is the single most common mistake junior engineers make when deploying NLP systems: they checkpoint the weights and throw away the tokenizer.</p><p>Think of it like a lock-and-key pair. The model is the lock; the tokenizer is the key. Ship them as one artifact.</p><h3>2. Bidirectional LSTMs for Sentiment</h3><p>A standard LSTM reads a sentence left to right. But the word &#8220;not&#8221; near the beginning of a sentence can completely invert the meaning of a word that appears twelve tokens later. A Bidirectional LSTM runs two passes &#8212; one forward, one backward &#8212; and concatenates the hidden states at each timestep. The model can now &#8220;see&#8221; what comes after a word before it decides how to weight that word. For sentiment tasks, Bi-LSTMs consistently outperform unidirectional ones by three to five percentage points on benchmark datasets with no extra training data.</p><h3>3. Pretrained GloVe Embeddings vs Learned Embeddings</h3><p>You can initialize your embedding layer with random weights and let the model learn them from scratch. That works fine if you have hundreds of thousands of training examples. For smaller datasets &#8212; under fifty thousand examples &#8212; you are better off seeding the embedding layer with pretrained GloVe vectors (100d or 200d). GloVe was trained on billions of tokens; your model gets that knowledge for free and only needs to fine-tune it. The practical difference in validation accuracy on IMDB is typically three to eight points, and training converges two to three times faster.</p><h3>4. Evaluation Beyond Accuracy</h3><p>Binary sentiment on IMDB is balanced (50% positive, 50% negative), so accuracy is a fair metric here. The moment you move to a real production dataset &#8212; say, customer support tickets where 80% are neutral &#8212; accuracy becomes misleading. A model that always predicts &#8220;neutral&#8221; gets 80% accuracy while being completely useless. F1 score, which balances precision and recall, is the metric that actually tells you whether the model is doing something useful. Wire in scikit-learn&#8217;s <code>classification_report</code> from day one so you develop the habit of looking at the full picture.</p><h3>5. The Inference Contract</h3><p>A trained model sitting in a <code>.pt</code> file does nothing for a product team. What they need is an inference function with a clean, stable interface: string in, label and confidence score out. In production this function gets wrapped in a FastAPI endpoint, containerized, and deployed behind a load balancer. Building that wrapper is as much a part of the project as training the model. The function needs to handle edge cases &#8212; empty strings, non-ASCII characters, extremely long inputs &#8212; without crashing.</p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!I-2p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!I-2p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png" width="1456" height="923" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:923,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!I-2p!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6f591ea-7cb9-430f-befa-6af87d358616_4100x2600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="/__u/aieworks.substack.com/p/days-173176-build-a-sentiment-analyzer">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[AI Reliability Engineering: Building a Production-Grade Multi-Agent System in 90 Days]]></title><description><![CDATA[Introduction]]></description><link>https://aieworks.substack.com/p/ai-reliability-engineering-building</link><guid isPermaLink="false">https://aieworks.substack.com/p/ai-reliability-engineering-building</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Tue, 28 Jul 2026 01:31:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!kZlH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2><blockquote><p>This isn&#8217;t a course on how to call an LLM API. It&#8217;s a masterclass in what happens after your AI feature works in the demo &#8212; evaluation, safe deployment, observability, and cost control at production scale. Most tutorials stop at &#8220;call the model, print the response.&#8221; Here, we start where they end. You will build a five-agent retrieval system, then spend the next 78 days making it trustworthy enough to run unattended in front of real users.</p></blockquote><blockquote><p>We dismantle the &#8220;ship it and hope&#8221; approach to AI systems, replacing it with an eval-gated, audit-logged, budget-enforced deployment pipeline. You will learn to wrestle with the specific failure modes of AI software &#8212; non-deterministic outputs, silent quality regressions, refusal spikes, and runaway API costs &#8212; the class of problems that don&#8217;t show up in a HTTP status code. By the end, you won&#8217;t just have a working system; you&#8217;ll know how to keep any AI system honest once it&#8217;s live.</p></blockquote><p>AI Reliability Engineering is a reader-supported publication. Days 1&#8211;12 are free for everyone. To receive new posts and support this work, consider becoming a free or paid subscriber.</p><p>Each lesson is hands-on &#8212; working code, real tests, no pseudocode.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><em>90 Lessons. 90 Days. One System, Built in the Open.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe&quot;,&quot;text&quot;:&quot;Subscribe Now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/aieworks.substack.com/subscribe"><span>Subscribe Now</span></a></p><div><hr></div><h2>1. Course Details</h2><h3>Why This Course?</h3><blockquote><p>Standard AI tutorials teach you to call a model and display the output. That&#8217;s the whole lesson. It doesn&#8217;t survive contact with production, where the same input produces a different output every time, where a &#8220;successful&#8221; API call can still return something wrong, harmful, or off-topic, and where nobody finds out until a user complains. This course exists to teach the discipline that closes that gap: reliability engineering applied specifically to AI systems, not general software.</p></blockquote><h3>What You&#8217;ll Build</h3><p>You will build a complete, five-agent retrieval system, then wrap it in a full reliability stack.</p><p><strong>The System:</strong> A multi-agent pipeline &#8212; query understanding, retrieval, synthesis, a critic, and response formatting &#8212; working together as one coordinated flow.</p><p><strong>The Eval Harness:</strong> A golden dataset, automated scoring, and a calibrated LLM judge that catches quality regressions before they ship.</p><p><strong>The Safety Net:</strong> Deployment tooling that makes changing a prompt as safe and reversible as any other code change.</p><p><strong>The Ledger:</strong> An audit trail that makes every decision the system made queryable and verifiable after the fact.</p><p><strong>The Governor:</strong> Cost tracking and budget enforcement so API spend is predictable instead of a surprise on the invoice.</p><h3>Who Should Take This Course?</h3><p>Engineers who&#8217;ve shipped an AI feature and are now getting the harder questions: &#8220;how do we know it&#8217;s not getting worse?&#8221;</p><p>Backend engineers moving from CRUD APIs into AI-native systems.</p><p>Teams who need to explain, after the fact, exactly why their system made a given decision.</p><p>Anyone tired of finding out about quality problems from a support ticket instead of a dashboard.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!kZlH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 424w, /__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 848w, /__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 1272w, /__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!kZlH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg" width="520" height="585" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1638,&quot;width&quot;:1456,&quot;resizeWidth&quot;:520,&quot;bytes&quot;:20127,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/svg+xml&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208416884?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 424w, /__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 848w, /__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 1272w, /__u/substackcdn.com/image/fetch/$s_!kZlH!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0470b63c-0497-4869-8cce-d9b4cebfb2b1_1200x1350.svg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>What Makes This Course Different?</h3><p>No black boxes. You build the eval harness, the audit trail, and the cost governor yourself &#8212; not a wrapper around someone else&#8217;s SaaS dashboard.</p><p>Reliability over demos. We care less about the model&#8217;s cleverest answer and more about what happens on its worst day.</p><p>Failure first. Every phase includes deliberately breaking the system &#8212; a bad prompt, a budget overrun, a corrupted audit record &#8212; to prove the safeguards actually catch it.</p><h3>Key Topics Covered</h3><p>Eval design: golden datasets, deterministic checks, LLM-as-judge with calibration.</p><p>CI gates that block a deploy when quality drops.</p><p>Safe prompt deployment: versioning, staged rollout, instant rollback.</p><p>Tamper-evident audit logging and full session replay.</p><p>Cost attribution, budget enforcement, and circuit breakers for runaway spend.</p><p>Multi-agent failure modes and how they differ from single-model failures.</p><h3>Prerequisites</h3><p>Comfortable writing and testing Python. Basic familiarity with calling an LLM API (OpenAI or Anthropic). A GitHub account. No prior evaluation, MLOps, or observability experience required.</p><div><hr></div><h2>2. Learning Objectives</h2><p><strong>By Phase 0 (Build):</strong> You will be able to stand up a working five-agent retrieval pipeline and articulate, in plain terms, why &#8220;it works in the demo&#8221; isn&#8217;t the same as &#8220;it&#8217;s reliable.&#8221;</p><p><strong>By Phase 1 (Evaluation):</strong> You will be able to design a golden dataset, write deterministic and LLM-judge-based checks, and wire a CI gate that blocks a pull request when output quality regresses.</p><p><strong>By Phase 2 (Deployment Safety):</strong> You will be able to deploy a prompt change through a feature-flag workflow with automatic rollback, instead of editing a string in production.</p><p><strong>By Phase 3 (Observability &amp; Audit):</strong> You will be able to build a tamper-evident log of every decision the system makes, and answer &#8220;why did it do that?&#8221; for any past session.</p><p><strong>By Phase 4 (Cost Governance):</strong> You will be able to attribute API spend to a specific user and feature, enforce budget limits in code, and fail gracefully instead of blowing through a ceiling.</p><p><strong>By Phase 5 (Advanced Reliability &amp; Capstone):</strong> You will be able to reason about multi-agent-specific failure modes and ship a fully integrated system &#8212; eval-gated, audited, and cost-governed &#8212; as a portfolio piece.</p><div><hr></div><h2>3. Course Structure</h2><p>The curriculum is divided into 6 phases across 90 days, mirroring the lifecycle of a production AI system: build it, prove it works, deploy it safely, watch it, control what it costs, then harden it.</p><p><em>Phase 0: Build the System (Days 1&#8211;12)</em> &#8212; Free. Stand up the five-agent pipeline: architecture, retrieval, and a working baseline.</p><p><em>Phase 1: Evaluation Engineering (Days 13&#8211;31)</em> &#8212; Paid. Golden datasets, automated scoring, calibrated LLM-as-judge.</p><p><em>Phase 2: Deployment Safety (Days 32&#8211;48)</em> &#8212; Paid. Feature flags, versioned rollout, instant rollback.</p><p><em>Phase 3: Observability &amp; Audit Trails (Days 49&#8211;66)</em> &#8212; Paid. Structured logging, tamper-evident audit logs, session replay.</p><p><em>Phase 4: Cost Governance (Days 67&#8211;78)</em> &#8212; Paid. Cost attribution, budget policy enforcement, circuit breakers.</p><p><em>Phase 5: Advanced Reliability &amp; Capstone (Days 79&#8211;90)</em> &#8212; Paid. Multi-agent failure modes, full system integration, final deployment.</p><div><hr></div><h2>4. The 90 Days, Lesson by Lesson</h2><h3>Days 1&#8211;12</h3><p>Day 1: Architecture &#8212; Designing the Five-Component Pipeline </p><p>Day 2: Ingestion &#8212; Getting Real Data Into the System </p><p>Day 3: Embeddings &#8212; Turning Text Into Searchable Vectors </p><p>Day 4: Hybrid Search &#8212; Combining Keyword and Vector Retrieval </p><p>Day 5: Reranking &#8212; Improving on Raw Retrieval Order </p><p>Day 6: The QueryUnderstandingAgent &#8212; Parsing User Intent </p><p>Day 7: The SynthesisAgent &#8212; Generating Coherent Answers </p><p>Day 8: The CriticAgent &#8212; Catching Bad Outputs Before They Ship </p><p>Day 9: The ResponseFormatter &#8212; Shaping Output for the User </p><p>Day 10: Wiring the Full Pipeline Together </p><p>Day 11: End-to-End Testing Across All Components </p><p>Day 12: Baseline Measurement &#8212; How the System Fails Today</p><div><hr></div><h3>Days 13&#8211;90</h3><p><strong>Phase 1 &#8212; Evaluation Engineering (Days 13&#8211;31)</strong> </p><p>Day 13: Why AI Outputs Need Eval Cases, Not Unit Tests </p><p>Day 14: Designing the Golden Dataset Schema </p><p>Day 15: Writing Your First Golden Eval Cases </p><p>Day 16: Building the Eval Case Intake Process </p><p>Day 17: Deterministic Checks &#8212; The Fast, Cheap First Layer </p><p>Day 18: Building the Composite Scorer </p><p>Day 19: Building the Eval Runner </p><p>Day 20: Connecting the Runner to the Live Pipeline </p><p>Day 21: Tracking Pass Rate Over Time </p><p>Day 22: LLM-as-Judge &#8212; Evaluating What Structure Can&#8217;t Catch </p><p>Day 23: Designing Judge Quality Dimensions </p><p>Day 24: Building the Mock Judge Backend </p><p>Day 25: Connecting the Judge to a Real Model </p><p>Day 26: Human-Labeling a Calibration Set </p><p>Day 27: Computing Inter-Rater Agreement </p><p>Day 28: The Trust Coefficient &#8212; When to Trust the Judge </p><p>Day 29: Composite Scoring &#8212; Checks and Judge Together </p><p>Day 30: The CI Gate &#8212; Blocking Bad Merges Automatically </p><p>Day 31: Phase 1 Review &#8212; Full Eval Suite Integration Test</p><div><hr></div><p><strong>Phase 2 &#8212; Deployment Safety (Days 32&#8211;48)</strong> </p><p>Day 32: Why Prompt Changes Need the Same Rigor as Code </p><p>Day 33: Designing the Flag Data Model </p><p>Day 34: Flag Storage and Version History </p><p>Day 35: Building the Flag CLI </p><p>Day 36: The Flag Evaluation Engine </p><p>Day 37: Deterministic Rollout Assignment </p><p>Day 38: Testing Rollout Math at Scale </p><p>Day 39: Eval-Gated Promotion </p><p>Day 40: Building the Promotion Workflow </p><p>Day 41: Version History and Change Audit </p><p>Day 42: Rollback in Under 30 Seconds </p><p>Day 43: Testing a Bad-Variant Rollback End to End </p><p>Day 44: Running A/B Experiments Between Variants </p><p>Day 45: Comparing Variants on Quality and Cost </p><p>Day 46: Establishing Statistical Confidence </p><p>Day 47: Wiring Flags Into the Live Pipeline </p><p>Day 48: Phase 2 Review &#8212; Full Deployment Safety Integration Test</p><div><hr></div><p><strong>Phase 3 &#8212; Observability &amp; Audit Trails (Days 49&#8211;66)</strong> </p><p>Day 49: Every Decision Is an Event Worth Recording </p><p>Day 50: Designing the Audit Event Schema </p><p>Day 51: Building the Event Writer </p><p>Day 52: Hashing Inputs and Outputs Instead of Raw Data </p><p>Day 53: The Async Write Path &#8212; Never Block a Request </p><p>Day 54: Buffering and Flush Strategy </p><p>Day 55: Structured Tracing Across the Pipeline </p><p>Day 56: Load-Testing the Async Queue </p><p>Day 57: Querying Audit Events by User and Session '</p><p>Day 58: Comparing Behavior Across Prompt Versions </p><p>Day 59: Replaying a Full Session, Decision by Decision </p><p>Day 60: Tamper Evidence &#8212; Hash Chaining </p><p>Day 61: Verifying Chain Integrity and Detecting Breaks </p><p>Day 62: Refusal-Rate Analysis by Topic </p><p>Day 63: Cost and Behavior, Side by Side </p><p>Day 64: Building the Unified Dashboard </p><p>Day 65: Anomaly Detection on Quality Trends </p><p>Day 66: Phase 3 Review &#8212; Full Observability Integration Test</p><div><hr></div><p><strong>Phase 4 &#8212; Cost Governance (Days 67&#8211;78)</strong> </p><p>Day 67: The Invoice Problem &#8212; Why P50 Estimates Are Wrong </p><p>Day 68: Building Cost Attribution Middleware </p><p>Day 69: Attributing Spend to User and Feature </p><p>Day 70: Reconciling Cost Records With the Audit Trail </p><p>Day 71: Testing Cost Accuracy End to End </p><p>Day 72: Designing Budget Policy Limits </p><p>Day 73: Enforcing Limits Before Every API Call </p><p>Day 74: Handling a Budget-Exceeded Error Gracefully </p><p>Day 75: Testing Budget Enforcement Under Load </p><p>Day 76: Circuit Breaker States &#8212; Closed, Open, Half-Open </p><p>Day 77: Serving a Fallback When Budget Is Exceeded </p><p>Day 78: Phase 4 Review &#8212; Full Cost Governance Integration Test</p><div><hr></div><p><strong>Phase 5 &#8212; Advanced Reliability &amp; Capstone (Days 79&#8211;90)</strong> </p><p>Day 79: Failure Modes Unique to Multi-Component Systems </p><p>Day 80: Simulating Cascading Failures </p><p>Day 81: Chaos Testing the Pipeline Under Load </p><p>Day 82: Recovery Strategies for Partial Failure </p><p>Day 83: Stress-Testing the Full Reliability Stack </p><p>Day 84: Wiring All Four Systems Into One Deployment </p><p>Day 85: Writing System Documentation </p><p>Day 86: Assembling the Unified Observability Dashboard </p><p>Day 87: Performance Tuning and Latency Budget </p><p>Day 88: Clean-Machine Verification </p><p>Day 89: Writing Your &#8220;Definition of Done&#8221; </p><p>Day 90: Final Deployment and Capstone Demo</p><div><hr></div><p>Subscribe to unlock full access to the private GitHub repository &#8212; every line of code, every test, for all 90 days.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe&quot;,&quot;text&quot;:&quot;Subscribe Here&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/aieworks.substack.com/subscribe"><span>Subscribe Here</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Day 172 — Recurrent Neural Networks and LSTMs]]></title><description><![CDATA[What We Are Building Today]]></description><link>https://aieworks.substack.com/p/day-172-recurrent-neural-networks</link><guid isPermaLink="false">https://aieworks.substack.com/p/day-172-recurrent-neural-networks</guid><dc:creator><![CDATA[sysdai]]></dc:creator><pubDate>Sun, 26 Jul 2026 10:31:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3kRn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!30Ox!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 424w, /__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 848w, /__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 1272w, /__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!30Ox!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png" width="850" height="303" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:303,&quot;width&quot;:850,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41916,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/192194183?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 424w, /__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 848w, /__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 1272w, /__u/substackcdn.com/image/fetch/$s_!30Ox!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F585e663c-c844-4c03-8912-fccc0aa528f2_850x303.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2>What We Are Building Today</h2><p>By the end of this lesson you will have done three things that actually matter:</p><ul><li><p><strong>Understood why sequential data breaks standard networks</strong> &#8212; and what RNNs do differently at the architectural level.</p></li><li><p><strong>Decoded how LSTMs solve the vanishing gradient problem</strong> that crippled early RNNs, using three learned memory gates.</p></li><li><p><strong>Built a working character-level language model</strong> that generates text one character at a time &#8212; the same core idea behind every autocomplete you have ever used.</p></li><li><p><strong>Connected this to production systems</strong> at Google, Amazon, Apple, and Stripe that run LSTM-based models today.</p></li></ul><div><hr></div><h2>Why This Matters</h2><blockquote><p>Yesterday you built word embeddings &#8212; dense vector representations of meaning. Today you learn how to <strong>process sequences</strong> of those vectors over time. Every autocomplete suggestion you have typed, every translation you have read, every voice command your phone understood &#8212; all of it traces back to the sequential modeling ideas you will master today.</p><p>Even in the age of transformers, understanding RNNs and LSTMs is not optional. Production systems at Apple (on-device Siri models), Amazon (Alexa keyword detection), and trading firms (time-series forecasting) still run LSTM-based models because they are computationally lighter and interpretable. Knowing <strong>why</strong> transformers replaced RNNs requires understanding what RNNs got wrong. That knowledge gap is what separates engineers who implement AI from engineers who design it.</p></blockquote><div><hr></div><h2>Core Concepts</h2><h3>1. The Sequence Problem &#8212; Why Feedforward Networks Fall Short</h3><blockquote><p>A standard dense neural network treats every input as independent. Feed it &#8220;The cat sat on the ___&#8221; and it sees seven unrelated word vectors with no awareness of order. The network has no memory &#8212; it cannot know that &#8220;cat&#8221; modifies what comes after &#8220;sat.&#8221;</p><p>Think of it like reading a book where each page is shuffled randomly. You see the words, but the story is gone.</p><p>RNNs fix this by introducing a <strong>hidden state</strong> &#8212; a memory vector that gets updated at every time step. At step <code>t</code>, the network takes two inputs: the current word vector <code>x_t</code> and the hidden state from the previous step <code>h_{t-1}</code>. The output hidden state <code>h_t</code> encodes everything the network has seen up to position <code>t</code>.</p></blockquote><pre><code><code>h_t = tanh( W_hh &#183; h_{t-1}  +  W_xh &#183; x_t  +  b )
</code></code></pre><p>That shared weight matrix <code>W_hh</code> is identical at every time step &#8212; it is what makes RNNs parameter-efficient. A 10-step sequence and a 1,000-step sequence use exactly the same parameter count.</p><div><hr></div><h3>2. The Vanishing Gradient &#8212; The RNN&#8217;s Achilles Heel</h3><p>Here is the bitter irony. Backpropagation through an RNN means multiplying gradients across every time step. If those gradients are smaller than 1.0 (which they almost always are after a tanh), they shrink exponentially. By the time you are 50 steps back, the gradient is effectively zero.</p><p>The network literally forgets what happened earlier in the sequence. Training an RNN on a long paragraph and asking it to remember the subject from sentence one is like whispering a secret across 100 people &#8212; by the end, the message is noise.</p><blockquote><p><strong>Key Insight &#8212; The Vanishing Gradient Problem</strong> This is the single biggest reason vanilla RNNs struggle with dependencies longer than roughly 10&#8211;20 tokens. Each time step multiplies gradients together, and repeated multiplication of values less than 1.0 sends them toward zero exponentially fast. LSTMs were invented specifically to fix this.</p></blockquote><div><hr></div><h3>3. LSTMs &#8212; Learned Memory Management</h3><p>Long Short-Term Memory networks (Hochreiter &amp; Schmidhuber, 1997) solve the vanishing gradient problem with a second memory channel: the <strong>cell state</strong> <code>C_t</code>. Where the hidden state is your working memory, the cell state is long-term storage &#8212; and crucially, information flows through it additively, not multiplicatively.</p><p>LSTMs use three learned <strong>gates</strong> to control information flow:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!3kRn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!3kRn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png" width="1456" height="852" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:852,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3kRn!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e6031ba-94bb-4368-b623-7c1e20b5df6f_4100x2400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p></p></li></ul>
      <p>
          <a href="/__u/aieworks.substack.com/p/day-172-recurrent-neural-networks">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 21-22 : Deep Learning with TensorFlow & PyTorch (Days 141-154) ]]></title><description><![CDATA[Introduction]]></description><link>https://aieworks.substack.com/p/week-21-22-deep-learning-with-tensorflow</link><guid isPermaLink="false">https://aieworks.substack.com/p/week-21-22-deep-learning-with-tensorflow</guid><dc:creator><![CDATA[sdr]]></dc:creator><pubDate>Sat, 25 Jul 2026 00:03:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4NEB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e18b1c-6c9d-4f14-832f-3dbcc5f9351f_3600x1600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>Introduction</strong></h3><blockquote><p>Notebook lessons teach tensors, Keras <code>Sequential</code>, PyTorch <code>nn.Module</code>, and CIFAR-10 pipelines in isolation. A platform team needs the opposite: one service that runs curriculum demos for learning, a product API for experiments and async training, and a core library that both surfaces import&#8212;without duplicating model code across fourteen days.</p><p>This project composes Days 141&#8211;147 into a single stack: <code>week2122_python</code> (TensorFlow + PyTorch cores), FastAPI with Learning and Vision APIs, Postgres-backed runs, a React dashboard on port 3000, and HTTP 501 stubs for Days 148&#8211;154 until lesson code lands.</p></blockquote><p><strong>Takeaway:</strong> You will see how to unify two deep-learning frameworks behind one vision engine, expose them through intentional API layers, and keep long training off the request thread.</p><h3><strong>System Overview</strong></h3><p>Four bands carry the design:</p><ul><li><p><strong>React + Nginx (port 3000)</strong> &#8212; unified pipeline, curriculum studio, operations monitor with live metrics polling.</p></li><li><p><strong>FastAPI (port 8000)</strong> &#8212; <code>/api/v1/week2122/learn/day{N}/...</code> for Days 141&#8211;147 demos; <code>/api/v1/vision/...</code> for experiments, Iris comparison, and CIFAR jobs; lesson aliases at <code>/api/v1/learn/lesson-{N}/</code>.</p></li><li><p><code>week2122_python</code><strong> core</strong> &#8212; <code>core/tensorflow/</code> (tensors, autodiff, Iris Sequential, MNIST, CIFAR-10 <code>tf.data</code>), <code>core/pytorch/</code> (tensors, autograd, tabular nets), <code>core/vision_engine/pipeline.py</code> (product composition), <code>core/runtime/write_gate.py</code> (Day 145 freeze pattern).</p></li><li><p><strong>Postgres + Alembic</strong> &#8212; experiments, runs, artifacts; background CIFAR training via <code>cifar_train_job.py</code> and <code>asyncio.to_thread</code>.</p></li></ul><p>Day 141&#8211;143 material surfaces as TensorFlow learning routes and MNIST smoke tests. Days 144&#8211;146 feed PyTorch learning routes and the Iris compare product endpoint. Day 147 drives CIFAR baseline training and experiment registration.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!tdA_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 424w, /__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 848w, /__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 1272w, /__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!tdA_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png" width="1456" height="1003" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1003,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:453015,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/200410866?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 424w, /__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 848w, /__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 1272w, /__u/substackcdn.com/image/fetch/$s_!tdA_!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8c58c58-b8a9-4ee0-ae33-24fc7f1705ce_3600x2480.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Engine / Core System Design</strong></h3><p>The core package owns tensors, training loops, and evaluation metrics. The backend owns HTTP, SQLAlchemy repositories, and job scheduling. Routes validate caps from <code>settings</code> (max epochs, batch limits) and delegate synchronously or enqueue work.</p>
      <p>
          <a href="/__u/aieworks.substack.com/p/week-21-22-deep-learning-with-tensorflow">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Announcement - AI Reliability Engineering launch]]></title><description><![CDATA[Something new, starting today]]></description><link>https://aieworks.substack.com/p/announcement-ai-reliability-engineering</link><guid isPermaLink="false">https://aieworks.substack.com/p/announcement-ai-reliability-engineering</guid><dc:creator><![CDATA[AI Engineering]]></dc:creator><pubDate>Fri, 24 Jul 2026 10:36:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zDxb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Something new, starting today</h2><blockquote><p>If you&#8217;re working through the 180-Day AI &amp; ML Curriculum &#8212; or you&#8217;ve followed along this far &#8212; you already know how this newsletter works. Daily lessons, real code, no fluff. Lesson 180 of that curriculum wraps up on <strong>August 1, 2026</strong>.</p></blockquote><p>Starting today, there&#8217;s a second track running alongside it: <strong>AI Reliability Engineering.</strong></p><div><hr></div><h3>What it is</h3><blockquote><p>A 90-day build: a production-grade, multi-agent RAG system, built completely in the open, two lessons a week, every lesson shipping with working code.</p><p>The 180-day curriculum takes you from zero to being able to build an AI system. This course picks up from there &#8212; it&#8217;s about what happens <em>after</em> you ship something: how you evaluate it, deploy it safely, keep it observable, control its cost, and keep it reliable once real users are hitting it. That&#8217;s the gap most courses don&#8217;t cover.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!zDxb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!zDxb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png" width="462" height="380.4519230769231" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1199,&quot;width&quot;:1456,&quot;resizeWidth&quot;:462,&quot;bytes&quot;:528298,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/208315538?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zDxb!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc0c2070-e07f-426a-a6cc-e1f88f467f12_2720x2240.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe&quot;,&quot;text&quot;:&quot;Subscribe Now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/aieworks.substack.com/subscribe"><span>Subscribe Now</span></a></p><div><hr></div><h3>What we build</h3><p>Five coordinated agents &#8212; query understanding, retrieval, synthesis, a critic, and response formatting &#8212; wired into one working pipeline. Not a toy demo. A real system with real failure modes, measured and fixed in public.</p><p><strong>Phase 0 (Days 1&#8211;12): Build the system from scratch.</strong> Ingestion, embeddings, hybrid search, reranking, the full multi-agent pipeline, and a baseline read on how it actually breaks. <strong>All 12 lessons are free.</strong></p><p>After Day 12, the course goes deep on production concerns: evaluation engineering (Days 13&#8211;31), deployment safety (32&#8211;48), observability and audit trails (49&#8211;66), cost governance (67&#8211;78), advanced reliability patterns (79&#8211;83), closing with a full capstone deployment (84&#8211;90).</p><p>Every lesson includes:</p><ul><li><p>A working, tested code package &#8212; not pseudocode</p></li><li><p>A full written breakdown of the design decisions and trade-offs</p></li><li><p>Diagrams showing the architecture and where we are in the 90-day arc</p></li></ul><div><hr></div><h3>The schedule</h3><p>New lessons publish <strong>every Monday and Thursday</strong>, starting Day 1 shortly after this post.</p><ul><li><p><strong>Days 1&#8211;12: free, permanently.</strong> No rush to catch up before a paywall closes.</p></li><li><p><strong>Days 13&#8211;90: paid.</strong></p></li></ul><div><hr></div><h3>Who this is for</h3><ul><li><p>Anyone who finished the 180-day curriculum and wants the next step &#8212; the production-reliability side of AI engineering</p></li><li><p>Engineers with a working RAG or multi-agent system who need it to hold up under real traffic, not just in a demo</p></li><li><p>Anyone who learns better from working code and real failure logs than from slide decks</p></li></ul><blockquote><p>You don&#8217;t need to have finished the 180-day course to start this one &#8212; Phase 0 assumes no prior lessons from this newsletter, just general familiarity with building software.</p></blockquote><div><hr></div><h3>A note on paid</h3><p>Days 1&#8211;12 stay free forever. From Day 13, lessons are for paid subscribers, under the same plans as the rest of the newsletter:</p><p>Plan Price Includes Monthly $39/month - Full curriculum access, private GitHub repo</p><p>Annual $279.30/year - Same, plus free ebook + 1 month systemdrd.com portal access</p><p>Lifetime Access $699/year - Unlimited access to entire course library, past/present/future</p><p>Free$0 - Basic free lessons only, no private repo access</p><div><hr></div><h3>First lesson</h3><p>Day 1 &#8212; the architecture lesson, why the system is shaped the way it is before a single line of implementation code &#8212; publishes shortly.</p><p>Subscribe (free through Day 12) and follow the code on GitHub. See you in Day 1.</p>]]></content:encoded></item><item><title><![CDATA[Day 171 — Word Embeddings: Word2Vec and GloVe]]></title><description><![CDATA[What We Cover Today]]></description><link>https://aieworks.substack.com/p/day-171-word-embeddings-word2vec</link><guid isPermaLink="false">https://aieworks.substack.com/p/day-171-word-embeddings-word2vec</guid><dc:creator><![CDATA[sysdai]]></dc:creator><pubDate>Thu, 23 Jul 2026 08:30:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!R0aa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We Cover Today</h2><ul><li><p>Understand why raw text is unusable by neural networks and what embeddings solve</p></li><li><p>Explore the architecture of Word2Vec (Skip-gram and CBOW) and GloVe</p></li><li><p>Implement embedding lookups, similarity search, and semantic arithmetic in Python</p></li><li><p>Connect word vectors to production NLP systems at scale</p></li></ul><div><hr></div><h2>Why This Matters</h2><blockquote><p>Every search engine, recommendation system, and large language model you have ever used runs on one foundational idea: words can be represented as points in high-dimensional space, and words with similar meanings cluster near each other. Without this, a model cannot know that &#8220;bank&#8221; and &#8220;financial institution&#8221; refer to similar concepts, or that &#8220;Paris&#8221; relates to &#8220;France&#8221; the same way &#8220;Tokyo&#8221; relates to &#8220;Japan.&#8221;</p><p>Word embeddings are the bridge between human language and mathematical computation. They are the reason modern NLP works at all.</p></blockquote><div><hr></div><h2>Core Concepts</h2><h3>The Problem: Language Has No Natural Numbers</h3><blockquote><p>Neural networks process tensors &#8212; grids of floating point numbers. The letter &#8220;A&#8221; means nothing to a matrix multiplication. One-hot encoding is the naive fix: represent 50,000 vocabulary words as a 50,000-dimensional vector where only one position is 1. It works, but it is catastrophically wasteful and semantically blind. &#8220;Dog&#8221; and &#8220;canine&#8221; have zero overlap in one-hot space even though they mean the same thing.</p></blockquote><h3>Word2Vec: Learning Meaning From Context</h3><p>Word2Vec, introduced by Google researchers in 2013, learns embeddings by training a shallow neural network on a deceptively simple task: given a word, predict its neighbors, or given neighbors, predict the center word.</p><p><strong>Skip-gram</strong> &#8212; Takes a center word, predicts surrounding context words. Works well for infrequent words.</p><p><strong>CBOW (Continuous Bag of Words)</strong> &#8212; Takes surrounding context words, predicts the center word. Faster, better for frequent words.</p><p>The key insight is that the network never actually cares about the prediction task. What matters is the weight matrix it builds while solving that task &#8212; a 300-dimensional vector per word that encodes semantic relationships the model learned purely from co-occurrence patterns in billions of text examples.</p><blockquote><p><strong>The &#8220;aha&#8221; moment.</strong> After training on billions of words, the geometry that emerges is astonishing. King minus man plus woman equals queen. Paris minus France plus Germany equals Berlin. These are not programmed rules. The geometry emerges entirely from statistics.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!R0aa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!R0aa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png" width="1456" height="923" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:923,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 424w, /__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 848w, /__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!R0aa!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dfd9af5-d463-49a9-9526-0724ad7a7970_4100x2600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="/__u/aieworks.substack.com/p/day-171-word-embeddings-word2vec">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 170: Text Preprocessing and Tokenization]]></title><description><![CDATA[What We Cover Today]]></description><link>https://aieworks.substack.com/p/day-170-text-preprocessing-and-tokenization</link><guid isPermaLink="false">https://aieworks.substack.com/p/day-170-text-preprocessing-and-tokenization</guid><dc:creator><![CDATA[sysdai]]></dc:creator><pubDate>Mon, 20 Jul 2026 08:30:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xaJc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We Cover Today</h2><ul><li><p>Why raw text is useless to a neural network, and what preprocessing actually does</p></li><li><p>The tokenization spectrum: from character-level splits to subword vocabularies</p></li><li><p>How modern NLP pipelines (BERT, GPT, LLaMA) tokenize text at production scale</p></li><li><p>Building a preprocessing pipeline from scratch: cleaning, normalization, tokenization, and encoding</p></li><li><p>Where this sits in a live AI system&#8217;s data flow</p></li></ul><div><hr></div><h2>Why This Matters</h2><blockquote><p>Yesterday you got the 10,000-foot view of NLP. Today you go one level down into the engine room. Every language model &#8212; whether it&#8217;s the BERT model powering Google Search or the GPT-4 backbone behind ChatGPT &#8212; starts with the exact same problem: text is just a sequence of Unicode characters, and neural networks only understand numbers.</p><p>Text preprocessing is the translation layer. It transforms messy, human-written language into clean, structured numerical representations that a model can actually learn from. Get this wrong and your model learns noise. Get it right and you&#8217;ve handed your model a consistent, information-dense signal. At companies like OpenAI, Hugging Face, and Cohere, entire teams are dedicated to tokenizer design &#8212; not because it&#8217;s glamorous, but because a bad tokenizer caps the ceiling on everything built on top of it.</p></blockquote><p><strong>Tokenizers are not utilities. They are model components.</strong></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xaJc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 424w, /__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 848w, /__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xaJc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png" width="1456" height="817" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:817,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 424w, /__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 848w, /__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xaJc!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ee0af34-3afe-490a-b21f-d18d2a7e6981_4100x2300.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Core Concepts</h2><h3>1. The Raw Text Problem</h3><p>Neural networks are mathematical functions. They compute dot products, matrix multiplications, and nonlinear activations &#8212; all operations that require floating-point numbers. The sentence <code>"The model crashed at 3am!!"</code> has no numeric form until you create one.</p><p>Before you even think about tokenization, the text itself needs to be cleaned. Production text is full of HTML artifacts (<code>&amp;amp;</code>, <code>&lt;br/&gt;</code>), inconsistent Unicode, mixed case, multiple whitespace characters, and null bytes from encoding issues. These irregularities don&#8217;t just add noise &#8212; they fragment your vocabulary. <code>"Apple"</code>, <code>"apple"</code>, and <code>"APPLE"</code> would all become different tokens if you skip lowercasing. That&#8217;s three entries in your vocabulary table for the same semantic concept.</p><p>The cleaning phase handles:</p><ul><li><p>Lowercasing (where appropriate)</p></li><li><p>HTML unescaping and tag stripping</p></li><li><p>Unicode normalization (NFC vs NFD)</p></li><li><p>Punctuation handling</p></li><li><p>Whitespace collapsing</p></li></ul><p>This is not optional housekeeping &#8212; it directly determines vocabulary size, which determines model parameter count.</p><div><hr></div><h3>2. Tokenization Strategies</h3><p>Tokenization is the act of splitting text into discrete units called tokens. Three main strategies exist, and each makes a different tradeoff.</p><p><strong>Word-level tokenization</strong> splits on whitespace and punctuation. Simple and interpretable &#8212; <code>"the cat sat"</code> becomes <code>["the", "cat", "sat"]</code>. The fatal flaw: out-of-vocabulary (OOV) words. If your training data never contained <code>"transformer"</code>, the model has no representation for it at inference time. Vocabularies also explode &#8212; English alone has 500,000+ words, and that&#8217;s before you add technical jargon, names, and typos.</p><p><strong>Character-level tokenization</strong> splits into individual characters. No OOV problem by definition &#8212; every character is in your alphabet. But sequences become extremely long, and the model must learn to compose meaning from scratch, which requires vastly more training data and compute.</p><p><strong>Subword tokenization</strong> is the practical solution that modern systems use. The insight is elegant: keep common words as single tokens, but split rare or unknown words into meaningful sub-pieces. The word <code>"tokenization"</code> might become <code>["token", "ization"]</code>. The word <code>"unhappiness"</code> might become <code>["un", "happiness"]</code>. The model sees morphemic structure rather than opaque character soup.</p><blockquote><p><strong>Two dominant subword algorithms:</strong></p><p><strong>Byte Pair Encoding (BPE)</strong> &#8212; used by GPT-2, GPT-3, GPT-4, and LLaMA. Starts with character-level tokenization and iteratively merges the most frequent adjacent pairs until a target vocabulary size is reached.</p><p><strong>WordPiece</strong> &#8212; used by BERT. Same general idea as BPE, but uses a likelihood-based merge criterion instead of raw frequency. Results in a vocabulary of 30,000&#8211;100,000 tokens that covers essentially any input text without OOV issues.</p></blockquote><div><hr></div><h3>3. Vocabulary and Encoding</h3><p>Once you have tokens, you need to map them to integers &#8212; this is the vocabulary lookup. The vocabulary is a dictionary: <code>{"the": 0, "cat": 1, ...}</code>. Every token in your corpus gets a unique integer ID. Special tokens are added explicitly to handle structural positions in a sequence.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Agt3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 424w, /__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 848w, /__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Agt3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png" width="840" height="350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:350,&quot;width&quot;:840,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50135,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/191960002?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 424w, /__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 848w, /__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Agt3!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa27e2547-a36c-4fc5-b23d-fa878e4f8d01_840x350.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>After encoding, each sentence is a list of integers. This is the actual input to the embedding layer &#8212; not the words themselves.</p><div><hr></div><h3>4. Attention Masks and the Padding Problem</h3><p>Batching sequences requires them to be the same length. But sentences are not. The solution is to pad shorter sequences with <code>[PAD]</code> tokens up to the maximum sequence length in the batch, then provide an <strong>attention mask</strong> &#8212; a binary vector marking which positions are real tokens (<code>1</code>) and which are padding (<code>0</code>).</p><p>The transformer&#8217;s attention mechanism uses this mask to ignore padded positions during computation. This is architectural plumbing that most tutorials skip, but it&#8217;s fundamental to how batched inference works at production scale.</p><div><hr></div><h2>Component Architecture</h2><p>The preprocessing pipeline has five distinct stages. Each stage is stateless except the Tokenizer and Encoder &#8212; both depend on a trained vocabulary file (typically a <code>.json</code> or <code>.txt</code> vocab file loaded at startup). In production systems, this vocabulary file is versioned alongside the model weights.</p><pre><code><code>Raw Input Text
      |
      v
+---------------------+
|   Text Cleaner      |  --&gt; strip HTML, normalize unicode, collapse whitespace
+---------------------+
      |
      v
+---------------------+
|   Normalizer        |  --&gt; lowercase, remove/keep punctuation, handle numbers
+---------------------+
      |
      v
+---------------------+
|   Tokenizer         |  --&gt; split into subword tokens using BPE/WordPiece vocab
+---------------------+
      |
      v
+---------------------+
|   Encoder           |  --&gt; map tokens --&gt; integer IDs via vocabulary lookup
+---------------------+
      |
      v
+---------------------+
|   Padder/Masker     |  --&gt; pad to batch length, generate attention masks
+---------------------+
      |
      v
 Tensor [batch_size, seq_len]  &lt;--&gt;  Model Input
</code></code></pre><blockquote><p><strong>Critical production note:</strong> Swapping a vocabulary file without updating the corresponding model weights is a silent failure mode. The model receives a different token distribution than it was trained on and produces degraded outputs with no error thrown. Tokenizer and weights are always versioned together.</p></blockquote><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!SGVw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!SGVw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png" width="1456" height="852" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:852,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!SGVw!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdd73daa-83fb-4830-b6b1-149a219eeaa2_4100x2400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Real-World Connection</h2><p>At Stripe, every merchant-facing text field &#8212; chargeback descriptions, dispute notes, fraud explanations &#8212; passes through a preprocessing pipeline before hitting the risk classification model. The tokenizer there is trained on financial domain text to ensure that terms like <code>"ACH reversal"</code> and <code>"NSF fee"</code> are preserved as meaningful units rather than split arbitrarily.</p><p>At Google, the search query tokenizer handles 8.5 billion queries per day, many in low-resource languages where subword tokenization is the only viable strategy. The design of that tokenizer &#8212; its vocabulary size, its handling of numerals and punctuation &#8212; directly affects ranking quality for billions of searches.</p><p>Tokenizers are not utilities. They are model components.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div><hr></div><h1>Implementation: Build, Test and Demo</h1><h2>Github Link:</h2><p><a href="https://github.com/sysdr/aiml/tree/main/day170/day170_tokenization">https://github.com/sysdr/aiml/tree/main/day170/day170_tokenization</a></p><blockquote><p><strong>What you will build:</strong> A complete, five-stage NLP preprocessing pipeline in Python, ending with batched tensors ready to feed directly into a BERT or GPT-2 model. All code lives in <code>lesson_code.py</code>. The test suite (20 pytest tests) lives in <code>test_lesson.py</code>. </p></blockquote><div><hr></div><p><strong>Expected output:</strong> a confirmation message and a listing of the five files created inside <code>day170_tokenization/</code>.</p><div><hr></div><h2>Step 1 &#8212; Set Up the Environment</h2><p> Installs all pinned dependencies, and downloads the NLTK and spaCy data required for tokenization exercises.<br></p><p><strong>Expected output:</strong> <code>"Environment ready."</code> followed by the activation command. If you see a warning about a missing Rust compiler, ignore it &#8212; the Hugging Face tokenizers library ships pre-compiled wheels.</p><blockquote><p><strong>With Docker instead:</strong> <code>docker build -t day170 . &amp;&amp; docker run --rm day170</code></p></blockquote><div><hr></div><h2>Step 2 &#8212; Run the Lesson Demo</h2><p><code>lesson_code.py</code> runs all seven sections of the pipeline in sequence, printing labeled output for each stage. Read through the terminal output as it runs &#8212; every printed block corresponds to a Core Concept section above.</p><pre><code><code>python lesson_code.py
</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!sl_S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 424w, /__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 848w, /__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!sl_S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png" width="857" height="662" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d947fa52-e764-4978-be6f-451e764a5e87_857x662.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:662,&quot;width&quot;:857,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:113006,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://aieworks.substack.com/i/191960002?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 424w, /__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 848w, /__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sl_S!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd947fa52-e764-4978-be6f-451e764a5e87_857x662.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Step 3 &#8212; Run the Test Suite</h2><p>The test suite has 20 tests organized into six classes, each covering a distinct stage of the pipeline.</p><pre><code><code>pytest test_lesson.py -v
</code></code></pre><p><strong>Expected result:</strong> 20 passed in approximately 8 seconds. The first run will download the BERT and GPT-2 tokenizer files from Hugging Face (~500 MB total). Subsequent runs use the local cache.</p><p>To also see line coverage:</p><pre><code><code>pytest test_lesson.py -v --cov=lesson_code --cov-report=term-missing
</code></code></pre><div><hr></div><h2>Step 4 &#8212; Key Exercises to Try Yourself</h2><h3>Exercise A &#8212; Compare tokenization of a medical vs. general sentence</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;e406a1f3-cce7-4bea-aed0-d714cdd88b80&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from lesson_code import load_bert_tokenizer, bert_tokenize

tok = load_bert_tokenizer()
medical = "The patient presented with tachycardia and diaphoresis."
general = "The patient felt dizzy and was sweating."

print(bert_tokenize(tok, medical))
print(bert_tokenize(tok, general))
</code></pre></div><p>Pay attention to how the medical terms fragment into subwords. This is exactly why clinical NLP teams train domain-specific tokenizers.</p><div><hr></div><h3>Exercise B &#8212; Inspect the token distribution of a larger corpus</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;61b89dec-71df-4f37-96c0-68c884b32f83&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from lesson_code import load_bert_tokenizer, token_length_analysis

sentences = [
    "BERT uses WordPiece tokenization.",
    "GPT uses byte pair encoding.",
    "Both are subword tokenization methods.",
    "The quick brown fox jumps over the lazy dog.",
    "Tokenizers convert text into numbers.",
]

tok = load_bert_tokenizer()
print(token_length_analysis(tok, sentences))
</code></pre></div><p>Change <code>max_length</code> in <code>bert_encode_batch</code> to the <code>max</code> value from this analysis. Compare the number of padding tokens before and after.</p><div><hr></div><h3>Exercise C &#8212; Find where BERT and GPT-2 agree</h3><pre><code><code>from lesson_code import load_bert_tokenizer, load_gpt2_tokenizer, compare_tokenizers

words = ["running", "cats", "the", "AI", "unhappiness"]
result = compare_tokenizers(load_bert_tokenizer(), load_gpt2_tokenizer(), words)

for word, splits in result.items():
    match = splits['bert_wordpiece'] == splits['gpt2_bpe']
    print(f"{word:15s}  match={match}  bert={splits['bert_wordpiece']}")
</code></code></pre><p>Common English function words like <code>"the"</code> and <code>"cats"</code> produce identical single-token results in both tokenizers. Rare or morphologically complex words diverge.</p><div><hr></div><h2>Verification Checklist</h2><p>Before moving to Day 171, confirm you can answer each of these from memory or by looking at <code>lesson_code.py</code>:</p><ol><li><p>Why does skipping lowercasing inflate vocabulary size?</p></li><li><p>What does the <code>##</code> prefix mean in a WordPiece token list?</p></li><li><p>What is the integer ID for <code>[CLS]</code> in <code>bert-base-uncased</code>? (Hint: check <code>bert_tok.cls_token_id</code>)</p></li><li><p>What shape does <code>bert_encode_batch</code> return for 3 sentences padded to <code>max_length=32</code>?</p></li><li><p>Why do BERT and GPT-2 produce different splits for the same word?</p></li><li><p>What does a <code>0</code> in the <code>attention_mask</code> indicate, and why does it matter?</p></li><li><p>If you swap the <code>vocab.json</code> file without updating model weights, what happens at inference?</p></li></ol><div><hr></div><h2>Working Code Demo:</h2><div id="youtube2-E-xi636mvY4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;E-xi636mvY4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/E-xi636mvY4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://aieworks.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On "AI Engineering" is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Day 169 · Introduction to NLP — Teaching Machines to Read]]></title><link>https://aieworks.substack.com/p/day-169-introduction-to-nlp-teaching</link><guid isPermaLink="false">https://aieworks.substack.com/p/day-169-introduction-to-nlp-teaching</guid><dc:creator><![CDATA[sysdai]]></dc:creator><pubDate>Fri, 17 Jul 2026 03:34:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ZILe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ZILe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 424w, /__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 848w, /__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_webp, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ZILe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png" width="1456" height="781" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:781,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_424, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 424w, /__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_848, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 848w, /__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_1272, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ZILe!, /__u/aieworks.substack.com/w_1456, /__u/aieworks.substack.com/c_limit, /__u/aieworks.substack.com/f_auto, /__u/aieworks.substack.com/q_auto:good, /__u/aieworks.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac96a5f9-b441-46bd-9b07-f9d079645297_4100x2200.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="/__u/aieworks.substack.com/p/day-169-introduction-to-nlp-teaching">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>