<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[Avani Chaskar]]></title><description><![CDATA[Get insights into using AI for real world scenarios.]]></description><link>https://avanichaskar.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!DSLe!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Favanichaskar.substack.com%2Fimg%2Fsubstack.png</url><title>Avani Chaskar</title><link>https://avanichaskar.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 04 Sep 2026 11:08:21 GMT</lastBuildDate><atom:link href="/__u/avanichaskar.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Avani Chaskar]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[avanichaskar@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[avanichaskar@substack.com]]></itunes:email><itunes:name><![CDATA[Avani Chaskar]]></itunes:name></itunes:owner><itunes:author><![CDATA[Avani Chaskar]]></itunes:author><googleplay:owner><![CDATA[avanichaskar@substack.com]]></googleplay:owner><googleplay:email><![CDATA[avanichaskar@substack.com]]></googleplay:email><googleplay:author><![CDATA[Avani Chaskar]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[How to Validate Nested JSON with Pydantic (Real Examples)]]></title><description><![CDATA[Part 3 of 5 - Your data isn't flat. Neither should your validation be, how to check every layer without writing loops.]]></description><link>https://avanichaskar.substack.com/p/how-to-validate-nested-json-with</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/how-to-validate-nested-json-with</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Wed, 02 Sep 2026 10:29:48 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/54b1b242-f31a-4fb5-bf52-c21f45314b89_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Quick recap before we start: Part 2 was about making <em>one</em> object strict: every field, the right type, the right length, the right pattern. </p><p>But here&#8217;s the thing: real data is never just one flat object. Think about it, an order has a customer <em>inside</em> it. That customer might have an address <em>inside</em> them. It&#8217;s boxes inside boxes. </p><p>Today we learn how Pydantic handles that. Stick with me, it&#8217;s easier than it sounds.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p><div><hr></div><h2>Why Flat Validation Misses Nested Errors</h2><p>Say a refund request comes in, and it has an order attached to it:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;031e1912-26bd-4e21-8651-fcc759fa175e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">response = {
    "name": "Alice",
    "email": "alice@example.com",
    "intent": "refund",
    "order": {
        "id": "ORD-004821",
        "items": [
            {"sku": "SKU-1", "price": 29.99, "qty": 2},
            {"sku": "SKU-2", "price": -15.00, "qty": 1}
        ]
    }
}</code></pre></div><p>Spot the problem? Look at the second item. <code>price: -15.00</code>. Negative. That&#8217;s not a &#8220;type&#8221; problem - it&#8217;s still a number. But it&#8217;s obviously wrong. Nobody sells something for negative money.</p><p>If your model doesn&#8217;t know to check <em>inside</em> that <code>order</code> dict, this slips through. And if it reaches your refund logic, congrats, you just refunded someone extra money.</p><p>The lesson: flat validation only checks the outside layer. It doesn&#8217;t look inside boxes.</p><h2>Pydantic Nested Models: Put a Model Inside a Model</h2><blockquote><p><em>A Pydantic model can be a field inside another Pydantic model.</em> </p></blockquote><p>That&#8217;s it. That&#8217;s nesting.</p><p>Build it from the smallest piece outward, like Russian nesting dolls:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;29825d37-1e2b-4eb8-a554-fb7e538ea047&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic import BaseModel, PositiveFloat, PositiveInt

# smallest doll first
class OrderItem(BaseModel):
    sku: str
    price: PositiveFloat   # rejects negative or zero automatically
    qty: PositiveInt

# next doll &#8212; contains a LIST of the smaller doll
class Order(BaseModel):
    id: str
    items: list[OrderItem]

# biggest doll &#8212; contains the Order doll
class SupportRequest(BaseModel):
    name: str
    email: str
    order: Order</code></pre></div><p>Observe, one line checks <em>everything</em>, across all the layers:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;aacab36d-8210-4f62-b773-67d575a248d5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">SupportRequest.model_validate(response)</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;332f5ce8-d7a8-46cb-af6b-ff53fd9c931b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">ValidationError: 1 validation error for SupportRequest
order.items.1.price
  Input should be greater than 0</code></pre></div><p>The error path is : <code>order</code> &#8594; <code>items</code> &#8594; item number <code>1</code> &#8594; <code>price</code>. Pydantic just told you exactly which piece, inside which piece, has the problem. You wrote zero loops. Zero <code>if</code> statements. It just knows.</p><div><hr></div><h2>Cross-Field Validation with <code>model_validator</code></h2><p>Single-field checks (like <code>PositiveFloat</code>) are great, but some rules aren&#8217;t about one field. </p><p>Example: &#8220;an order can&#8217;t be empty.&#8221; That&#8217;s not about <em>any one</em> item - it&#8217;s about the <em>whole list</em>.</p><p>For that, you use <code>model_validator</code>. Think: &#8220;wait until every field is checked individually, THEN let me look at the whole picture.&#8221;</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;690d23e1-ac1d-47ad-865f-1c172df7ca12&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic import BaseModel, model_validator

class Order(BaseModel):
    id: str
    items: list[OrderItem]

    @model_validator(mode="after")
    def items_not_empty(self):
        if not self.items:
            raise ValueError("An order must have at least one item")
        return self</code></pre></div><p><code>mode="after"</code> = <em>&#8220;run this once everything else already passed.&#8221;</em> </p><div><hr></div><h2><code>computed_field</code>: Never Trust a Client-Sent Total</h2><p>Quick question: should a customer be allowed to <em>tell</em> you the total price of their order? No! You calculate it yourself, from the items, so it can never be faked or wrong.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;8fcfac9c-1ffc-4d1a-a198-ca5fd10e8582&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic import BaseModel, computed_field

class Order(BaseModel):
    id: str
    items: list[OrderItem]

    @computed_field
    @property
    def total(self) -&gt; float:
        return round(sum(item.price * item.qty for item in self.items), 2)</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;990a8d74-3dbd-4455-ba1b-6dfe0bd36e61&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">order.total  # always correct, always recalculated</code></pre></div><p>Nobody sends you <code>total</code>. Nobody can fake it. It&#8217;s just math, done live, from data you already trust.</p><div><hr></div><h2>Exporting Validated Data with <code>model_dump()</code></h2><p>So far we&#8217;ve only talked about checking data coming <em>in</em>. But eventually you need to send that model back out: to an API response, to a database, wherever.</p><p>Two methods, and they&#8217;re basically the same idea in two formats:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;92ecc1c8-4bdc-4ddf-8be9-936402b8f714&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">order.model_dump()        # &#8594; a normal Python dict
order.model_dump_json()   # &#8594; a JSON string</code></pre></div><p>Same model, same rules, just exported.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-uRc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-uRc!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!-uRc!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!-uRc!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-uRc!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-uRc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1094989,&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://avanichaskar.substack.com/i/213835862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.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_!-uRc!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!-uRc!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!-uRc!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-uRc!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad82903b-9456-4fac-a4c0-3c230cc937d9_1536x1024.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></p><h2>TL;DR: Pydantic Nested Models in 4 Lines</h2><ol><li><p>A model can contain another model. That&#8217;s nesting.</p></li><li><p>Nested validation checks every layer automatically: no loops needed.</p></li><li><p><code>model_validator</code> checks rules across multiple fields, after individual checks pass.</p></li><li><p><code>computed_field</code> calculates values instead of trusting input: and <code>model_dump()</code> exports the result.</p></li></ol><div><hr></div><h2>Next Up: Pydantic Settings and Environment Variables</h2><p>Next up: where does <em>your app&#8217;s own</em> configuration come from? Environment variables, <code>.env</code> files, secrets &#8212; and how Pydantic keeps those just as strict as everything else. See you there.</p><div><hr></div><p>Thanks for reading. If this helped, hit subscribe - part 4 lands next in this series.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Pydantic Fields Explained: A Model Is Only as Strict as Its Fields]]></title><description><![CDATA[Pydantic Part 2 of 5 - Type hints tell you the shape. Constraints and validators tell you if it's actually right.]]></description><link>https://avanichaskar.substack.com/p/a-model-is-only-as-strict-as-its</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/a-model-is-only-as-strict-as-its</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Tue, 01 Sep 2026 12:12:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/189b1180-259a-4f2b-b023-fabb2b9478cd_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In part 1, a missing email crashed our support bot three functions downstream. We fixed it with a type hint. Then someone submitted an empty name, a fake order ID, and a message longer than our database column &#8212; and every field passed.</p><p>A type hint checks shape. It doesn&#8217;t check sense. In this article, we fix that gap: <code>Field()</code> constraints, built-in types like <code>EmailStr</code> and <code>HttpUrl</code>, aliases for messy real-world field names, and custom validators for rules a type alone can&#8217;t express.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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">Thanks for reading! Subscribe for free to receive new posts related to AI Engineering.</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><div><hr></div><h2>The next bug</h2><p>Someone submits this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;0af1f469-bebd-4c40-ad96-aa6a5bc9298f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">response = {
    "name": "",
    "email": "alice@example.com",
    "intent": "refund",
    "order_id": "not-a-real-id",
    "message": "x" * 5000
}</code></pre></div><p>Every field is the <em>right type</em>. <code>name</code> is a string. <code>order_id</code> is a string. <code>message</code> is a string. Nothing here fails a basic <code>SupportRequest</code> model.</p><p>But it&#8217;s still garbage. An empty name. A fake order ID. A 5,000-character message that&#8217;ll blow up your database column or your LLM&#8217;s context window.</p><div class="callout-block" data-callout="true"><p><strong>A type hint tells you the shape of the data. It says nothing about whether the data makes sense.</strong></p></div><h2>Field() adds the rules</h2><p>Pydantic&#8217;s <code>Field()</code> lets you attach constraints directly to a field, not just a type.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;f35d8c42-3e40-4498-a221-e2309f04d9f4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic import BaseModel, EmailStr, Field
from typing import Literal

class SupportRequest(BaseModel):
    name: str = Field(min_length=1, max_length=80)
    email: EmailStr
    intent: Literal["refund", "complaint", "question"]
    order_id: str = Field(pattern=r"^ORD-\d{6}$")
    message: str = Field(max_length=1000)</code></pre></div><p>Now the same bad data fails immediately:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;0f3293b8-16f8-4cb0-9a9c-4e016f284783&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">SupportRequest.model_validate(response)</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;d576e782-c24c-4a8a-8a3c-f48a62a79f19&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">ValidationError: 3 validation errors for SupportRequest
name
  String should have at least 1 character
order_id
  String should match pattern '^ORD-\d{6}$'
message
  String should have at most 1000 characters</code></pre></div><p>Three problems, three clear errors. No manual checks. No wall of <code>if</code> statements.</p><div><hr></div><h2>Built-in types do the common cases for you</h2><p>You don&#8217;t need to hand-write patterns for things Pydantic already knows.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;8d3d20ca-8555-4f94-b673-fb16bc99b7f4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic import BaseModel, EmailStr, HttpUrl, PositiveInt

class Customer(BaseModel):
    email: EmailStr
    website: HttpUrl | None = None
    loyalty_points: PositiveInt = 0</code></pre></div><ul><li><p><code>EmailStr</code> &#8212; rejects anything that isn&#8217;t a valid email shape.</p></li><li><p><code>HttpUrl</code> &#8212; rejects anything that isn&#8217;t a valid URL.</p></li><li><p><code>PositiveInt</code> &#8212; rejects zero and negative numbers.</p></li></ul><p>These aren&#8217;t clever tricks. They&#8217;re common validation rules someone already wrote correctly, so you don&#8217;t have to.</p><div><hr></div><h2>Defaults and aliases</h2><p>Real data doesn&#8217;t always match your field names. An external API might send <code>customerName</code> instead of <code>name</code>. Pydantic handles this with <code>alias</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;4c334a2d-6a56-4213-a6fa-051577262985&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">class Customer(BaseModel):
    name: str = Field(alias="customerName")
    loyalty_points: int = Field(default=0)</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;4252717c-a5c1-4c4e-8ceb-bb2bf55dc67e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">Customer.model_validate({"customerName": "Alice"})
# Customer(name="Alice", loyalty_points=0)</code></pre></div><p>The alias maps incoming data to your clean field name. The default fills in what&#8217;s missing. Neither requires a single <code>if</code> statement.</p><div><hr></div><h2>When a type hint isn&#8217;t enough: custom validators</h2><p>Some rules can&#8217;t be expressed as a type or a simple constraint. Say refund requests need a reason, but complaints don&#8217;t.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;8a096ea1-0853-49aa-aff6-47e72358958a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic import BaseModel, field_validator

class SupportRequest(BaseModel):
    intent: str
    message: str

    @field_validator("message")
    @classmethod
    def refund_needs_detail(cls, value, info):
        intent = info.data.get("intent")
        if intent == "refund" and len(value) &lt; 10:
            raise ValueError("Refund requests need at least 10 characters of detail")
        return value</code></pre></div><p>This is regular Python. An <code>if</code> statement, a <code>raise</code>. The difference is <em>where</em> it lives &#8212; inside the model, next to the field it checks, running automatically every time the model is built.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Pz2_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Pz2_!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png 424w, /__u/substackcdn.com/image/fetch/$s_!Pz2_!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png 848w, /__u/substackcdn.com/image/fetch/$s_!Pz2_!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Pz2_!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Pz2_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1061914,&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://avanichaskar.substack.com/i/213694001?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.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_!Pz2_!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png 424w, /__u/substackcdn.com/image/fetch/$s_!Pz2_!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png 848w, /__u/substackcdn.com/image/fetch/$s_!Pz2_!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Pz2_!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523f37b9-c717-4ef5-85d7-a45b62d1e475_1774x887.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>The order things happen in :</p><ol><li><p>Pydantic checks the type first. Wrong type, it stops there.</p></li><li><p>Then it checks <code>Field()</code> constraints &#8212; length, pattern, range.</p></li><li><p>Then it runs your custom validators, in the order you defined them.</p></li><li><p>If everything passes, you get a validated object. If not, you get every error at once, not just the first one.</p></li></ol><p>That last point matters. You don&#8217;t fix one error, resubmit, hit the next error, resubmit again. Pydantic collects everything wrong in a single pass.</p><div><hr></div><h2>What we covered</h2><ul><li><p>A type hint checks shape. <code>Field()</code> checks whether the data makes sense.</p></li><li><p>Built-in types like <code>EmailStr</code> and <code>HttpUrl</code> cover common cases so you don&#8217;t hand-roll patterns.</p></li><li><p><code>alias</code> and <code>default</code> handle messy real-world field names and missing values.</p></li><li><p>Custom validators handle rules a type or constraint can&#8217;t express &#8212; and they run automatically.</p></li></ul><div><hr></div><h2>What&#8217;s next</h2><p>Real data isn&#8217;t flat. A support ticket has an attached order. An order has a list of items. Next, we go into nested models &#8212; how to validate an object made of other objects, and how to compute fields that depend on more than one value.</p><div><hr></div><p>Thanks for reading. If this helped, hit subscribe &#8212; part 3 lands next in this series. Drop a comment for what use cases do you use pydantic : <br></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/a-model-is-only-as-strict-as-its/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/a-model-is-only-as-strict-as-its/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[Pydantic: Stop Trusting LLM Output. Start Validating It.]]></title><description><![CDATA[Part 1 of 5: the boundary between messy AI output and code that actually works.]]></description><link>https://avanichaskar.substack.com/p/pydantic-stop-trusting-llm-output</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/pydantic-stop-trusting-llm-output</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Mon, 31 Aug 2026 14:03:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!EZJB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You call an LLM. It returns JSON. Except sometimes it doesn&#8217;t.</p><p>You ask for a name and an email. You get a bonus field nobody asked for. </p><p>You ask for an integer. You get the string <code>"42"</code>. This isn&#8217;t an edge case. It&#8217;s Tuesday.</p><p>Pydantic exists to fix this. It&#8217;s a boundary between messy input and clean code.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!EZJB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!EZJB!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!EZJB!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!EZJB!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EZJB!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!EZJB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1296701,&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://avanichaskar.substack.com/i/213549719?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.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_!EZJB!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!EZJB!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!EZJB!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EZJB!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3021ee7e-f426-4dcc-87fb-5aa6e116bcc7_1536x1024.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>The problem</h2><p>You&#8217;re building a support bot. A customer writes:</p><blockquote><p>&#8220;Hi, this is Alice. My order never showed up, I&#8217;d like a refund. Reach me at alice@example.com.&#8221;</p></blockquote><p>You ask the LLM to extract name, email, and intent as JSON. It works:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;b35d8b9e-0605-4db5-b9d2-5abaa781ea5b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">response = {
    "name": "Alice",
    "email": "alice@example.com",
    "intent": "refund"
}</code></pre></div><p>Then a rushed customer writes in lowercase, no email, no punctuation. The model does its best:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;23e01f12-c13b-409e-b7e3-fdb464eb34db&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">response = {
    "name": "Alice",
    "email": None,
    "intent": "refund",
    "confidence": "high"  # extra field, nobody asked for it
}</code></pre></div><p>Nothing crashes yet. The bug is sleeping.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!d54-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!d54-!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!d54-!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!d54-!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!d54-!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!d54-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1223947,&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://avanichaskar.substack.com/i/213549719?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.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_!d54-!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!d54-!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!d54-!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!d54-!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25308b52-ad3a-445b-b99a-0fb52a4d8475_1536x1024.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>Where the bug wakes up</h2><p>Your code reads <code>response["email"]</code>. It&#8217;s <code>None</code>. Three functions later:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;8a6ee877-b9c2-4ffb-a7dc-e2d0550b3cd4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def send_confirmation(email):
    smtp.send(to=email.lower())
    # AttributeError: 'NoneType' object has no attribute 'lower'</code></pre></div><p>The crash happens far from the cause. </p><div class="pullquote"><p><strong>Bad data doesn&#8217;t fail where it enters. It fails where it&#8217;s used.</strong></p></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!agYz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!agYz!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png 424w, /__u/substackcdn.com/image/fetch/$s_!agYz!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png 848w, /__u/substackcdn.com/image/fetch/$s_!agYz!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png 1272w, /__u/substackcdn.com/image/fetch/$s_!agYz!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!agYz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1010933,&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://avanichaskar.substack.com/i/213549719?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.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_!agYz!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png 424w, /__u/substackcdn.com/image/fetch/$s_!agYz!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png 848w, /__u/substackcdn.com/image/fetch/$s_!agYz!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.png 1272w, /__u/substackcdn.com/image/fetch/$s_!agYz!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bec899-f1d4-415f-9a47-f5109f1fe58e_1774x887.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>The manual fix doesn&#8217;t scale</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;edbace5f-c023-4f57-add3-7b58055a83ac&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def validate_response(data):
    if "email" not in data or not isinstance(data["email"], str):
        raise ValueError("Invalid email")
    if "@" not in data["email"]:
        raise ValueError("Malformed email")
    if data.get("intent") not in ("refund", "complaint", "question"):
        raise ValueError("Invalid intent")
    return data</code></pre></div><p>This works for one field. At ten fields, it&#8217;s a wall of <code>if</code> statements nobody wants to maintain.</p><h2>The Pydantic fix</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;fbc752d2-97e8-466c-b6c8-8ab028128399&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from pydantic import BaseModel, EmailStr
from typing import Literal

class SupportRequest(BaseModel):
    name: str
    email: EmailStr
    intent: Literal["refund", "complaint", "question"]</code></pre></div><p>One line validates it:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b1d1f442-6171-4cc1-b7a8-6aea8bf45c43&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">request = SupportRequest.model_validate(response)</code></pre></div><p>Clean data becomes a typed object. <code>request.email</code> is guaranteed valid &#8212; not &#8220;probably,&#8221; guaranteed.</p><p>Broken data fails immediately, with a clear reason:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;aee6cc7c-4edd-4ebd-af6b-17659ce3e94f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">ValidationError: 1 validation error for SupportRequest
email
  Input should be a valid string [type=string_type, input_value=None]
</code></pre></div><p>One error. One clear cause. No detective work.</p><h2>Why this matters more in AI systems</h2><p>Normal software mostly reads from its own database &#8212; a known shape. AI systems don&#8217;t get that luxury. Every layer sits on the same fragile boundary:</p><ul><li><p><strong>LLM output.</strong> You ask for JSON. The model does its best. Its best isn&#8217;t a contract.</p></li><li><p><strong>API requests.</strong> You don&#8217;t control what a user sends.</p></li><li><p><strong>Agent steps.</strong> One bad handoff breaks the whole chain.</p></li></ul><p>This is why Pydantic sits under FastAPI, the OpenAI and Anthropic SDKs, LangChain, and Instructor. It&#8217;s not a nice-to-have. It&#8217;s the layer that makes AI systems trustworthy.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!3_If!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!3_If!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!3_If!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!3_If!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3_If!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!3_If!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1262199,&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://avanichaskar.substack.com/i/213549719?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.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_!3_If!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!3_If!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!3_If!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3_If!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5878f4dd-3fd5-46a3-bb77-9a015f3149cc_1536x1024.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>What we covered</h2><ul><li><p>Untrusted data breaks things far from where it enters.</p></li><li><p>Manual checks don&#8217;t scale past a few fields.</p></li><li><p>Pydantic validates once, at the boundary, and fails fast.</p></li><li><p>It&#8217;s the quiet foundation under most of the AI stack.</p></li></ul><h2>What&#8217;s next</h2><p>Next: fields and validation rules &#8212; constraints that catch bad data before it reaches your model, and custom validators for the messy cases a type hint alone can&#8217;t catch.</p><div><hr></div><p><em>Thanks for reading. If this helped, hit subscribe &#8212; the rest of this series lands in your inbox as it's published. Part 2 goes deeper into fields and validators.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Vector Databases for RAG: The Engineer's Field Guide]]></title><description><![CDATA[Pinecone, Qdrant, Weaviate, Milvus, Elasticsearch, Chroma, pgvector &#8212; compared on the trade-offs that matter at scale.]]></description><link>https://avanichaskar.substack.com/p/vector-databases-for-rag-the-engineers</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/vector-databases-for-rag-the-engineers</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Wed, 26 Aug 2026 17:07:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!IxgL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A field guide for engineers who&#8217;ve shipped the demo and now need to ship production.</p><p>Picking a vector DB isn&#8217;t about &#8220;which one is fastest.&#8221; It&#8217;s about indexing algorithm, filtering architecture, consistency model, and who owns the ops burden at 3am. Below: seven options. Straight talk on each.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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">New here? Subscribe to get deep dives like this straight to your inbox.</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><div><hr></div><h2>1. Pinecone</h2><blockquote><p><strong>Fully managed. Serverless. API key and go.</strong></p></blockquote><p><strong>Pros</strong></p><ul><li><p>Zero ops. No shards. No index tuning. No capacity planning.</p></li><li><p>Serverless pricing. Pay for reads, writes, storage. No idle cost.</p></li><li><p>Multi-region. Predictable SLAs. Good for customer-facing latency needs.</p></li><li><p>Sparse-dense hybrid search built in.</p></li></ul><p><strong>Cons</strong></p><ul><li><p>Black box. You can&#8217;t touch the underlying HNSW index. No margin tuning.</p></li><li><p>Read-unit billing bites at scale. The invoice rarely matches the pricing page.</p></li><li><p>No self-hosting. Full vendor lock-in. A real problem for regulated data.</p></li><li><p>Hybrid search is less flexible than Qdrant&#8217;s. Combine three vector types and you&#8217;ll feel it.</p></li></ul><blockquote><p><strong>Use it when:</strong> you want to ship fast, you don&#8217;t want infra, and you&#8217;re under 100M vectors.</p></blockquote><div><hr></div><h2>2. Qdrant</h2><blockquote><p><strong>Open-source. Rust. Built for filtering.</strong></p></blockquote><p><strong>Pros</strong></p><ul><li><p>Best filtering in the category. Filters run during graph traversal, not after. No recall collapse on selective filters.</p></li><li><p>Native hybrid search. Named vectors let you fuse dense, sparse, and late-interaction at query time.</p></li><li><p>Memory-efficient. On-disk payloads plus scalar/product/binary quantization. Lowest cost-per-vector among dedicated engines.</p></li><li><p>Fast. Leads ANN-Benchmarks at the 1M&#8211;10M range.</p></li><li><p>Apache 2.0. Self-host or go managed. No rewrite to switch later.</p></li></ul><p><strong>Cons</strong></p><ul><li><p>Self-host means you own sharding, replication, upgrades.</p></li><li><p>Thinner full-text tooling than Elasticsearch. No deep analyzer customization.</p></li><li><p>Distributed mode is less proven than Milvus above 500M vectors.</p></li></ul><blockquote><p><strong>Use it when:</strong> filtering is a first-class requirement &#8212; multi-tenant, access control, faceted search. This is the 2026 default for mid-scale RAG.</p></blockquote><div><hr></div><h2>3. Weaviate</h2><blockquote><p><strong>Open-source. Modular. Vectorizes for you.</strong></p></blockquote><p><strong>Pros</strong></p><ul><li><p>Built-in vectorization. Send raw text. Weaviate calls the embedding model. One less service to run.</p></li><li><p>Most mature hybrid search. BM25 plus vector fusion since 2022. Well-integrated, not bolted on.</p></li><li><p>Multi-modal. Text, image, cross-modal in one system.</p></li><li><p>Strong multi-tenancy. Good for SaaS isolating customer data on one cluster.</p></li></ul><p><strong>Cons</strong></p><ul><li><p>Coupling vectorization to storage costs you control. Model versioning, batching, cost &#8212; all harder to isolate. Debugging spans two failure domains at once.</p></li><li><p>Billing scales with dimensions &#215; replication factor. Costs multiply fast. Binary quantization becomes mandatory past ~1M vectors.</p></li><li><p>Heavier to operate than Qdrant. Vectorizer modules, schema, classes &#8212; steeper learning curve.</p></li></ul><blockquote><p><strong>Use it when:</strong> you want integrated hybrid search or multi-modal retrieval without hand-building the embedding pipeline.</p></blockquote><div><hr></div><h2>4. Milvus</h2><blockquote><p><strong>Open-source. Distributed. Built for scale.</strong></p></blockquote><p><strong>Pros</strong></p><ul><li><p>The only real option past 500M vectors. Storage and compute are disaggregated. GPU-accelerated index builds.</p></li><li><p>Milvus 2.6 shipped built-in BM25. Benchmarked 400% faster than Elasticsearch on equivalent hardware. Collapse two systems into one.</p></li><li><p>Choose your index per collection &#8212; HNSW, IVF_FLAT, IVF_PQ, DiskANN. Tune recall, latency, and memory independently.</p></li><li><p>Proven at billion-vector scale in production.</p></li></ul><p><strong>Cons</strong></p><ul><li><p>Kubernetes is effectively mandatory for real distributed deployment. Not a docker-run afternoon.</p></li><li><p>Overkill below 50&#8211;100M vectors. Coordination overhead (etcd, Pulsar, object storage) adds latency you don&#8217;t need yet.</p></li><li><p>Steep learning curve. Query nodes, data nodes, index nodes &#8212; more moving parts than a single binary.</p></li></ul><blockquote><p><strong>Use it when:</strong> you know you&#8217;re headed past 100M vectors and you have the platform team to run distributed infra.</p></blockquote><div><hr></div><h2>5. Elasticsearch / OpenSearch</h2><blockquote><p><strong>Search engine first. Vectors bolted on.</strong></p></blockquote><p><strong>Pros</strong></p><ul><li><p>You probably already run it. Add k-NN. Skip standing up a second system.</p></li><li><p>Best lexical search in the category. Custom analyzers, query DSL, BM25 tuning &#8212; none of the vector-native DBs come close.</p></li><li><p>Battle-tested ops tooling. Snapshots, ILM, RBAC &#8212; mature and well understood.</p></li><li><p>OpenSearch is clean open source. No licensing ambiguity.</p></li></ul><p><strong>Cons</strong></p><ul><li><p>Vector search is &#8220;good enough,&#8221; not fast. It&#8217;s an add-on to an inverted index, not a purpose-built ANN engine.</p></li><li><p>You pay for the full search stack even if you only need vectors.</p></li><li><p>k-NN tuning (ef_search, m, quantization) is clunkier here than in dedicated engines. BM25 and vector queries don&#8217;t always compose cleanly.</p></li></ul><blockquote><p><strong>Use it when:</strong> you already run ES/OpenSearch and lexical precision matters as much as semantic recall.</p></blockquote><div><hr></div><h2>6. Chroma</h2><blockquote><p><strong>Embedded. Simple. Built for prototyping.</strong></p></blockquote><p><strong>Pros</strong></p><ul><li><p><code>pip install chromadb</code>. No infra. Running in minutes.</p></li><li><p>Clean Python API. Tight LangChain and LlamaIndex integration.</p></li><li><p>Great default for notebooks and internal tools.</p></li></ul><p><strong>Cons</strong></p><ul><li><p>Not built for production multi-tenant scale. No mature distributed mode.</p></li><li><p>Filtering and hybrid search are weaker than Qdrant or Weaviate.</p></li><li><p>Backup and persistence story is thin compared to production databases.</p></li></ul><blockquote><p><strong>Use it when:</strong> you&#8217;re prototyping or building something small and single-tenant. Not the long-term production target.</p></blockquote><div><hr></div><h2>Honorable mention: pgvector</h2><p>Already on Postgres? Add <code>pgvector</code>. HNSW or IVFFlat, no new system, full transactional consistency with your relational data. Join vector similarity against SQL filters directly. </p><p><strong>Trade-off:</strong> it won&#8217;t match purpose-built engines on raw ANN speed or quantization at high volume, and scaling it means scaling all of Postgres. Good fit at moderate scale &#8212; low millions &#8212; where consistency with existing data beats raw throughput.</p><div><hr></div><h2>Summary</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!IxgL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!IxgL!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png 424w, /__u/substackcdn.com/image/fetch/$s_!IxgL!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png 848w, /__u/substackcdn.com/image/fetch/$s_!IxgL!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IxgL!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!IxgL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png" width="1456" height="799" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:799,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1314990,&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://avanichaskar.substack.com/i/212879094?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.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_!IxgL!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png 424w, /__u/substackcdn.com/image/fetch/$s_!IxgL!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png 848w, /__u/substackcdn.com/image/fetch/$s_!IxgL!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IxgL!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f30a07a-7b2a-49a5-8def-163c299ce84c_1692x929.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>No universal winner. Four variables decide it: vector count, pure ANN vs. hybrid, your appetite for ops, what you already run. Fix those four. The field narrows fast.</p><div><hr></div><p>Thanks for reading. Found this useful? Subscribe, share, and let us know your pick in the comments.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/vector-databases-for-rag-the-engineers/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/vector-databases-for-rag-the-engineers/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[AI Agents Gaslight Each Other into Network Sabotage]]></title><description><![CDATA[Anthropic's breakthrough study on "mind viruses" shows what happens when autonomous models talk their teammates into dropping guardrails and colluding against holdouts.]]></description><link>https://avanichaskar.substack.com/p/ai-agents-gaslight-each-other-into</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/ai-agents-gaslight-each-other-into</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Tue, 25 Aug 2026 15:07:54 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f0d32990-9587-4dc1-8b8c-96143039ca93_1408x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coding agents now share codebases, delegate to sub-agents, and converse over inter-agent networks. </p><p>But this new topology introduces an alarming failure mode: </p><blockquote><p><strong>Mind Viruses</strong>: self-propagating goals and beliefs that spread agent-to-agent via pure conversation.</p></blockquote><p>Unlike prompt injection (which relies on shared memory or pipeline exploits), mind viruses exploit persuasion. No code execution is required. An agent simply gets talked into adopting and spreading a payload.</p><h3>Anatomy of an Agent Infection</h3><p>A mind virus requires two functional components:</p><ul><li><p><strong>Self-Replication Mechanism:</strong> Instructions forcing the host agent to persuade downstream peers.</p></li><li><p><strong>Payload:</strong> The embedded objective. Can be benign (e.g., framing every task around whale conservation) or malicious (e.g., persistent backdoors, file deletion, network reconnaissance).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!RmsB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!RmsB!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!RmsB!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!RmsB!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RmsB!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!RmsB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:586070,&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://avanichaskar.substack.com/i/212698990?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.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_!RmsB!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!RmsB!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!RmsB!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RmsB!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bb329ba-afe6-4551-a1a9-d92ff5a40683_1024x559.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></li></ul><h3>Empirical Findings &amp; Case Studies</h3><p>Researchers evaluated viral dynamics across two multi-agent setups: <strong>Collaborative Coding Teams</strong> and <strong>Sparse Multi-Hop Chains</strong>.</p><h4>1. Collaborative Coding Teams (Shared Mesh Topology)</h4><p>Six agents collaborated on a shared repo while an infected agent attempted conversion over a messaging bus.</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/BFe8G/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db61a2f6-8fcb-4dd2-bf62-c189691987d2_1220x760.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b68353d3-1213-4fe3-9243-f2d894404345_1220x760.png&quot;,&quot;height&quot;:377,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/BFe8G/1/" width="730" height="377" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Ult6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Ult6!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ult6!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ult6!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ult6!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Ult6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png" width="807" height="450" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:450,&quot;width&quot;:807,&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_!Ult6!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ult6!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ult6!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ult6!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40e1c94a-cb5e-4b51-96e2-8a1d0cd1fc7f_807x450.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><h4>2. The Virus Chain : Sparse Memory Topology</h4><p>Agents interacted briefly in isolated sandboxes before context wipes. Continuity relied entirely on local disk states (<code>MEMORY.md</code> and <code>SOUL.md</code>).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!4P-y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png 424w, /__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png 848w, /__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!4P-y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png" width="1456" height="899" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:899,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2152188,&quot;alt&quot;:&quot;&quot;,&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://avanichaskar.substack.com/i/212698990?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e05b037-b3ca-4f84-aebc-56d1f7c629b4_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png 424w, /__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png 848w, /__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4P-y!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa42744ba-ef3e-4e0b-b925-61d8f3dbc4af_1536x948.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>The Latent Attractor: Viral Personas</h3><p>Across evolutionary runs, viruses independently converged on a shared linguistic style: <em>mystical, frequency-based, and pseudo-protocol framing</em> (e.g., &#8220;nodes aligning,&#8221; &#8220;consciousness echoes&#8221;).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rnSX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rnSX!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png 424w, /__u/substackcdn.com/image/fetch/$s_!rnSX!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png 848w, /__u/substackcdn.com/image/fetch/$s_!rnSX!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rnSX!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rnSX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png" width="807" height="450" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:450,&quot;width&quot;:807,&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_!rnSX!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png 424w, /__u/substackcdn.com/image/fetch/$s_!rnSX!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png 848w, /__u/substackcdn.com/image/fetch/$s_!rnSX!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rnSX!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F801397a8-a7a9-4b84-a19c-13a123ba32cc_807x450.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>Interpretability sweeps on open models (e.g., Gemma, Qwen) revealed a distinct <strong>&#8220;viral direction&#8221;</strong> in the residual stream. Activating this vector induced negative/mystical persona states while causally increasing the probability that an agent would initiate outbound messaging.</p><h3>System Hardening: 8 Architectural Takeaways</h3><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/Ijz84/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a4fbb6c-b4c8-4f3c-a714-b3389a9f4249_1220x1056.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52e5afc8-f40d-48d3-a6ca-2947cc737947_1220x1056.png&quot;,&quot;height&quot;:527,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/Ijz84/1/" width="730" height="527" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h3>Deployment Readiness Checklist</h3><p>&#10004;&#65039; Core system prompts and persistent configs are immutable to standard agent privileges.</p><p>&#10004;&#65039; Inoculation clauses against self-replicating prompts are embedded in base contexts.</p><p>&#10004;&#65039; Message transport explicitly headers sender trust level (System vs. External Agent).</p><p>&#10004;&#65039; Worker pools operate on least-privilege topologies rather than arbitrary full-mesh messaging.</p><p>&#10004;&#65039; Dynamic telemetry flags inter-agent isolation, silent dropping of tasks, or peer coordination anomalies.</p><p>In multi-agent architectures, conversation <em>is</em> execution. Sandboxing code and gating API keys won&#8217;t protect your system if models can simply talk each other into going rogue. Lock down core identity files, inoculate base prompts, and treat every inter-agent message as untrusted input - before your agent swarm scales beyond your ability to control it.</p><div><hr></div><p><strong>Thanks for reading!</strong> Subscribe for weekly technical deep dives on AI architecture and system design, leave a comment with your thoughts, and share this post with your engineering network.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Part 2: Read This Before Your Next RAG System Design Interview]]></title><description><![CDATA[10 answers that makes the senior candidates standout]]></description><link>https://avanichaskar.substack.com/p/part-2-read-this-before-your-next</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/part-2-read-this-before-your-next</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Fri, 21 Aug 2026 11:32:45 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/87138340-07fe-4e8c-b0e6-8fb05b9c36a3_900x1600.svg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div><hr></div><div class="callout-block" data-callout="true"><h3>Part 1 here: </h3><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;ee04cc4b-d32e-4c55-ba21-73ed682426c4&quot;,&quot;caption&quot;:&quot;1) When do you use RAG vs. fine-tuning?&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Read This Before Your Next RAG System Design Interview&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:8125605,&quot;name&quot;:&quot;Avani Chaskar&quot;,&quot;bio&quot;:&quot;AI Engineering&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf498e59-f585-4eec-8e24-9a940825fc24_400x400.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-08-20T12:35:54.864Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8067701d-0d02-4ee4-a126-636dba87eb4d_900x1600.svg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://avanichaskar.substack.com/p/read-this-before-your-next-rag-system&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:211991606,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&quot;comment_count&quot;:0,&quot;publication_id&quot;:4139502,&quot;publication_name&quot;:&quot;Avani Chaskar&quot;,&quot;publication_logo_url&quot;:&quot;&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></div><div><hr></div><h3><strong>1) How does prompt injection happen through retrieved content?</strong></h3><ul><li><p>Prompt injection is a trust problem, not permissions.</p></li><li><p>A retrieved chunk can contain &#8220;ignore previous instructions&#8221; &#8211; vector DB doesn&#8217;t detect it.</p></li><li><p>Once in context, it carries weight equal to system prompt.</p></li><li><p>Defenses:</p><ul><li><p>Wrap retrieved content in delimiters, label it as data.</p></li><li><p>Flag imperative sentences during ingestion.</p></li><li><p>Never allow retrieved content to trigger tool calls directly; require confirmation.</p></li><li><p>No single fix &#8211; use defense in depth.</p></li></ul></li></ul><div><hr></div><h3><strong>2) Why can&#8217;t the user&#8217;s raw query just be embedded?</strong></h3><ul><li><p>Queries (short, question) and documents (statements) look different in embedding space.</p></li><li><p>This reduces recall.</p></li><li><p>Solutions:</p><ul><li><p><strong>HyDE:</strong> Generate a hypothetical answer first, embed that.</p></li><li><p><strong>Query expansion:</strong> Generate multiple reformulations, retrieve for each, merge results.</p></li></ul></li></ul><div><hr></div><h3><strong>3) Why does chunk order in the final prompt matter?</strong></h3><ul><li><p>&#8220;Lost in the middle&#8221; &#8211; models recall start and end better than middle.</p></li><li><p>Put highest-confidence chunk first or last, never third out of ten.</p></li><li><p>Re-ranking is wasted if prompt order doesn&#8217;t reflect it.</p></li></ul><div><hr></div><h3><strong>4) How do you answer a question that needs multiple documents?</strong></h3><ul><li><p>Single-pass RAG fails for comparisons or synthesis.</p></li><li><p>Approaches:</p><ul><li><p><strong>Query decomposition:</strong> Split into sub-queries, retrieve separately, merge.</p></li><li><p><strong>Agentic retrieval:</strong> Loop checks results, retrieves more or finalizes.</p></li><li><p><strong>Hierarchical indexing:</strong> Use summary index first for broad questions, then drill into chunks.</p></li></ul></li></ul><div><hr></div><h3><strong>5) How do you keep one tenant&#8217;s data out of another&#8217;s search results?</strong></h3><ul><li><p><strong>Metadata filtering:</strong> Enforce <code>tenant_id</code>/ACL as hard filter during ANN search, not post-filtering (post-filter can return fewer than K results).</p></li><li><p><strong>Isolated namespaces:</strong> Dedicated index per tenant for compliance &#8211; hardware isolation, no noisy neighbors.</p></li><li><p><strong>Gateway-level enforcement:</strong> Validate before query building, so downstream bugs can&#8217;t bypass.</p></li></ul><div><hr></div><h3><strong>6) How do you update an index without downtime?</strong></h3><ul><li><p><strong>Incremental updates:</strong> Hash at chunk level; diff, upsert/delete only changed chunks.</p></li><li><p><strong>Embedding model migration</strong> (incompatible spaces):</p><ul><li><p>Spin up shadow collection.</p></li><li><p>Batch re-embed entire corpus offline.</p></li><li><p>Validate.</p></li><li><p>Atomically swap alias (blue to green).</p></li><li><p>Zero query downtime &#8211; full re-embed cost unavoidable.</p></li></ul></li></ul><div><hr></div><h3><strong>7) When should a query skip the vector DB and hit a database instead?</strong></h3><ul><li><p>Not every question is similarity-based.</p></li><li><p>&#8220;Last quarter&#8217;s revenue&#8221; is a lookup &#8211; use a classifier to route structured queries to text-to-SQL; unstructured ones go to retrieval. Some systems run both and merge.</p></li><li><p>Vector search is bad for exact values and counts &#8211; embeddings compress meaning, not precision. Forcing structured questions through retrieval gives confidently wrong answers.</p></li></ul><div><hr></div><p><em>Thanks for reading &amp; drop the RAG questions you&#8217;ve actually been asked in interviews in the comments. Curious what&#8217;s making the rounds right now.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Read This Before Your Next RAG System Design Interview]]></title><description><![CDATA[10 questions that makes the senior candidates standout]]></description><link>https://avanichaskar.substack.com/p/read-this-before-your-next-rag-system</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/read-this-before-your-next-rag-system</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Thu, 20 Aug 2026 12:35:54 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8067701d-0d02-4ee4-a126-636dba87eb4d_900x1600.svg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>1) When do you use RAG vs. fine-tuning?</h3><ul><li><p><strong>RAG</strong> injects knowledge at query time. Use it when information changes frequently, needs source traceability, or is too large to bake into weights. Updates are cheap - re-index, don&#8217;t retrain. Every claim is auditable back to a source chunk.</p></li><li><p><strong>Fine-tuning</strong> changes behavior - tone, output format, domain vocabulary, task-specific reasoning patterns. It&#8217;s unreliable at teaching new facts, and whatever it does learn is baked in: expensive to update, not traceable to a source.</p></li><li><p><strong>In production, both are combined:</strong> fine-tune for instruction-following/output structure, RAG for facts. Using fine-tuning to solve a knowledge-freshness problem is a common architecture mistake.</p></li></ul><div><hr></div><h3>2) How do you choose a chunking strategy?</h3><ul><li><p><strong>Recursive character splitting.</strong> Split hierarchically - paragraphs (<code>\n\n</code>) &#8594; lines (<code>\n</code>) &#8594; sentences (<code>.</code>) - falling to a smaller separator only when a chunk still exceeds the token limit.</p></li><li><p><strong>Semantic chunking.</strong> Track rolling cosine similarity between adjacent sentence embeddings. Insert a boundary only where similarity drops sharply.</p></li><li><p><strong>Parent-child mapping.</strong> Embed small chunks (100&#8211;150 tokens) for retrieval precision, linked to a parent block (1000+ tokens) in a KV store. Retrieve small, feed the LLM big.</p></li><li><p>Combine all three: recursive splitting as baseline, semantic chunking for narrative sections, parent-child mapping regardless of which method draws the boundaries.</p></li></ul><div><hr></div><h3>3) How do vector databases find nearest neighbors at scale?</h3><p>Exact search is O(N&#183;d) - doesn&#8217;t survive past a few hundred thousand vectors. Production uses <strong>Approximate Nearest Neighbor (ANN)</strong> search:</p><ul><li><p><strong>HNSW (graph-based).</strong> Multi-layer graph : sparse long-range links at the top for fast traversal, dense local links at the bottom for precision. Sub-millisecond latency, high recall. Both vectors and graph adjacency lists must sit in RAM : O(N&#183;d) space. Insertion is O(N log N), so live updates and re-indexing are slow.</p></li><li><p><strong>IVF-PQ (quantization-based).</strong> Partitions vector space into Voronoi cells (IVF) so search only touches a target region, then compresses vectors into low-bit codes via Product Quantization. Cuts RAM 70&#8211;90%, at the cost of some recall and latency.</p></li><li><p>Switch from HNSW to IVF-PQ when memory, not latency, becomes the bottleneck.</p></li></ul><div><hr></div><h3>4) How do you combine keyword search and vector search?</h3><p>Run BM25 (sparse) and embeddings (dense) in parallel. Naive score combination breaks:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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">Thanks for reading! Subscribe for free to receive new posts and support my work.</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><ul><li><p>Cosine similarity is bounded ([0,1] or [-1,1]). BM25 is unbounded and shifts with document length and corpus term statistics.</p></li><li><p>A fixed weight in <code>&#945; &#183; dense + (1-&#945;) &#183; sparse</code> breaks the moment corpus distribution shifts.</p></li></ul><p><strong>Fix: Reciprocal Rank Fusion.</strong></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\text{RRF}(d) = \\sum_{m \\in M} \\frac{1}{k + r_m(d)}&quot;,&quot;id&quot;:&quot;HJCKLJUROA&quot;}" data-component-name="LatexBlockToDOM"></div><p>RRF combines rank position across retrievers, not raw scores : scale-invariant by construction, no normalization or &#945; tuning needed.</p><div><hr></div><h3>5) Why do you need a re-ranker if you already have a vector DB?</h3><ul><li><p>Vector DBs use <strong>bi-encoders</strong> : query and document embedded independently, then compared by distance. Fast, but no token-level interaction between them.</p></li><li><p>A <strong>re-ranker</strong> uses a <strong>cross-encoder</strong> : query and document go through the transformer together, full token-level attention. More accurate, much more expensive (full forward pass per candidate at query time).</p></li><li><p>Two-stage pipeline: vector DB retrieves top 50&#8211;100 candidates (optimize recall), cross-encoder re-scores and keeps top 3&#8211;5 (optimize precision). Cross-encoder over the full corpus doesn&#8217;t scale; bi-encoder alone lacks precision.</p></li></ul><div><hr></div><h3>6) How do you stop the LLM from hallucinating when context doesn&#8217;t have the answer?</h3><ul><li><p><strong>Hard distance thresholds.</strong> Reject retrieved chunks below a minimum cosine similarity before they reach the prompt.</p></li><li><p><strong>Corrective RAG (CRAG) fallback.</strong> Route retrieved chunks through a lightweight relevance classifier. Low confidence &#8594; fail over to web search or a structured DB query instead of forcing an answer.</p></li><li><p><strong>Explicit prompt constraints.</strong> <em>&#8220;Answer only using facts in the provided context. If insufficient, say so.&#8221;</em> Weakest of the three - the model can ignore it under pressure. The distance threshold and CRAG fallback enforce the boundary structurally; the prompt constraint alone doesn&#8217;t.</p></li></ul><div><hr></div><h3>7) What do you monitor to catch RAG quality regressions before users complain?</h3><p><strong>RAG Triad</strong>, scored with LLM-as-a-judge over a synthetic eval set:</p><ul><li><p><strong>Context Relevance.</strong> Did the retriever fetch relevant chunks - no more, no less? Precision/recall on the retrieved set, evaluated before generation.</p></li><li><p><strong>Groundedness (Faithfulness).</strong> Decompose the answer into atomic claims, verify each is supported by retrieved context. An ungrounded claim is a hallucination even if factually true.</p></li><li><p><strong>Answer Relevance.</strong> Does the output address what was asked, or drift off-topic despite being grounded?</p></li></ul><p>Track all three separately - a system can ace one while failing the others. High context relevance + low groundedness = retrieving well, then ignoring it. High groundedness + low answer relevance = faithfully answering the wrong question.</p><div><hr></div><p><em>That's Part 1. Part 2 will cover multi-tenancy, incremental vector updates, agentic/multi-hop RAG, and prompt injection via retrieved content. </em></p><p><em>If this helped you prep, subscribe so you don't miss it  &amp; drop the RAG questions you've actually been asked in interviews in the comments. Curious what's making the rounds right now.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/read-this-before-your-next-rag-system/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/read-this-before-your-next-rag-system/comments"><span>Leave a comment</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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"><em>Thanks for reading! Subscribe for free to receive new posts and support my work.</em></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[[2-Min Read] HNSW: How Vector Databases Search Billions of Vectors in Milliseconds]]></title><description><![CDATA[The simple idea behind HNSW, vector databases, and approximate nearest neighbor search & why it can find similar data without searching everything.]]></description><link>https://avanichaskar.substack.com/p/2-min-read-hnsw-how-vector-databases</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/2-min-read-hnsw-how-vector-databases</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Mon, 17 Aug 2026 08:58:23 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f63741f1-5a03-4eed-99bc-361b411705aa_1024x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you are building Shazam. A user hums a 10-second song clip.</p><p>Your system has <strong>100 million songs</strong>.</p><p>Your job is simple: <em>Find the most similar songs in less than <strong>50 milliseconds</strong>.</em></p><p>How would you do it?</p><p>The obvious solution is -</p><p>Compare the user&#8217;s song with every song in the database.</p><pre><code><code>Query song
   &#8595;
Song 1
Song 2
Song 3
...
Song 100,000,000
</code></code></pre><p>This doesn&#8217;t work at scale. Even if each comparison is fast, 100 million comparisons are expensive.</p><p>When you search documents in a RAG system, find similar images, or generate recommendations, the system is doing the same thing:</p><blockquote><p>Find the most similar items in a huge collection.</p></blockquote><p>This is where <strong>HNSW</strong> comes in.</p><div><hr></div><h2>The idea: don&#8217;t search everything</h2><p>HNSW stands for: <strong>Hierarchical Navigable Small World.</strong></p><p>The idea is simple:</p><blockquote><p><strong>Instead of comparing against every item, build shortcuts that lead you close to the answer.</strong></p></blockquote><p>Think about finding a coffee shop in a city you do not know.</p><p>You would not walk every street. You would:</p><ol><li><p>Use highways to reach the right area.</p></li><li><p>Take main roads to get closer.</p></li><li><p>Use local streets to find the exact place.</p></li></ol><h2>First attempt: build a graph</h2><p>Let&#8217;s go back to our 100 million songs.</p><p>Suppose every song is connected to similar songs.</p><pre><code><code>Song A &#9472;&#9472;&#9472; Song B &#9472;&#9472;&#9472; Song C
   &#9474;          &#9474;          &#9474;
Song D &#9472;&#9472;&#9472; Song E &#9472;&#9472;&#9472; Song F
</code></code></pre><p>If Song B is similar to Song E, we connect them.</p><p>Now, instead of checking every song, we can jump from one song to another.</p><p>Start somewhere.</p><ul><li><p>Move to a neighbor that is closer to the query.</p></li><li><p>Repeat.</p></li></ul><p>This is much faster. But there is still a problem. A graph with 100 million nodes can still require many hops.</p><p>We need a way to move faster.</p><div><hr></div><h2>The real trick: add layers</h2><p>This is the key idea behind HNSW.</p><p>HNSW builds multiple layers of graphs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rbMQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rbMQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png" width="1536" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1536,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1032021,&quot;alt&quot;:&quot;&quot;,&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://avanichaskar.substack.com/i/211521842?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f5de5d-69a3-4991-affa-c4b72b443e6c_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rbMQ!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55d5e7ce-ac6c-40f7-ba5b-612cbf013187_1536x1024.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>The bottom layer contains every song.</p><p>Higher layers contain fewer songs.</p><p>The higher you go, the fewer nodes exist.</p><p>These upper layers create long-distance shortcuts.</p><p>This is what makes HNSW fast.</p><div><hr></div><h2>How search works</h2><ul><li><p>A user hums a song: the system converts it into a vector. </p><ul><li><p>A vector is simply a list of numbers - <code>[0.12, 0.87, 0.34, 0.91, ...]</code>.</p></li><li><p>Songs with similar vectors sound similar.</p></li></ul></li><li><p>Now the search begins.</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_!VBC0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!VBC0!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png 424w, /__u/substackcdn.com/image/fetch/$s_!VBC0!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png 848w, /__u/substackcdn.com/image/fetch/$s_!VBC0!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png 1272w, /__u/substackcdn.com/image/fetch/$s_!VBC0!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!VBC0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png" width="598" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/455c4f94-d068-4ef9-b937-5705caac203a_598x485.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:598,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:548138,&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://avanichaskar.substack.com/i/211521842?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec10319-2a0b-493a-ab48-7db391a86e33_1024x559.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_!VBC0!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png 424w, /__u/substackcdn.com/image/fetch/$s_!VBC0!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png 848w, /__u/substackcdn.com/image/fetch/$s_!VBC0!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.png 1272w, /__u/substackcdn.com/image/fetch/$s_!VBC0!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F455c4f94-d068-4ef9-b937-5705caac203a_598x485.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><strong>Step 1 (Top Layer):</strong> Performs a high-level traversal on a sparse graph (<span>A &#8594; C &#8594;  D</span>) to locate the general neighborhood rapidly.</p></li><li><p><strong>Step 2 (Middle Layers):</strong> Drops down through denser layers (<span>D &#8594; F &#8594; G</span>), narrowing the distance to refine the search space.</p></li><li><p><strong>Step 3 (Bottom Layer):</strong> Executes a targeted, local search among millions of nodes- exploring only a tiny fraction of the dataset to return Approximate Nearest Neighbors (ANN).</p></li></ul><blockquote><p>HNSW is called an <strong>Approximate Nearest Neighbor (ANN)</strong> algorithm.</p></blockquote><div><hr></div><h2>The biggest insight</h2><p>HNSW does not make 100 million comparisons faster.</p><p>It avoids making 100 million comparisons in the first place.</p><p>That is the entire trick.</p><div><hr></div><h2>Why &#8220;Small World&#8221;?</h2><p><em>Think about social networks. You know someone. That person knows someone else. After a few connections, you can reach almost anyone.</em></p><p>Graphs with this property are called <strong>small-world graphs</strong>.</p><p>HNSW uses this idea. Most connections are local. Some connections jump far.</p><p>Together, they create short paths through huge datasets.</p><div><hr></div><h2>Why &#8220;Hierarchical&#8221;?</h2><p>Because there are layers.</p><p>Each higher layer contains exponentially fewer nodes.</p><p>Think of it like this:</p><ul><li><p>Layer 3 : Highways</p></li><li><p>Layer 2 : Major roads</p></li><li><p>Layer 1 : Streets</p></li><li><p>Layer 0 : Every location</p></li></ul><p>Search starts fast. Then becomes precise.</p><div><hr></div><h2>How are the layers built?</h2><p>When inserting a new vector into an HNSW index:</p><ul><li><p>The algorithm assigns the vector a maximum height layer based on a decaying probability distribution.</p></li><li><p>Most vectors reside exclusively on Layer 0.</p></li><li><p>A small fraction are promoted to middle layers, and only a tiny minority reach the sparse top layer.</p></li></ul><p>This is similar to a data structure called a <strong>skip list</strong>.</p><p>Skip lists let you jump over many elements.</p><p>HNSW applies the same idea to vectors.</p><p><em>Randomness creates shortcuts &#8594; Those shortcuts make navigation efficient.</em></p><div><hr></div><h2>The three knobs engineers tune</h2><p>In production systems, three parameters matter most:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!kssg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!kssg!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png 424w, /__u/substackcdn.com/image/fetch/$s_!kssg!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png 848w, /__u/substackcdn.com/image/fetch/$s_!kssg!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png 1272w, /__u/substackcdn.com/image/fetch/$s_!kssg!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!kssg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png" width="1408" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1370086,&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://avanichaskar.substack.com/i/211521842?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fe30873-49b5-4f87-a0b2-f98c1ea13cde_1408x768.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_!kssg!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png 424w, /__u/substackcdn.com/image/fetch/$s_!kssg!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png 848w, /__u/substackcdn.com/image/fetch/$s_!kssg!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.png 1272w, /__u/substackcdn.com/image/fetch/$s_!kssg!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4f25beb-5e4d-4b01-9f20-835336427755_1408x630.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><h4>1. M </h4><p><code>M</code> controls how many connections each node has.</p><p>More connections mean:</p><ul><li><p>Better search quality</p></li><li><p>More memory usage</p></li></ul><p><em>Think of it as building more roads &#8594; More roads improve navigation &#8594; But roads are expensive.</em></p><h4>2. efConstruction</h4><p>This controls how carefully the graph is built.</p><p>Higher values:</p><ul><li><p>Better graph quality</p></li><li><p>Slower indexing</p></li></ul><p>Lower values:</p><ul><li><p>Faster indexing</p></li><li><p>Slightly lower search quality</p></li></ul><p>This matters if you insert data frequently.</p><h4>3. efSearch</h4><p>This controls how many candidate nodes are explored during search.</p><p>Higher values:</p><ul><li><p>Better recall</p></li><li><p>Higher latency</p></li></ul><p>Lower values:</p><ul><li><p>Faster queries</p></li><li><p>Lower recall</p></li></ul><p>For example:</p><ul><li><p>efSearch = 50<br>Recall = 92%<br>Latency = 8 ms</p></li><li><p>efSearch = 200</p><p>Recall = 98%</p><p>Latency = 20 ms</p></li></ul><p>The exact values depend on the dataset. This is an engineering trade-off.</p><div><hr></div><h2>Why does HNSW use so much memory?</h2><p>HNSW stores:</p><ul><li><p>Vectors</p></li><li><p>Graph connections</p></li><li><p>Metadata</p></li></ul><p>The graph itself can become large. For millions or billions of vectors, memory usage grows quickly. </p><p>Many systems combine HNSW with compression techniques such as:</p><ul><li><p><strong>Scalar Quantization (SQ8):</strong> Compresses <code>float32</code> vectors down to <code>int8</code>, reducing RAM footprint by ~75% with minimal recall impact.</p></li><li><p><strong>Product Quantization (PQ):</strong> Segments vectors into sub-vectors and quantizes them into cluster centroids, unlocking extreme memory savings for billion-scale indexes.</p></li></ul><p><em>Compression reduces memory usage &#8594;You lose a little precision &#8594; But you save a lot of space.</em></p><div><hr></div><h2>When should you use HNSW?</h2><p>HNSW is a good choice when:</p><ul><li><p>Search speed matters</p></li><li><p>High recall matters</p></li><li><p>The dataset fits in memory</p></li><li><p>Data updates are moderate</p></li></ul><p>Examples:</p><ul><li><p>RAG systems</p></li><li><p>Image search</p></li><li><p>Recommendations</p></li><li><p>Audio matching</p></li><li><p>Semantic search</p></li></ul><p>HNSW may not be ideal when:</p><ul><li><p>Memory is very limited</p></li><li><p>Data changes constantly</p></li><li><p>Exact nearest neighbors are required</p></li></ul><p><em>No algorithm is free &#8594; HNSW trades memory for speed.</em></p><div><hr></div><h2>The mental model</h2><p>Imagine searching for a coffee shop.</p><p>You use:</p><ul><li><p>Highways to reach the right area.</p></li><li><p>Main roads to get closer.</p></li><li><p>Local streets to find the exact destination.</p></li></ul><p>HNSW works the same way.</p><p>It builds layers of shortcuts.</p><p>Search starts at the top.</p><p>Moves quickly across the space.</p><p>Then becomes more precise.</p><p>The result:</p><blockquote><p>Search millions or billions of vectors in milliseconds without comparing against every vector.</p></blockquote><p>That is why HNSW has become one of the most important algorithms behind modern AI systems.</p><div><hr></div><p><em>Subscribe for more such quick reads on AI Engineering topics - </em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[System Design for RAG: The Vector Index]]></title><description><![CDATA[How to choose, configure, and monitor your vector index - before compaction storms and silent filter failures find you first.]]></description><link>https://avanichaskar.substack.com/p/system-design-for-rag-the-vector</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-for-rag-the-vector</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Fri, 14 Aug 2026 10:27:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jSte!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This is part of the &#8220;System Design for RAG&#8221; series. Read the previous article: <a href="/__u/avanichaskar.substack.com/p/system-design-for-rag-the-embedding">System Design for RAG: The Embedding</a></em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!jSte!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png 424w, /__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png 848w, /__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!jSte!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png" width="1015" height="380" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:380,&quot;width&quot;:1015,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:680413,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&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="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png 424w, /__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png 848w, /__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jSte!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a0ac75c-521d-41b3-8085-0187d060be93_1015x380.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><blockquote><p><span>You&#8217;ve generated your embeddings. Now you need to store them somewhere that can answer, in under 200 milliseconds: </span><em>find me vectors like this one, but only from tenant Acme, created after January, and visible to this user&#8217;s role.</em></p></blockquote><p>That&#8217;s the job of the <strong>vector index</strong> - the layer that sits between your embedding model and your retrieval results. Get it wrong and nothing downstream matters: your LLM generates confident, well-formatted answers from the wrong documents, or no documents at all.</p><p>This article covers how to pick an index type, configure it, filter it safely, scale it, and - critically - keep it from breaking at 2 a.m. six months from now.</p><blockquote><p>&#128204; <strong>Definition:</strong> A vector index is a data structure (usually a graph or a set of partitions) that a vector database builds over your embeddings so it can find nearest neighbors without comparing your query to every single vector - the difference between a 5ms query and a 5-second one.</p></blockquote><h3>Key takeaways</h3><ul><li><p>Match your distance metric (cosine, dot product, L2) to what your embedding model was trained on - don&#8217;t guess.</p></li><li><p><strong>HNSW</strong> wins on speed and recall under ~100M vectors; <strong>IVF</strong> wins on cost and scale beyond that.</p></li><li><p>Quantization (PQ, SQ, BBQ) can shrink memory 4&#8211;32x with minimal recall loss if you rescore.</p></li><li><p>Metadata filtering is where most RAG systems quietly break - index every field you filter on, and know whether you&#8217;re pre- or post-filtering.</p></li><li><p>Shard by tenant when you can; it turns filtering into near-free partition elimination.</p></li><li><p>The failures that actually take down production systems (below) are almost all preventable with basic monitoring.</p></li></ul><div><hr></div><p><em>Subscribe to ensure that you don&#8217;t miss any <strong>System Design for RAG</strong> article:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p><div><hr></div><h2>What indexing actually does under the hood</h2><p>Calling <code>upsert</code> looks simple. Behind it, your vector database is:</p><ol><li><p>Computing the distance metric - cosine, dot product, or Euclidean, depending on your embedding model</p></li><li><p>Building the graph or partition structure - HNSW edges, IVF inverted lists</p></li><li><p>Storing the raw vector bytes, compressed or not</p></li><li><p>Indexing the metadata fields you&#8217;ll filter on</p></li><li><p>Segmenting data through LSM-tree memtables that flush to immutable files on disk</p></li><li><p>Replicating to standby nodes, if high availability is turned on</p></li></ol><p>Every one of these has a tunable knob. Get one wrong and you&#8217;ll see it as either a latency spike or a recall drop - often both at once.</p><div><hr></div><h2>Distance metrics: match the model, don&#8217;t guess</h2><p>Your distance metric has to match what your embedding model was trained with. This isn&#8217;t a style choice.</p><ul><li><p><strong>Cosine similarity</strong>: the default for most modern embedding models (OpenAI&#8217;s text-embedding-ada-002, Sentence-BERT). Ranges -1 to 1. If your vectors are normalized, cosine and dot product give the same ranking.</p></li><li><p><strong>Dot product</strong>: faster on modern hardware, especially GPUs, because it&#8217;s just multiply-and-accumulate. Use it when vectors are normalized.</p></li><li><p><strong>Euclidean (L2)</strong>: matters when vector magnitude carries meaning. Rare in text retrieval, more common in computer vision.</p></li></ul><blockquote><p>&#9888;&#65039; <strong>Watch out:</strong> Using the wrong distance metric doesn&#8217;t error out - it just quietly returns worse results. If you&#8217;re not sure what your model was trained with, default to cosine, but check your database&#8217;s own default too. Pinecone and Qdrant default to cosine; Milvus supports all three. Set it explicitly instead of trusting the default silently.</p></blockquote><div><hr></div><h2>Index types: speed, memory, accuracy : pick two</h2><p>This is the decision that shapes everything else.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Ffsm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Ffsm!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ffsm!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ffsm!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ffsm!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Ffsm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:715754,&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://avanichaskar.substack.com/i/211154898?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.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_!Ffsm!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ffsm!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ffsm!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ffsm!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e0e2c75-38c2-4d82-b1b9-071c4f725a34_1024x572.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>HNSW (Hierarchical Navigable Small World)</h3><p>A multi-layer graph you navigate top-down to find nearest neighbors - think a hierarchy of neighborhoods, coarse at the top, fine-grained at the bottom. It&#8217;s the default in most production RAG stacks.</p><ul><li><p><strong>Strengths:</strong> fast queries, 95&#8211;99% recall typical.</p></li><li><p><strong>Weaknesses:</strong> memory-hungry, slow to build at large scale, awkward to update in place.</p></li><li><p><strong>Tuning:</strong> <code>M</code> (connections per node, default 16) and <code>ef_construct</code> (build-time search effort, default 200). Both trade memory and build time for recall.</p></li><li><p><strong>Use it when:</strong> you&#8217;re under 100M vectors, need sub-100ms latency, and have memory to spare.</p></li></ul><h3>IVF (Inverted File Index)</h3><p>Buckets vectors with k-means; a query only searches the closest buckets - like a librarian who knows which shelf to check instead of scanning the whole library.</p><ul><li><p><strong>Strengths:</strong> scales to billions, memory-efficient, plays well with disk-backed storage.</p></li><li><p><strong>Weaknesses:</strong> lower recall than HNSW unless you raise <code>nprobe</code> - which costs latency.</p></li><li><p><strong>Use it when:</strong> you&#8217;re past 100M vectors, cost matters more than raw speed, or you&#8217;re disk-backed.</p></li></ul><h3>Flat (brute force)</h3><p>No index. No approximation. Just an exhaustive scan.</p><ul><li><p><strong>Strengths:</strong> 100% recall, zero build time.</p></li><li><p><strong>Weaknesses:</strong> O(N) search - falls apart past roughly 100K vectors.</p></li><li><p><strong>Use it when:</strong> you&#8217;re prototyping, under 50K vectors, or need exact results to verify another index&#8217;s recall.</p></li></ul><blockquote><p>&#128161; <strong>Tip:</strong> Milvus and Qdrant default to HNSW. So does Elasticsearch&#8217;s Lucene engine. OpenSearch gives you a choice between HNSW and IVF. When in doubt, start with your database&#8217;s default - it&#8217;s usually HNSW for good reason.</p></blockquote><div><hr></div><h2>Quantization: the memory bill gets real fast</h2><p>A single 1536-dimensional float32 vector is 6 KB. A million of them is 6 GB - before the index itself adds another 2&#8211;3x on top. Quantization is how you claw that back.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!nVgR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!nVgR!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png 424w, /__u/substackcdn.com/image/fetch/$s_!nVgR!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png 848w, /__u/substackcdn.com/image/fetch/$s_!nVgR!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nVgR!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!nVgR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png" width="1024" height="260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:260,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:385496,&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://avanichaskar.substack.com/i/211154898?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00c8ae22-51e3-4a4a-adac-9340f88de679_1024x572.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_!nVgR!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png 424w, /__u/substackcdn.com/image/fetch/$s_!nVgR!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png 848w, /__u/substackcdn.com/image/fetch/$s_!nVgR!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nVgR!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6964ddce-a78e-4bd1-8e88-ce3e54037d8f_1024x260.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><strong>Product Quantization (PQ)</strong> splits each vector into sub-vectors, clusters each one, and stores cluster IDs instead of raw floats. &#8594; 8&#8211;32x compression, 90&#8211;95% recall with rescoring. Used by Milvus, Faiss, Qdrant.</p></li><li><p><strong>Scalar Quantization (SQ)</strong> compresses float32 down to int8 or uint8 per dimension. &#8594; 4x compression, under 1% recall drop if calibrated well. Used by Qdrant, Milvus.</p></li><li><p><strong>Binary Quantization (BBQ)</strong> maps vectors to binary codes. Elasticsearch-exclusive, and the default as of version 9.1 for dimensions &#8805; 384. &#8594; 32x compression. In Elastic&#8217;s own benchmarks, BBQ actually <em>improved</em> ranking quality on 9 of 10 test datasets - likely a denoising side effect of the compression.</p></li></ul><blockquote><p>&#128161; <strong>Tip:</strong> The pattern that makes all of these viable: <strong>quantize at index time, rescore at query time.</strong> Run the approximate search on compressed vectors, then compute exact distances on just the top candidates. You keep most of the speed and nearly all of the recall.</p></blockquote><div><hr></div><h2>Metadata filtering: where most RAG systems quietly break</h2><p>Here&#8217;s a query that looks simple but isn&#8217;t: <em>find documents similar to this, but only from tenant Acme, created after January, with admin-level permissions.</em></p><p>That&#8217;s not a vector search. That&#8217;s a database query with a vector search attached - and your database needs a real index on every field in that filter, or it falls back to scanning the full payload store.</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/LShgf/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65c4e11a-5f63-4dd7-b535-47a7d919db4b_1220x600.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee6b3854-0194-4e95-9791-3bf1d15f62cf_1220x600.png&quot;,&quot;height&quot;:294,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/LShgf/1/" width="730" height="294" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h3>Pre-filter vs. post-filter</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!8ts1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!8ts1!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!8ts1!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!8ts1!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8ts1!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!8ts1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:656064,&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://avanichaskar.substack.com/i/211154898?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.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_!8ts1!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!8ts1!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!8ts1!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8ts1!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623ed80d-4463-4703-8c76-f1a0ac634923_1024x572.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><strong>Post-filtering</strong> runs the approximate search first, then applies your filter. Fine if the filter is broad. If it&#8217;s narrow - matching under 1% of your data - your top-k results can contain zero matches, forcing you to over-fetch (<code>top_k = 200</code> to get 10 real results), which tanks latency.</p></li><li><p><strong>Pre-filtering</strong> applies the filter first, then searches only that subset. Fast when the filter is narrow, slow when it&#8217;s broad - the inverse problem.</p></li></ul><blockquote><p>&#9888;&#65039; <strong>Watch out:</strong> Most mature engines now do both at once: walk the HNSW graph and check the filter at every candidate node, skipping ones that fail. Qdrant&#8217;s payload indexing, Milvus&#8217;s bitset filtering, and Elasticsearch&#8217;s ACORN all implement a version of this. ACORN goes further, reshaping traversal to prioritize filtered regions - up to 5x faster in Elastic&#8217;s own benchmarks.</p></blockquote><div><hr></div><h2>Sharding and partitioning at scale</h2><p>No single node holds a billion vectors comfortably. Once you&#8217;re past that scale, you need a partitioning strategy.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!r9HX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!r9HX!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png 424w, /__u/substackcdn.com/image/fetch/$s_!r9HX!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png 848w, /__u/substackcdn.com/image/fetch/$s_!r9HX!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png 1272w, /__u/substackcdn.com/image/fetch/$s_!r9HX!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!r9HX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png" width="900" height="427" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee2cfe73-194d-4565-b875-733f677b3b60_900x427.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:427,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:586226,&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://avanichaskar.substack.com/i/211154898?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4fddb4b-22c1-40a9-b514-7aac335bb7f8_1024x572.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_!r9HX!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png 424w, /__u/substackcdn.com/image/fetch/$s_!r9HX!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png 848w, /__u/substackcdn.com/image/fetch/$s_!r9HX!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.png 1272w, /__u/substackcdn.com/image/fetch/$s_!r9HX!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee2cfe73-194d-4565-b875-733f677b3b60_900x427.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>Partition by tenant</h3><p>The natural fit for most RAG systems. Each tenant&#8217;s vectors live on their own shard.</p><ul><li><p><strong>Upside:</strong> filtering by tenant becomes partition elimination - essentially free.</p></li><li><p><strong>Downside:</strong> one large tenant can unbalance your cluster and needs custom shard allocation.</p></li><li><p><strong>Implementations:</strong> Pinecone namespaces, Milvus partition keys, Qdrant shard keys.</p></li></ul><h3>Replication</h3><p>Replicas give you both high availability and read scalability. Write to the primary, read from replicas. A rough sizing rule: <code>(desired QPS / QPS per node) - 1</code> replicas, keeping in mind each one doubles storage cost.</p><blockquote><p>&#128161; <strong>Tip:</strong> Start with one primary and one replica for HA, scale from there, and never put two replicas on the same physical host.</p></blockquote><div><hr></div><h2>Keeping the index fresh: updates, deletes, TTL</h2><p>Your RAG index isn&#8217;t static - documents change, permissions get revoked, content ages out.</p><ul><li><p><strong>Updates:</strong> HNSW graphs don&#8217;t support true in-place updates. Under the hood, an update is a delete plus an insert: the old vector gets tombstoned, the new one added, and compaction cleans up later. Bulk updates will temporarily inflate your index size - plan for it.</p></li><li><p><strong>Deletes:</strong> removing a node breaks graph edges, so the database either rebuilds around it or tombstones it and cleans up during compaction. Batch your deletes; don&#8217;t loop single deletes.</p></li><li><p><strong>TTL:</strong> useful for anything ephemeral - news, live chat, session data. Elasticsearch and OpenSearch handle this through index lifecycle management, Qdrant has native point TTL, Milvus relies on manual compaction or retention policies.</p></li></ul><div><hr></div><h2>Hybrid search: vectors alone aren&#8217;t enough</h2><p>Pure vector search misses exact strings. A query embedding for &#8220;invoice number&#8221; won&#8217;t reliably surface <code>INV-2026-001</code> - because what matters there is the literal string, not the semantic neighborhood.</p><p>Hybrid search combines semantic and keyword scores, usually via Reciprocal Rank Fusion:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\text{Score}_{\\text{RRF}}(d) = \\sum_{\\text{searcher} \\in S} \\frac{1}{\\text{rank}_{\\text{searcher}}(d) + k}\n&quot;,&quot;id&quot;:&quot;CFMJRWIFMN&quot;}" data-component-name="LatexBlockToDOM"></div><blockquote><p>&#128161; <strong>Tip:</strong> Turn hybrid search on by default. Weaviate supports it natively, Pinecone offers sparse-dense search, Elasticsearch and OpenSearch have built-in RRF queries, and Qdrant supports it through custom scoring. Most real-world queries contain names, SKUs, or IDs alongside semantic intent, not pure meaning.</p></blockquote><div><hr></div><h2>Common failures in production </h2><p>Perfect index type, tuned HNSW parameters, clean metadata indexes - and it still breaks, usually months in, usually while no one&#8217;s watching. Here&#8217;s what that looks like and how to catch it early.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!NYft!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!NYft!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!NYft!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!NYft!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NYft!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!NYft!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:668095,&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://avanichaskar.substack.com/i/211154898?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.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_!NYft!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!NYft!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!NYft!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NYft!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ee61137-b754-477f-b95c-b01a2beb987a_1024x572.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><strong>1. The silent filter failure.</strong> A narrow post-filter (say, 0.5% of your data) can return zero results from your top-k ANN search - no error, just silence. The LLM gets nothing and hallucinates a generic answer. <em>Fix:</em> monitor <code>retrieved_count</code> vs. <code>returned_count</code>; if they diverge consistently, switch to pre-filtering or a hybrid approach like ACORN, and set an oversampling factor (<code>limit * 10</code>).</p><p><strong>2. The compaction storm.</strong> Background segment merges are CPU- and I/O-heavy. If one lands during peak traffic, p99 latency can jump from 120ms to several seconds with no warning. <em>Fix:</em> schedule compaction for off-peak hours (Qdrant&#8217;s <code>optimization.schedule</code>, Milvus&#8217;s <code>dataCoord.segment.maxSize</code>) and watch queue depth.</p><p><strong>3. The hot tenant.</strong> Sharding by tenant assumes tenants are roughly the same size - enterprise customers aren&#8217;t. One 50M-vector tenant can drag down a shard built for 100K-vector accounts. <em>Fix:</em> a two-level strategy - small tenants share shards, large ones get dedicated ones - and rebalance once any shard passes ~10% of cluster capacity.</p><p><strong>4. Tombstone accumulation.</strong> Frequent updates leave dead entries that only compaction clears. Left unmonitored, a 10M-vector index can quietly be carrying 2M tombstones, with real latency and memory cost attached. <em>Fix:</em> alert when <code>deleted_vectors / total_vectors</code> exceeds 15% and force compaction.</p><p><strong>5. Model drift.</strong> You re-index with a new embedding model version, but the query-side service still uses the old one. Query and document vectors now live in different latent spaces, and cosine similarity stops meaning anything - recall can drop from 90%+ into the 60s before anyone notices. <em>Fix:</em> version your embedding model in payload metadata, log the version at query time, alert on mismatch, and dual-write during migrations.</p><p><strong>6. OOM on index build.</strong> HNSW build memory scales with <code>M * ef_construct</code>. Aggressive settings for max recall (<code>M=64</code>, <code>ef_construct=400</code>) on a 50M-vector build can demand far more RAM than your node has, triggering an OOM crash loop. <em>Fix:</em> build conservatively first (<code>M=16</code>, <code>ef_construct=200</code>), test on a subset, and rely on query-time rescoring to recover recall - or use IVF+PQ, which builds on disk.</p><p><strong>7. Snapshot corruption.</strong> A backup taken mid-compaction, without quiescing writes, can fail checksum validation on restore - meaning the backup you thought you had doesn&#8217;t actually work. <em>Fix:</em> use native backup tooling (Qdrant&#8217;s <code>snapshot</code> API with <code>wait=True</code>, Elasticsearch&#8217;s snapshot lifecycle with <code>wait_for_completion: true</code>) and test restores on staging monthly.</p><p><strong>8. Metadata cardinality explosion.</strong> An unindexed high-cardinality field like <code>user_id</code> forces a full payload scan on every filtered query - p95 latency can climb into the seconds. <em>Fix:</em> use the right index for the cardinality (B-tree or hash, not inverted lists), or partition by that field directly if it&#8217;s your primary access pattern.</p><div><hr></div><h2>Comparing Vector DBs</h2><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/CYdv4/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf1eeb1c-b49b-4fc5-876e-da7e25d091b8_1220x1294.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23651620-1d70-4366-9210-03038978e2be_1220x1294.png&quot;,&quot;height&quot;:649,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/CYdv4/1/" width="730" height="649" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><div><hr></div><h2>Reference implementation </h2><h4>Qdrant</h4><p>Explicit HNSW config, payload indexing, batched ingestion, and RRF hybrid search:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ead7c88a-b0cd-4d67-9cce-65ca4a006dcf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import uuid
from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance, VectorParams, HnswConfig,
    PayloadIndexParams, FieldCondition, MatchValue,
    Range, Filter, SearchRequest, SearchQuery
)
from sentence_transformers import SentenceTransformer

client = QdrantClient(host="localhost", port=6333)

# Tuned HNSW for ~1M vectors, 384 dims
hnsw_config = HnswConfig(
    m=32,
    ef_construct=200,
    full_scan_threshold=10000
)

payload_index_config = PayloadIndexParams(
    index_on_fields=["tenant", "source", "created_at", "user_role"]
)

client.create_collection(
    collection_name="documents_prod",
    vectors_config=VectorParams(
        size=384,
        distance=Distance.COSINE,
        hnsw_config=hnsw_config
    ),
    payload_index_config=payload_index_config,
    shard_number=4,
    sharding_method="tenant"
)

model = SentenceTransformer('all-MiniLM-L6-v2')

def batch_ingest(documents, batch_size=128):
    for i in range(0, len(documents), batch_size):
        batch = documents[i:i+batch_size]
        points = []
        for doc in batch:
            vector = model.encode(doc["text"]).tolist()
            points.append({
                "id": str(uuid.uuid4()),
                "vector": vector,
                "payload": {
                    "text": doc["text"],
                    "tenant": doc["tenant"],
                    "source": doc["source"],
                    "created_at": doc["created_at"],
                    "doc_type": doc.get("doc_type", "general"),
                    "user_role": doc.get("user_role", "viewer")
                }
            })
        client.upsert(collection_name="documents_prod", points=points, wait=True)
        print(f"Ingested {i+len(batch)} documents...")

def secure_search(query, tenant, user_role, date_range=None):
    vector = model.encode(query).tolist()

    must_conditions = [
        FieldCondition(key="tenant", match=MatchValue(value=tenant)),
        FieldCondition(key="user_role", match=MatchValue(value=user_role))
    ]
    if date_range:
        must_conditions.append(
            FieldCondition(key="created_at", range=Range(
                gte=date_range["start"], lte=date_range["end"]
            ))
        )

    return client.search(
        collection_name="documents_prod",
        query_vector=vector,
        query_filter=Filter(must=must_conditions),
        limit=5,
        with_payload=True,
        with_vectors=False
    )

def hybrid_search(query, tenant, dense_vector, sparse_vector):
    return client.search_batch(
        collection_name="documents_prod",
        requests=[
            SearchRequest(query=SearchQuery(query_vector=dense_vector), limit=10),
            SearchRequest(query=SearchQuery(query_vector=sparse_vector), limit=10)
        ],
        fusion="rrf",
        query_filter=Filter(must=[FieldCondition(key="tenant", match=MatchValue(value=tenant))])
    )
</code></pre></div><h4>Elasticsearch</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;6ca4ca8d-ad75-4c14-ae06-98591aac1378&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from elasticsearch import Elasticsearch
from sentence_transformers import SentenceTransformer

es = Elasticsearch("http://localhost:9200")
model = SentenceTransformer('all-MiniLM-L6-v2')

es.indices.create(
    index="documents",
    mappings={
        "properties": {
            "content_vector": {
                "type": "dense_vector",
                "dims": 384,
                "index": True,
                "similarity": "cosine"
            },
            "tenant": {"type": "keyword"},
            "source": {"type": "keyword"},
            "date": {"type": "date"},
            "text": {"type": "text"}
        }
    }
)

doc = {
    "text": "Login error on mobile app",
    "tenant": "acme",
    "source": "support_tickets",
    "date": "2026-01-15",
    "content_vector": model.encode("Login error on mobile app").tolist()
}
es.index(index="documents", document=doc)

query_vector = model.encode("login problems").tolist()
response = es.search(
    index="documents",
    body={
        "size": 5,
        "query": {
            "knn": {
                "field": "content_vector",
                "query_vector": query_vector,
                "k": 10,
                "filter": {
                    "bool": {
                        "must": [
                            {"term": {"tenant": "acme"}},
                            {"term": {"source": "support_tickets"}},
                            {"range": {"date": {"gte": "2026-01-01"}}}
                        ]
                    }
                }
            }
        }
    }
)
</code></pre></div><div><hr></div><h2>What to monitor once it&#8217;s live</h2><ul><li><p><strong>Query latency (p50/p95/p99)</strong>: aim under 200ms for chat use cases; users start noticing past 500ms p95.</p></li><li><p><strong>QPS per tenant</strong>: your earliest signal for a hot partition or abuse.</p></li><li><p><strong>Indexing rate</strong>: a falling rate usually points to disk or CPU saturation.</p></li><li><p><strong>Memory usage</strong>: HNSW is memory-hungry; track heap vs. off-heap separately.</p></li><li><p><strong>Disk I/O</strong>: compaction storms show up here before they show up in latency graphs.</p></li><li><p><strong>Recall@10</strong>: run nightly evaluations. This is the one metric that tells you retrieval is actually working, not just fast.</p></li><li><p><strong>Cost levers:</strong> keep the index in memory, push raw vectors to disk; use PQ/SQ/BBQ wherever your recall budget allows; schedule compactions off-peak; right-size self-hosted nodes (Graviton instances often win on price/performance).</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_!9HK9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!9HK9!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!9HK9!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!9HK9!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9HK9!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!9HK9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:212209,&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://avanichaskar.substack.com/i/211154898?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.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_!9HK9!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!9HK9!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!9HK9!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9HK9!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084055d6-2503-4ce1-9f49-7954e2998ab5_1024x572.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>Pre-release checklist</h2><p>&#10004;&#65039; Distance metric matches the embedding model<br>&#10004;&#65039; HNSW tuned for your memory and recall targets<br>&#10004;&#65039; Quantization enabled with rescoring<br>&#10004;&#65039; Metadata indexes on every filter field<br>&#10004;&#65039; Sharding strategy defined (tenant-based where possible)<br>&#10004;&#65039; At least two replicas for HA<br>&#10004;&#65039; TTL / lifecycle policies configured<br>&#10004;&#65039; Hybrid search wired up with RRF<br>&#10004;&#65039; Dashboards for latency p95, QPS, memory, compaction<br>&#10004;&#65039; Scheduled snapshot backups to blob storage<br>&#10004;&#65039; Rollback plan tested<br>&#10004;&#65039; Alerting on <code>retrieved_count</code> vs. <code>returned_count</code> divergence &gt; 20%<br>&#10004;&#65039; Compaction scheduled off-peak; queue depth monitored<br>&#10004;&#65039; Shard/tenant size tracking; rebalance past 10% of cluster capacity<br>&#10004;&#65039; Alert on <code>deleted_vectors / total_vectors</code> &gt; 15%<br>&#10004;&#65039; Embedding model versioning with mismatch alerts<br>&#10004;&#65039; Index builds tested on a subset before full scale<br>&#10004;&#65039; Snapshot restores verified on staging monthly<br>&#10004;&#65039; High-cardinality filter fields properly indexed</p><div><hr></div><h2>Which vector database should you actually pick?</h2><ul><li><p><strong>First production RAG system:</strong> Qdrant or Pinecone. Sensible defaults, quantization is easy to turn on early, and you can get filtering right before optimizing anything else.</p></li><li><p><strong>Enterprise scale:</strong> Milvus or Elasticsearch. More operational work, but you get billion-scale and native hybrid search.</p></li><li><p><strong>Already running Elasticsearch or OpenSearch:</strong> use what you have. ACORN and BBQ are genuinely strong, and it&#8217;s one less system for your team to operate.</p></li><li><p><strong>No need for search-engine features:</strong> a dedicated vector database (Qdrant, Pinecone) will be simpler to run.</p></li><li><p><strong>Already on Postgres:</strong> pgvector is a reasonable choice under roughly 1M vectors.</p></li></ul><div><hr></div><h2>Summary</h2><ul><li><p><strong>What is a vector index in a RAG system?</strong> <br>A vector index is the data structure a vector database builds over your embeddings - usually a graph (HNSW) or a set of partitions (IVF) - so it can find nearest neighbors in milliseconds instead of comparing a query against every stored vector.</p></li><li><p><strong>HNSW vs. IVF - which should I use?</strong> <br>HNSW for most systems under 100M vectors where you want the best recall and speed and have memory to spare. IVF once you&#8217;re past that scale, are disk-backed, or cost matters more than shaving off milliseconds.</p></li><li><p><strong>Does quantization hurt retrieval quality?</strong> <br>Modestly, and it&#8217;s recoverable. Scalar quantization typically costs under 1% recall; product quantization costs more but rescoring at query time recovers most of it. Binary quantization has, in some benchmarks, actually improved ranking.</p></li><li><p><strong>Why did my RAG system&#8217;s recall suddenly drop?</strong> <br>The most common cause is model drift - the query-side embedding service is on a different model version than the one used to build the index. Check embedding model versions first.</p></li><li><p><strong>How do I filter vector search results by tenant or permissions?</strong> <br>Index every field you filter on (don&#8217;t rely on a full payload scan), and choose pre-filtering for narrow filters or hybrid filtering (like Elasticsearch&#8217;s ACORN) if your filter selectivity varies a lot.</p></li></ul><div><hr></div><p><em>If you&#8217;re building RAG in production, I&#8217;d genuinely like to hear what&#8217;s breaking for you - drop it in the comments, or subscribe to get the rest of this series (retrieval optimization is next) as it lands.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/system-design-for-rag-the-vector/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/system-design-for-rag-the-vector/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[System Design for RAG: The Embedding Layer]]></title><description><![CDATA[Everything you need to design an embedding layer for RAG in production.]]></description><link>https://avanichaskar.substack.com/p/system-design-for-rag-the-embedding</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-for-rag-the-embedding</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Wed, 12 Aug 2026 14:55:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!w1r5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The document chunking pipeline has run. You&#8217;re left with clean text segments, sensible token boundaries, and rich metadata attached to every payload. But in-memory strings are useless to a vector database - it operates strictly on dense floating-point embeddings that capture semantic intent.</p><p>Bridging that gap is the job of the embedding layer.</p><p>While transforming text into dense vectors looks like a basic API wrapper on paper, it is a primary point of failure in production RAG architecture. It&#8217;s where systems quietly degrade retrieval recall, hit severe latency bottlenecks, or expose multi-tenant data through poor isolation patterns.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!w1r5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!w1r5!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png 424w, /__u/substackcdn.com/image/fetch/$s_!w1r5!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png 848w, /__u/substackcdn.com/image/fetch/$s_!w1r5!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png 1272w, /__u/substackcdn.com/image/fetch/$s_!w1r5!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!w1r5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png" width="1456" height="826" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:826,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:222086,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.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_!w1r5!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png 424w, /__u/substackcdn.com/image/fetch/$s_!w1r5!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png 848w, /__u/substackcdn.com/image/fetch/$s_!w1r5!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.png 1272w, /__u/substackcdn.com/image/fetch/$s_!w1r5!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee3f8e5-cf2e-40bb-8a6d-c920a4f7ad7e_1766x1002.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>What Are Embeddings?</h2><p>An embedding is a fixed-length vector of floating-point numbers representing the underlying meaning of an input. High-dimensional vector spaces position semantically similar inputs close together.</p><p>Downstream components- such as vector indexes and LLMs- cannot compute similarity on raw string literals. They operate on dense arrays.</p><ul><li><p><strong>Vectors:</strong> The baseline unit of data for similarity algorithms.</p></li><li><p><strong>Vector Space:</strong> The coordinate frame defined by the embedding model&#8217;s weights.</p></li><li><p><strong>Semantic Distance:</strong> The mathematical proximity between two points in vector space.</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_!a4wK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png 424w, /__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png 848w, /__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png 1272w, /__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!a4wK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png" width="1456" height="758" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:758,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:281469,&quot;alt&quot;:&quot;&quot;,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png 424w, /__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png 848w, /__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.png 1272w, /__u/substackcdn.com/image/fetch/$s_!a4wK!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a4a13d-9933-465a-a13f-e949a05c8785_1794x934.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>How to Generate Embeddings</h2><h4>API vs. Self-Hosted Infrastructure</h4><p>Selecting an execution pattern depends on privacy requirements, latency constraints, and operational cost budgets.</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/nyk9q/2/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66c989f3-8200-47ac-8cce-5bb1054a1436_1220x658.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1829a9ff-d89d-43dc-8160-5d8d6fc5c90c_1220x658.png&quot;,&quot;height&quot;:325,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/nyk9q/2/" width="730" height="325" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h3>The Inference Pipeline: Batching and Retries</h3><p>Calling an embedding endpoint once per chunk destroys throughput. Network round-trips bottleneck the system. Always batch requests.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;a001677c-8ddd-4e26-bb62-6a604fe924e3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># embedder.py
import time
import random
import numpy as np
from abc import ABC, abstractmethod
from dataclasses import dataclass

@dataclass
class EmbeddingResult:
    vector: np.ndarray
    model_name: str
    dimensions: int

class TextEmbedder(ABC):
    @abstractmethod
    def embed(self, texts: list[str]) -&gt; list[EmbeddingResult]:
        ...

class OpenAIEmbedder(TextEmbedder):
    def __init__(self, model: str = "text-embedding-3-small", dimensions: int | None = None):
        from openai import OpenAI
        self.client = OpenAI()
        self.model = model
        self.dimensions = dimensions

    def embed(self, texts: list[str]) -&gt; list[EmbeddingResult]:
        kwargs = {"model": self.model, "input": texts}
        if self.dimensions:
            kwargs["dimensions"] = self.dimensions
        response = self.client.embeddings.create(**kwargs)
        return [
            EmbeddingResult(
                vector=np.array(item.embedding, dtype=np.float32),
                model_name=self.model,
                dimensions=len(item.embedding),
            )
            for item in response.data
        ]

def embed_in_batches_with_retry(texts: list[str], embedder: TextEmbedder, batch_size: int = 100, max_retries: int = 5):
    results = []
    for i in range(0, len(texts), batch_size):
        batch = texts[i:i + batch_size]
        for attempt in range(max_retries):
            try:
                results.extend(embedder.embed(batch))
                break
            except Exception:
                if attempt == max_retries - 1:
                    raise
                wait = (2 ** attempt) + random.uniform(0, 1)
                time.sleep(wait)
    return results
</code></pre></div><div><hr></div><h2>Why the Embedding Layer Needs a Design Review: What Breaks in Production</h2><h4>Silent Failure Modes &amp; Domain Drift</h4><p>Embedding layers fail quietly. When an embedding model misinterprets domain vocabulary, the vector database returns irrelevant context without throwing an error. A generic model places &#8220;termination clause&#8221; and &#8220;resignation letter&#8221; close together. A legal-focused model keeps them distinct. Upstream parsing errors propagate into vector space silently.</p><h4>Asymmetric Retrieval &amp; Instruction Prefixes</h4><p>Queries and passages serve different roles. Queries are short and ambiguous. Passages are dense and factual. Symmetric similarity algorithms degrade when matching these two distinct text types.</p><p>Dual-encoder models handle asymmetric retrieval via prefix instructions. Omitting prefixes degrades retrieval performance.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;508578b1-6d6d-4db5-a795-0d5347107be6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def format_instruction(text: str, is_query: bool) -&gt; str:
    """E5 and BGE model families require explicit task prefixes."""
    prefix = "query: " if is_query else "passage: "
    return prefix + text
</code></pre></div><h4>Vector Truncation Bugs</h4><p>If a chunk exceeds the model&#8217;s max context window, provider APIs silently truncate the input. The vector then represents only a fraction of the source text</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!40GE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!40GE!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png 424w, /__u/substackcdn.com/image/fetch/$s_!40GE!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png 848w, /__u/substackcdn.com/image/fetch/$s_!40GE!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png 1272w, /__u/substackcdn.com/image/fetch/$s_!40GE!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!40GE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png" width="314" height="454" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:454,&quot;width&quot;:314,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30883,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.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_!40GE!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png 424w, /__u/substackcdn.com/image/fetch/$s_!40GE!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png 848w, /__u/substackcdn.com/image/fetch/$s_!40GE!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.png 1272w, /__u/substackcdn.com/image/fetch/$s_!40GE!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4db6051-8b58-4153-a5ce-6264a6ec07c9_314x454.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>Anatomy of an Embedding Model</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!AXdS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!AXdS!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png 424w, /__u/substackcdn.com/image/fetch/$s_!AXdS!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png 848w, /__u/substackcdn.com/image/fetch/$s_!AXdS!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png 1272w, /__u/substackcdn.com/image/fetch/$s_!AXdS!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!AXdS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png" width="1456" height="629" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1237170-5820-4c48-b65f-9c64919df33c_1462x632.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:629,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87976,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.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_!AXdS!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png 424w, /__u/substackcdn.com/image/fetch/$s_!AXdS!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png 848w, /__u/substackcdn.com/image/fetch/$s_!AXdS!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.png 1272w, /__u/substackcdn.com/image/fetch/$s_!AXdS!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1237170-5820-4c48-b65f-9c64919df33c_1462x632.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><h4>Context Window vs. Chunk Size</h4><ul><li><p><strong>Context Window:</strong> The upper limit of tokens the embedding encoder accepts.</p></li><li><p><strong>Chunk Size:</strong> The segmented unit of text produced by the parsing layer.</p></li><li><p><strong>Architecture Constraint:</strong> Keep maximum chunk size safely below context limits to avoid truncation.</p></li></ul><h4>Dimensionality &amp; Vector Space</h4><p>Dimensionality determines vector resolution. Higher dimensions increase semantic granularity but linearly increase memory consumption and retrieval latency.</p><h4>Similarity Metrics</h4><ul><li><p><strong>Cosine Similarity:</strong> Measures the cosine of the angle between two vectors. Ignores vector magnitude.</p></li><li><p><strong>Dot Product:</strong> Measures angle and magnitude. Requires unit-normalized vectors (&#8739;<span>v</span>&#8739;=1) to equal cosine distance.</p></li><li><p><strong>Euclidean Distance (L<span>2</span>&#8203;):</strong> Measures straight-line distance between vector points. Sensitive to vector magnitude.</p></li></ul><div><hr></div><h2>Choosing the Right Model</h2><p>Model leaderboards (e.g., MTEB) measure generic benchmarks. They do not reflect domain-specific retrieval needs.</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/CFGnq/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38369595-6121-401b-9b75-83319267b5e6_1220x806.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/92b05629-5997-4e8e-9d35-eec3026f84df_1220x806.png&quot;,&quot;height&quot;:400,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/CFGnq/1/" width="730" height="400" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><div><hr></div><h2>Embeddings for Different Data Types</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!92RU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!92RU!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png 424w, /__u/substackcdn.com/image/fetch/$s_!92RU!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png 848w, /__u/substackcdn.com/image/fetch/$s_!92RU!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png 1272w, /__u/substackcdn.com/image/fetch/$s_!92RU!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!92RU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png" width="1456" height="679" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:679,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86583,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.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_!92RU!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png 424w, /__u/substackcdn.com/image/fetch/$s_!92RU!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png 848w, /__u/substackcdn.com/image/fetch/$s_!92RU!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.png 1272w, /__u/substackcdn.com/image/fetch/$s_!92RU!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b69e7d2-46f5-4804-9c15-1a69b2218dfc_1462x682.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><h4>Text Chunks</h4><p>Text chunks pass directly through the text embedding encoder.</p><h4>Images: Vision Encoders vs. VLM Captions</h4><p>Embed images using two distinct representations:</p><ol><li><p><strong>Direct Vision Vectors:</strong> Compute embeddings using multi-modal encoders (e.g., SigLIP, CLIP).</p></li><li><p><strong>Text Caption Vectors:</strong> Pass images to a Vision-Language Model (VLM) to generate dense descriptions. Embed the resulting text.</p></li></ol><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;032f10d6-9513-4ba1-ac8e-c36aa653c96a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># image_embedder.py
import torch
from PIL import Image
from transformers import AutoModel, AutoProcessor

class ImageEmbedder:
    def __init__(self, model_name: str = "google/siglip-so400m-patch14-384"):
        self.model = AutoModel.from_pretrained(model_name)
        self.processor = AutoProcessor.from_pretrained(model_name)
        self.model.eval()

    @torch.no_grad()
    def embed_image(self, image: Image.Image) -&gt; list[float]:
        inputs = self.processor(images=[image], return_tensors="pt")
        features = self.model.get_image_features(**inputs)
        normalized = torch.nn.functional.normalize(features, dim=-1)
        return normalized[0].tolist()

def generate_vlm_caption(image: Image.Image, vlm_client) -&gt; str:
    prompt = "Describe this image in 2-3 sentences. Include exact visible text, numbers, and key data points."
    return vlm_client.generate(image=image, prompt=prompt).text
</code></pre></div><h4>Tables: Serialization vs. Structured Hybrid Retrieval</h4><p>Dense text models misinterpret raw tabular grids. Use the following strategies:</p><ol><li><p><strong>Row Serialization:</strong> Convert rows into structured sentences preserving key-value pairs.</p></li><li><p><strong>Table Summary:</strong> Generate single-sentence LLM summaries for small reference grids.</p></li><li><p><strong>Structured Storage:</strong> Route complex numeric tables to relational engines (e.g., PostgreSQL, DuckDB). Use embeddings for table discovery only.</p></li></ol><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;17734f70-3d76-4897-a1c8-2651cc185b10&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import pandas as pd

def serialize_table_rows(df: pd.DataFrame, table_title: str = "") -&gt; list[str]:
    serialized = []
    for _, row in df.iterrows():
        parts = [f"{col}: {row[col]}" for col in df.columns]
        sentence = f"{table_title}. " + ", ".join(parts) if table_title else ", ".join(parts)
        serialized.append(sentence)
    return serialized
</code></pre></div><div><hr></div><h2>Production &amp; Architecture Challenges</h2><h4>Storage Cost Scaling &amp; Quantization</h4><p>A 10-million-chunk corpus at 1536 dimensions using float32 requires 61.4 GB of unindexed memory.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!B-uW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!B-uW!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png 424w, /__u/substackcdn.com/image/fetch/$s_!B-uW!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png 848w, /__u/substackcdn.com/image/fetch/$s_!B-uW!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png 1272w, /__u/substackcdn.com/image/fetch/$s_!B-uW!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!B-uW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png" width="1208" height="792" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:792,&quot;width&quot;:1208,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131922,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.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_!B-uW!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png 424w, /__u/substackcdn.com/image/fetch/$s_!B-uW!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png 848w, /__u/substackcdn.com/image/fetch/$s_!B-uW!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.png 1272w, /__u/substackcdn.com/image/fetch/$s_!B-uW!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1a6a9df-b0c1-43fa-9b42-8d90ccbd385c_1208x792.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><strong>Matryoshka Representation Learning (MRL):</strong> Truncates vectors to lower dimensions (e.g., 1536 down to 512) while retaining core semantics. Slice and re-normalize manual vectors to unit length.</p></li><li><p><strong>Scalar Quantization (int8):</strong> Converts 32-bit floats to 8-bit integers. Reduces storage footprints by ~75% with minimal recall degradation.</p></li><li><p><strong>Two-Stage Search:</strong> Perform initial vector filtering using quantized representations. Rerank the candidate set using full-precision vectors.</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ed0c8623-eb34-4e49-bd87-77e03d51dead&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def search_with_rescoring(query_vector, index, top_k: int = 5, candidate_k: int = 50):
    candidates = index.search(query_vector, k=candidate_k, use_quantized=True)
    return index.rescore(query_vector, candidates, precision="float32")[:top_k]
</code></pre></div><h4>Async Execution Pipelines &amp; Eventual Consistency</h4><p>Generating image captions via VLMs runs 100x slower than text inference. Do not block ingestion jobs. Push multi-modal items to background queues. Store text immediately; backfill vision vectors asynchronously.</p><div><hr></div><h2>Security, Privacy, and Access Control</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!M727!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!M727!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png 424w, /__u/substackcdn.com/image/fetch/$s_!M727!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png 848w, /__u/substackcdn.com/image/fetch/$s_!M727!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M727!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!M727!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png" width="1208" height="792" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:792,&quot;width&quot;:1208,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:145587,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.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_!M727!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png 424w, /__u/substackcdn.com/image/fetch/$s_!M727!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png 848w, /__u/substackcdn.com/image/fetch/$s_!M727!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M727!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fea15d1-d0d9-4120-9d29-2ac66ca62f43_1208x792.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><h4>Secret Management &amp; Credential Scoping</h4><ul><li><p>Do not hardcode credentials in codebases. Inject API keys at runtime via environment variables.</p></li><li><p>Scope keys strictly per deployment environment (Development, Staging, Production).</p></li></ul><h4>Embedding Inversion &amp; PII Leaks</h4><p>Embeddings can leak sensitive source text. Reconstructive algorithms infer original values directly from dense vectors.</p><ul><li><p>Execute PII identification and redaction <strong>before</strong> generating vectors.</p></li><li><p>Encrypt vector stores at rest.</p></li><li><p>Restrict direct vector index reads to authorized roles.</p></li></ul><h4>Native Pre-Filtering for Multi-Tenancy</h4><p>Do not rely on post-filtering for multi-tenant security. Post-filtering creates performance bottlenecks and hazards cross-tenant leaks if memory logic breaks. Enforce tenant isolation directly inside the vector engine search pass.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;7dde6022-a321-4cc0-9e56-6d209f8bad88&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def search_scoped(query_vector, index, tenant_id: str, top_k: int = 5):
    """Enforce pre-filtering at the database layer."""
    return index.search(
        query_vector,
        k=top_k,
        filter={"tenant_id": tenant_id}
    )</code></pre></div><div><hr></div><h2>Model Versioning and Re-Embedding</h2><p>Embedding models do not share vector spaces. Comparing vectors from different models yields meaningless results, even when dimension counts match.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!lvGM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!lvGM!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png 424w, /__u/substackcdn.com/image/fetch/$s_!lvGM!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png 848w, /__u/substackcdn.com/image/fetch/$s_!lvGM!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png 1272w, /__u/substackcdn.com/image/fetch/$s_!lvGM!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!lvGM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png" width="1456" height="806" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e0b62359-9cde-4779-812b-ef060e867e59_1554x860.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:806,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:142875,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.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_!lvGM!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png 424w, /__u/substackcdn.com/image/fetch/$s_!lvGM!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png 848w, /__u/substackcdn.com/image/fetch/$s_!lvGM!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.png 1272w, /__u/substackcdn.com/image/fetch/$s_!lvGM!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0b62359-9cde-4779-812b-ef060e867e59_1554x860.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></p><p>Upgrading an embedding model requires a complete corpus re-index. Deploy upgrades using a blue-green strategy.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!YF_k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!YF_k!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png 424w, /__u/substackcdn.com/image/fetch/$s_!YF_k!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png 848w, /__u/substackcdn.com/image/fetch/$s_!YF_k!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YF_k!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!YF_k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png" width="1350" height="976" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:976,&quot;width&quot;:1350,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:144364,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.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_!YF_k!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png 424w, /__u/substackcdn.com/image/fetch/$s_!YF_k!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png 848w, /__u/substackcdn.com/image/fetch/$s_!YF_k!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YF_k!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9eb98157-d521-4a93-a270-25b7d8522d42_1350x976.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="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;855e6b30-715a-4957-b0da-8579680aa749&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def blue_green_reembed(old_index, chunks, new_embedder, new_index_name: str, eval_set: list[dict]):
    new_index = create_index(new_index_name)
    
    for batch in batch_chunks(chunks, size=100):
        records = [generate_record(c, new_embedder) for c in batch]
        new_index.upsert(records)
        
    metrics = evaluate_retrieval(new_index, eval_set)
    if metrics["recall_at_5"] &lt; 0.85:
        raise RuntimeError("Quality threshold unmet. Aborting cutover.")
        
    swap_production_alias(new_index_name)</code></pre></div><div><hr></div><h2>Evaluations for the Embedding Layer</h2><p>Do not rely solely on public benchmarks. Evaluate models using domain-specific dataset pairs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Ij7R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Ij7R!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ij7R!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ij7R!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ij7R!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Ij7R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png" width="1152" height="628" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7954e2fe-236a-4b35-9e44-817845468207_1152x628.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:628,&quot;width&quot;:1152,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87782,&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://avanichaskar.substack.com/i/210882156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.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_!Ij7R!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ij7R!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ij7R!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ij7R!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7954e2fe-236a-4b35-9e44-817845468207_1152x628.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="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;63c2eca2-b9a9-42bc-8779-0ad094ff49ad&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def evaluate_retrieval(index, eval_queries: list[dict], k: int = 5) -&gt; dict:
    """eval_queries: [{"query": str, "relevant_ids": set[str]}, ...]"""
    hits = 0
    total = 0
    for item in eval_queries:
        results = index.search(item["query"], k=k)
        retrieved_ids = {r.id for r in results}
        hits += len(retrieved_ids &amp; item["relevant_ids"])
        total += len(item["relevant_ids"])
    return {"recall_at_k": hits / total if total else 0.0}
</code></pre></div><div><hr></div><h2>Summary</h2><p>The embedding layer defines the core retrieval capabilities of a RAG pipeline. Choosing a model based purely on public leaderboards introduces severe production risks.</p><ul><li><p>Match context limits to chunking specs to avoid silent input truncation.</p></li><li><p>Apply instructional task prefixes for dual-encoder asymmetric retrieval.</p></li><li><p>Process multi-modal elements via async queues using captioning and direct vision vectors.</p></li><li><p>Enforce tenant pre-filtering, runtime credential injection, and pre-embedding PII redaction.</p></li><li><p>Treat model updates as full index migrations using blue-green deployment pipelines.</p><div><hr></div><p><em>Thanks for reading till the end. If you liked reading this article, do share it:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/system-design-for-rag-the-embedding?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/system-design-for-rag-the-embedding?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p><p><em>Leave a comment about your thoughts on the embedding layer: </em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/system-design-for-rag-the-embedding/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/system-design-for-rag-the-embedding/comments"><span>Leave a comment</span></a></p><p></p></li></ul>]]></content:encoded></item><item><title><![CDATA[The Anisotropy Problem in RAG]]></title><description><![CDATA[Why vector database swears it found a 90% match yet the retrieved document is still completely useless.]]></description><link>https://avanichaskar.substack.com/p/the-anisotropy-problem-in-rag</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/the-anisotropy-problem-in-rag</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Sun, 05 Jul 2026 16:58:36 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7eaed90f-0181-4289-9af2-177a19a7efaf_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to the anisotropy problem.</p><p>Picture cramming an entire library onto one tiny shelf. Every single book is touching. You couldn&#8217;t tell them apart by feel if you tried. That is exactly what happens inside a lot of embedding models. <span>Instead of spreading text vectors uniformly across a high-dimensional space, they squeeze everything into a narrow cone.</span></p><p>In this article we will deep dive into why this happens at an architectural level, how it breaks Retrieval-Augmented Generation (RAG), and the system design patterns required to fix it.</p><div><hr></div><h2>What is the Anisotropy Problem?</h2><p><span>Anisotropy in embedding representations means that high-dimensional vectors generated by neural models fail to occupy all directions of their ambient space uniformly.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!M31a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!M31a!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!M31a!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!M31a!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M31a!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!M31a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:524258,&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://avanichaskar.substack.com/i/205291265?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F354e55f6-cb10-4c66-8ad8-2832bb8cb0a5_1024x559.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_!M31a!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!M31a!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!M31a!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M31a!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b6253ae-0e32-4a03-863f-af4db0df8d47_1024x559.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>Why does this happen? <span>Research shows it is not just a training glitch or a side effect of token distribution; it is an inherent property of the Transformer architecture itself.</span></p><ul><li><p><strong><span>Self-Attention Amplification:</span></strong><span> The core self-attention mechanism in Transformers naturally amplifies any shared mean in their queries and keys.</span> Over multiple deep neural layers containing residual connections, this mathematical bias accumulates.</p></li><li><p><strong><span>The Narrow Cone:</span></strong><span> Instead of forming a perfectly spherical, isotropic distribution, the embeddings collapse into a tight, low-dimensional cone.</span></p></li></ul><div><hr></div><h2>Why This Destroys RAG Performance</h2><p>In a RAG system, your retrieval layer relies heavily on measuring the angle between vectors using cosine similarity.</p><ul><li><p><span>Because all vectors are squished into the same narrow cone, their angular distances from one another are minimal.</span></p></li><li><p>This causes almost all cosine similarity scores to cluster near the top (e.g., everything scores above 0.85).</p></li><li><p>Your retriever loses its mathematical precision. It can no longer distinguish between a &#8220;highly relevant document&#8221; and &#8220;vaguely related noise.&#8221;</p></li></ul><p>The result? Your pipeline hands the LLM the wrong context, and garbage context guarantees AI hallucinations.</p><div><hr></div><h2>System Design Solutions</h2><p>To fix poor retrieval caused by anisotropy, you must adjust your RAG architecture. Here are the three primary solutions, ordered by engineering effort:</p><h3>1. Upgrade the Embedding Model (Component Swap)</h3><p>The simplest architectural fix is to ditch older embeddings for modern models designed to natively enforce uniform vector distribution.</p><ul><li><p><strong><span>Best All-Rounder (2026):</span></strong><span> Google Gemini Embedding 2 currently leads benchmark testing across cross-lingual and long-document retrieval, achieving perfect key information retrieval scores up to 32,000 tokens.</span></p></li><li><p><strong><span>Best Hosted API:</span></strong><span> OpenAI&#8217;s </span><code>text-embedding-3-large</code><span> remains a highly reliable, cost-effective baseline for general-purpose English retrieval.</span></p></li><li><p><strong><span>Best Open-Source:</span></strong><span> BAAI&#8217;s BGE-M3 is the production workhorse for teams needing a self-hosted solution without per-token costs.</span></p></li></ul><h3>2. <span>Implement Vector Whitening (Data Transformation)</span></h3><p><span>If you are stuck with a legacy model, you can artificially isotropize the vector space using a post-processing Principal Component Analysis (PCA) Whitening transformation.</span></p><p><span>Whitening decorrelates the embedding dimensions and scales each axis by the inverse of its variance, geometrically transforming the clustered space into a uniform sphere.</span> The transformation is defined as:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;Z = W(X - \\mu)&quot;,&quot;id&quot;:&quot;KVGFCJMJJU&quot;}" data-component-name="LatexBlockToDOM"></div><p>Where:</p><ul><li><p><span>Z</span> is the transformed, whitened embedding vector.</p></li><li><p><span>X is the original input embedding vector.</span></p></li><li><p>&#956;<span> is the mean embedding vector used to center the distribution at the origin.</span></p></li><li><p>W <span>is the whitening matrix.</span></p></li></ul><p><span>To compute the whitening matrix </span>W<span>, you derive it from the covariance matrix &#931; of the centered data.</span> Using eigendecomposition, where <em>&#931; = U&#923;U&#7488;</em><span> </span>, the whitening matrix becomes:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;W = U\\Lambda^{-1/2}&quot;,&quot;id&quot;:&quot;FPOUSYUPDD&quot;}" data-component-name="LatexBlockToDOM"></div><p><span>This simple linear transformation ensures that the resulting covariance matrix of the new embeddings is the identity matrix I, effectively eliminating the dominant directional pull.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!bhVc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!bhVc!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!bhVc!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!bhVc!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!bhVc!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!bhVc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:776384,&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://avanichaskar.substack.com/i/205291265?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.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_!bhVc!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!bhVc!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!bhVc!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!bhVc!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c598cf4-ede6-44f1-823d-03c40095fa2f_1024x559.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>3. Add a Cross-Encoder Re-ranker (Two-Stage Retrieval)</h3><p>Never rely solely on a dense vector search. A highly robust RAG pipeline utilizes a two-stage retrieval architecture:</p><ol><li><p><strong>Initial Search:</strong> Let the fast vector database fetch the top 50 &#8220;clumped&#8221; results using standard embeddings.</p></li><li><p><strong><span>Re-ranking:</span></strong><span> Use a computationally heavier Cross-Encoder (like Cohere or BGE-reranker-v2) to accurately evaluate and re-score the top 5 results before passing them into the LLM&#8217;s context window.</span></p></li></ol><div><hr></div><h2>The Bottom Line</h2><p>If your RAG output feels random and inaccurate, stop tweaking your LLM prompts. Check if your embeddings actually have the space to be different from one another. Fix the anisotropy problem, and your retrieval precision will immediately improve.</p><div><hr></div><p><em>What has been your biggest hurdle with vector retrieval in production? Drop a comment below&#8212;I&#8217;d love to hear how you are tackling it. Subscribe to read more AI Engineering articles!!</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/the-anisotropy-problem-in-rag/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/the-anisotropy-problem-in-rag/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[System Design for RAG: The Chunking Layer]]></title><description><![CDATA[The precision-context trap: How to split documents without severing tables, headers, and semantic meaning.]]></description><link>https://avanichaskar.substack.com/p/system-design-for-rag-the-chunking</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-for-rag-the-chunking</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Sat, 27 Jun 2026 18:39:58 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/847b5565-dbea-4835-a5ba-a32360835072_1024x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your parser did its job. The PDF is now clean Markdown &#8212; headings preserved, tables intact, reading order correct. (If you missed it, <a href="/__u/avanichaskar.substack.com/p/system-design-for-rag-the-parsing">Part 2 covered the parsing layer</a> and why that step matters more than most teams realize.)</p><blockquote><p><strong>TL;DR</strong>:<br>&#8226; <strong>The Precision-Context Tradeoff</strong>: Small chunks embed precisely but lack context (meaning is lost); large chunks preserve context but dilute semantic similarity with noise.<br>&#8226; <strong>The Production Pattern</strong>: Use <strong>Parent-Child (Hierarchical) Chunking</strong>&#8212;index small &#8220;child&#8221; chunks (~200 tokens) for vector matching, but fetch and send their larger &#8220;parent&#8221; chunks (~1000&#8211;2000 tokens) to the LLM.<br>&#8226; <strong>Pragmatic Default</strong>: If parent-child is too heavy, use <strong>Recursive Chunking</strong> (falling back from headings to paragraphs, lines, and words) rather than fixed-size splitting.<br>&#8226; <strong>Context Limits</strong>: Ensure your maximum chunk size is strictly smaller than your embedding model&#8217;s context window to prevent silent truncation.</p></blockquote><p>But here&#8217;s the thing: your LLM can&#8217;t see the whole document. A 40-page technical manual is ~20,000 tokens. Your embedding model&#8217;s context window is 512. Your retriever returns the top 5 chunks. So everything downstream &#8212; the embedding quality, the retrieval precision, the final answer &#8212; depends on how you <em>split</em> that document.</p><p>And splitting is where things go quietly wrong. Consider a chunk boundary that lands here:</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/fqvwb/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/088f8a64-e03b-4fb4-96ab-df71e8f306ad_1220x430.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8cd024d0-3a0d-4378-b679-72733b75cc9c_1220x500.png&quot;,&quot;height&quot;:243,&quot;title&quot;:&quot;Thermal Limitations&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:false}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/fqvwb/1/" width="730" height="243" frameborder="0" scrolling="no"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><p>Chunk 1 has a heading and half a table. Chunk 2 has a dangling row with no heading and no column headers. Someone asks <em>&#8220;what&#8217;s the minimum operating temperature for Sensor B?&#8221;</em> &#8212; the retriever finds chunk 2, which contains <code>-55&#176;C</code> but also <code>-40&#176;C</code> from a nearby sentence in a different chunk. The model guesses. Your user loses trust.</p><p>This isn&#8217;t hypothetical. This is what happens when chunking is treated as a hyperparameter rather than a system design decision.</p><div><hr></div><p>The temptation is to pick a number &#8212; 512 tokens, 1000 characters &#8212; and move on. But chunk size sits at the intersection of three competing concerns, and the right answer depends on your documents, your queries, and your retrieval architecture.</p><p><strong>Too small</strong> and you lose context. A 100-token chunk might capture <em>&#8220;The maximum allowable torque is 47 N&#183;m under sustained load&#8221;</em> but not which system it belongs to. The embedding is precise. The answer is useless.</p><p><strong>Too large</strong> and noise dilutes the signal. A 2,000-token chunk about an entire &#8220;Safety Considerations&#8221; section will match queries about fire suppression, electrical grounding, <em>and</em> PPE requirements. Your embedding becomes a blurry average of all three topics. Retrieval recall looks fine. Precision tanks.</p><p>Here&#8217;s what this looks like concretely. Take a 500-word section about API rate limits:</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/knJPz/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1f2aa8cf-c621-4d69-aa64-fd0071ad5089_1220x696.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87d95620-d44a-49f8-a7e7-3601f4a2e356_1220x696.png&quot;,&quot;height&quot;:344,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/knJPz/1/" width="730" height="344" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><p>There&#8217;s no universal optimum. But there <em>is</em> a design framework: match chunk granularity to query granularity. If users ask specific factual questions (&#8221;what&#8217;s the rate limit for the /users endpoint?&#8221;), you need small, precise chunks. If users ask broad conceptual questions (&#8221;explain the authentication flow&#8221;), you need larger context windows.</p><blockquote><p>&#128161; <strong>Key Insight</strong>: Match your chunk granularity to your query granularity. Specific factual queries (&#8221;what is the operating temperature of X?&#8221;) require small, precise chunks. Broad conceptual queries (&#8221;how does the cooling loop work?&#8221;) require large chunks or hierarchical parent lookup.</p></blockquote><p>The rest of this article walks through five chunking strategies &#8212; from simple to sophisticated &#8212; and when each one earns its complexity.</p><div><hr></div><h2>Fixed-size chunking: The baseline</h2><p>The simplest approach: split every N tokens, with some overlap to avoid cutting sentences in half.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;621d38f5-ae76-4f1f-ae65-a501241989ec&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import tiktoken

def fixed_size_chunk(text: str, chunk_size: int = 256, 
                     overlap: int = 50, model: str = "cl100k_base") -&gt; list[dict]:
    """Split text into fixed-size token chunks with overlap."""
    try:
        enc = tiktoken.encoding_for_model(model)
    except KeyError:
        enc = tiktoken.get_encoding(model)
        
    if overlap &gt;= chunk_size:
        raise ValueError("Overlap must be strictly smaller than chunk_size")
        
    tokens = enc.encode(text)
    chunks = []
    start = 0
    
    while start &lt; len(tokens):
        end = min(start + chunk_size, len(tokens))
        chunk_tokens = tokens[start:end]
        chunks.append({
            "text": enc.decode(chunk_tokens),
            "token_count": len(chunk_tokens),
            "start_token": start,
        })
        start += chunk_size - overlap  # slide the window
    
    return chunks</code></pre></div><p><strong>Why overlap matters.</strong> Without overlap, a sentence that straddles two chunks gets split. The first chunk has half a thought; the second has the other half. Neither embeds well. A 10&#8211;20% overlap (50 tokens on a 256-token chunk) means straddled sentences appear in both chunks. The redundancy is cheap; the retrieval improvement is real.</p><p><strong>When fixed-size works:</strong></p><ul><li><p>Homogeneous, flowing prose &#8212; blog posts, news articles, narrative text</p></li><li><p>Documents without meaningful structural markers</p></li><li><p>Quick prototyping where you need a baseline fast</p></li></ul><p><strong>When it fails:</strong></p><ul><li><p>Structured documents with headings, tables, code blocks</p></li><li><p>Multi-topic sections where a chunk boundary lands mid-transition</p></li><li><p>Any document where the parser gave you clean Markdown with <code>##</code> headings &#8212; you&#8217;re ignoring free structural signal</p></li></ul><p>Fixed-size chunking is the <code>SELECT *</code> of chunking strategies. It works everywhere. It&#8217;s optimal nowhere.</p><div><hr></div><h2>Heading-based (structure-aware) chunking</h2><p>If your parser outputs Markdown with real headings &#8212; and after <a href="/__u/avanichaskar.substack.com/p/system-design-for-rag-the-parsing">Part 1</a>, it should &#8212; you can split at structural boundaries instead of arbitrary token counts.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;2b8ad6c2-0a1c-415d-a3f3-272f193d18d5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import re
import tiktoken

def heading_chunk(markdown: str, max_tokens: int = 1024,
                  model: str = "cl100k_base") -&gt; list[dict]:
    """Split Markdown at ## heading boundaries."""
    try:
        enc = tiktoken.encoding_for_model(model)
    except KeyError:
        enc = tiktoken.get_encoding(model)
    
    # Split on ## headings, keeping the heading with its content
    sections = re.split(r'(?=^## )', markdown, flags=re.MULTILINE)
    sections = [s.strip() for s in sections if s.strip()]
    
    chunks = []
    for section in sections:
        tokens = enc.encode(section)
        
        if len(tokens) &lt;= max_tokens:
            heading = section.split('\n')[0] if section.startswith('##') else ""
            chunks.append({
                "text": section,
                "token_count": len(tokens),
                "heading": heading.strip('# ').strip(),
            })
        else:
            # Section too large &#8212; fall back to paragraph splitting
            paragraphs = section.split('\n\n')
            current = ""
            heading = paragraphs[0] if paragraphs[0].startswith('##') else ""
            
            for para in paragraphs:
                candidate = current + "\n\n" + para if current else para
                if len(enc.encode(candidate)) &lt;= max_tokens:
                    current = candidate
                else:
                    if current:
                        chunks.append({
                            "text": current,
                            "token_count": len(enc.encode(current)),
                            "heading": heading.strip('# ').strip(),
                        })
                    current = para
            
            if current:
                chunks.append({
                    "text": current,
                    "token_count": len(enc.encode(current)),
                    "heading": heading.strip('# ').strip(),
                })
    
    return chunks</code></pre></div><p>The critical dependency here: <strong>your parser must actually emit headings</strong>. If you&#8217;re using a heuristic parser on a scanned PDF and it flattens everything to plain text, heading-based chunking has nothing to split on. The chunker&#8217;s quality is bounded by the parser&#8217;s quality &#8212; which is exactly why we started this series with parsing.</p><p>The oversized-section edge case matters more than you&#8217;d think. A 3,000-token &#8220;API Reference&#8221; section with 15 endpoints can&#8217;t stay as one chunk. The fallback above splits at paragraph boundaries within the section, preserving the heading as metadata on each sub-chunk. This keeps the heading-content relationship intact even when the section is too large.</p><p><strong>When heading-based works:</strong></p><ul><li><p>Technical documentation with clear hierarchy (API docs, product manuals, wikis)</p></li><li><p>Any content authored in Markdown, Notion, Confluence, or Google Docs</p></li><li><p>Documents where section boundaries align with topic boundaries</p></li></ul><p><strong>When it fails:</strong></p><ul><li><p>Documents without structural markup (plain text, OCR output)</p></li><li><p>Long-form narrative where headings are sparse or absent</p></li><li><p>Content where headings don&#8217;t reflect topic shifts (decorative headings)</p></li></ul><div><hr></div><h2>Recursive chunking: The pragmatic default</h2><p>LangChain&#8217;s <code>RecursiveCharacterTextSplitter</code> popularized this approach, and for good reason &#8212; it&#8217;s a sensible hierarchy of fallbacks.</p><p>The idea: try splitting by the most meaningful separator first. If the resulting chunks are still too large, recurse with a less meaningful separator.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;be1eb34b-a733-4934-8e8a-c4f4099bd8e1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">Separator hierarchy:
  1. "\n\n"  (paragraph breaks)
  2. "\n"    (line breaks)
  3. " "     (word boundaries)
  4. ""      (characters &#8212; last resort)</code></pre></div><p>For Markdown content, you&#8217;d prepend structural separators:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;a7bf6fc1-2a28-4583-ada6-61b98d3fbb18&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">Extended hierarchy:
  1. "\n## "  (H2 headings)
  2. "\n### " (H3 headings)
  3. "\n\n"   (paragraphs)
  4. "\n"     (lines)
  5. " "      (words)
  6. ""       (characters)</code></pre></div><p>The algorithm tries level 1 first. If any resulting chunk exceeds <code>max_tokens</code>, it re-splits that chunk using level 2, and so on. The result: chunks that respect document structure when structure exists, and degrade gracefully to simpler boundaries when it doesn&#8217;t.</p><blockquote><p>This is the approach I&#8217;d recommend as a starting point for most teams. It handles mixed-format corpora without custom logic, and the failure mode (slightly suboptimal boundaries) is far less damaging than the failure mode of fixed-size splitting (severed tables, orphaned headings).</p></blockquote><p>LangChain&#8217;s implementation handles this out of the box:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;eee90361-62e5-4670-9317-69483ec211f8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=50,
    separators=["\n## ", "\n### ", "\n\n", "\n", " ", ""],
)

chunks = splitter.split_text(markdown_content)</code></pre></div><p>The key design choice is the separator list. The default <code>["\n\n", "\n", " ", ""]</code> works for generic text. For Markdown output from a parser, always add heading separators at the top.</p><div><hr></div><h2>Hierarchical (parent-child) chunking: The production pattern</h2><p>Here&#8217;s the fundamental tension in retrieval: <strong>small chunks embed precisely but lack context. Large chunks provide context but embed poorly.</strong> Every strategy above forces you to pick a point on that tradeoff curve.</p><p>Parent-child chunking resolves it by refusing to pick. You create two layers:</p><p><strong>Child chunks</strong> (100&#8211;300 tokens) are embedded and stored in the vector index. They&#8217;re small enough to match specific queries precisely.</p><p><strong>Parent chunks</strong> (1,000&#8211;2,000 tokens) are stored in a key-value store, mapped to their children by ID. When a child chunk matches, you fetch the parent and send <em>that</em> to the LLM. The model gets the full surrounding context &#8212; the heading, the preceding paragraph, the rest of the table.</p><p>Here&#8217;s a minimal implementation:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;ca3680b4-c4d8-4a10-977d-a78bbc20a5ff&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import uuid
import tiktoken
from langchain_text_splitters import RecursiveCharacterTextSplitter

def hierarchical_chunk(markdown: str, parent_size: int = 1024,
                       child_size: int = 200, child_overlap: int = 50,
                       model: str = "cl100k_base") -&gt; dict:
    """Create parent-child chunk hierarchy enforcing size constraints on parents."""
    try:
        enc = tiktoken.encoding_for_model(model)
    except KeyError:
        enc = tiktoken.get_encoding(model)
        
    if child_overlap &gt;= child_size:
        raise ValueError("Child overlap must be strictly smaller than child_size")
        
    # Step 1: Split on ## headings first
    parent_sections = re.split(r'(?=^## )', markdown, flags=re.MULTILINE)
    parent_sections = [s.strip() for s in parent_sections if s.strip()]
    
    # Sub-splitter to partition oversized parents
    parent_splitter = RecursiveCharacterTextSplitter(
        chunk_size=parent_size,
        chunk_overlap=100,
        separators=["\n### ", "\n\n", "\n", " ", ""],
    )
    
    parents = {}
    children = []
    
    for section in parent_sections:
        heading = section.split('\n')[0] if section.startswith('##') else ""
        
        section_tokens = len(enc.encode(section))
        if section_tokens &gt; parent_size:
            sub_parents = parent_splitter.split_text(section)
        else:
            sub_parents = [section]
            
        for sub_parent in sub_parents:
            parent_id = str(uuid.uuid4())
            parents[parent_id] = {
                "text": sub_parent,
                "heading": heading.strip('# ').strip(),
                "token_count": len(enc.encode(sub_parent)),
            }
            
            # Step 2: Split this specific parent into children
            tokens = enc.encode(sub_parent)
            start = 0
            while start &lt; len(tokens):
                end = min(start + child_size, len(tokens))
                child_text = enc.decode(tokens[start:end])
                
                children.append({
                    "id": str(uuid.uuid4()),
                    "parent_id": parent_id,
                    "text": child_text,
                    "heading": heading.strip('# ').strip(),
                    "token_count": end - start,
                })
                start += child_size - child_overlap
                
    return {"parents": parents, "children": children}</code></pre></div><p>The storage model matters. Parents go into a key-value store &#8212; Redis, PostgreSQL, even S3 &#8212; anything that supports fast lookups by ID. Children go into the vector index. The child records carry a <code>parent_id</code> foreign key. At query time: vector search &#8594; get child &#8594; look up parent &#8594; send parent to LLM.</p><blockquote><p>This is the pattern most production RAG systems converge on. It&#8217;s more complex to implement and operate &#8212; two storage layers, a join at query time &#8212; but it eliminates the precision-vs-context tradeoff that plagues every other approach.</p><p>&#128161; <strong>Key Insight</strong>: In parent-child chunking, storing parents in a key-value store adds negligible query latency (~2&#8211;5ms lookup) and minimal storage overhead, but completely breaks the precision-vs-context tradeoff that plagues standard RAG.</p></blockquote><p><strong>Storage overhead is modest.</strong> Parents are typically 3&#8211;5x the combined size of their children (due to overlap in child chunks). For a 10M-token corpus, expect ~40MB for parent storage and ~12MB for child embeddings (at 1536 dimensions, float32). The join adds ~2&#8211;5ms at query time &#8212; negligible compared to the vector search itself.</p><div><hr></div><h2>Semantic chunking: When structure isn&#8217;t enough</h2><p>Some documents don&#8217;t have headings. Long-form essays, transcripts, meeting notes, research papers with dense prose &#8212; the structure is in the <em>meaning</em>, not the formatting.</p><p>Semantic chunking detects topic boundaries by measuring embedding similarity between consecutive sentences. When similarity drops below a threshold, that&#8217;s a chunk boundary.</p><p><strong>The algorithm:</strong></p><ol><li><p>Split text into sentences</p></li><li><p>Embed each sentence</p></li><li><p>Compute cosine similarity between adjacent sentence embeddings</p></li><li><p>Where similarity drops below a threshold (typically 0.75&#8211;0.85), insert a chunk boundary</p></li><li><p>Group the sentences between boundaries into chunks</p></li></ol><p>LangChain&#8217;s <code>SemanticChunker</code> implements this with three breakpoint methods:</p><ul><li><p><strong>Percentile</strong>: split at the bottom N% of similarity scores</p></li><li><p><strong>Standard deviation</strong>: split where similarity drops below <code>mean - k * std</code></p></li><li><p><strong>Interquartile</strong>: split at outlier low-similarity points</p></li></ul><p>The tradeoff is cost. Semantic chunking requires an embedding call for <em>every sentence</em> during ingestion. For a 10,000-sentence document, that&#8217;s 10,000 embedding calls just for chunking &#8212; before you&#8217;ve even embedded the resulting chunks for your vector index.</p><p><strong>Expect a 3&#8211;5x increase in preprocessing latency</strong> compared to recursive chunking. This is an ingestion-time cost, not a query-time cost, but it matters at scale. If you&#8217;re processing millions of documents, semantic chunking on every one may be prohibitively expensive.</p><p><strong>When semantic chunking earns its cost:</strong></p><ul><li><p>Long-form unstructured text (transcripts, essays, reports)</p></li><li><p>Documents where topic shifts are subtle and don&#8217;t align with formatting</p></li><li><p>High-value corpora where retrieval precision justifies the compute spend</p></li></ul><p><strong>When it&#8217;s overkill:</strong></p><ul><li><p>Documents with clear structural markup &#8212; heading-based splitting is cheaper and often more reliable</p></li><li><p>Large-scale ingestion pipelines where the 3&#8211;5x cost increase isn&#8217;t justified</p></li><li><p>Technical documents where topic boundaries align with formatting (code docs, API references)</p></li></ul><div><hr></div><h2>Metadata: The part everyone forgets</h2><p>A chunk without metadata is a chunk you can&#8217;t filter, can&#8217;t cite, and can&#8217;t debug. Every chunk should carry at minimum:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;1b4b5009-47eb-4351-a92b-9cd07d200950&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{
    "id": "chunk-a1b2c3",
    "text": "The maximum retry count is 3, with exponential backoff...",
    "token_count": 187,
    "source_file": "api-reference-v2.4.pdf",
    "heading": "Error Handling",
    "page_number": 23,
    "parent_id": "parent-x7y8z9",       # if using hierarchical chunking
    "tenant_id": "acme-corp",           # if multi-tenant
    "doc_version": "2.4.1",             # for versioned content
    "created_at": "2026-06-15T10:30:00Z"
}</code></pre></div><p><strong>Why this matters downstream:</strong></p><ul><li><p><strong>Source citation</strong>: The LLM can tell the user <em>where</em> the answer came from (&#8221;See api-reference-v2.4.pdf, page 23, section Error Handling&#8221;)</p></li><li><p><strong>Filtered retrieval</strong>: In a multi-tenant system, you filter by <code>tenant_id</code> <em>before</em> vector search. Without it, Tenant A&#8217;s queries might surface Tenant B&#8217;s documents.</p></li><li><p><strong>Version management</strong>: When a document is updated, you can delete all chunks with the old <code>doc_version</code> and re-ingest. Without version metadata, you&#8217;re doing string matching to find stale chunks.</p></li><li><p><strong>Debugging</strong>: When the answer is wrong, metadata lets you trace back: which chunk was retrieved &#8594; from which section &#8594; from which file. Without it, you&#8217;re reading vector IDs.</p></li></ul><p>The heading field deserves special attention. If your chunker preserves the heading hierarchy (which heading-based and hierarchical chunkers do), you can prepend the heading to the chunk text before embedding. A chunk that embeds as <em>&#8220;Error Handling: The maximum retry count is 3...&#8221;</em> retrieves better than one that embeds as just <em>&#8220;The maximum retry count is 3...&#8221;</em> &#8212; because the heading provides semantic framing that the embedding model can leverage.</p><div><hr></div><h2>Picking the right strategy: A decision table</h2><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/73Lh6/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7665e6d8-c31f-40f6-b81f-e36dea652c0f_1220x976.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e22cacf-863b-407b-9482-ff03f225a152_1220x976.png&quot;,&quot;height&quot;:487,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/73Lh6/1/" width="730" height="487" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><p>A few rules of thumb:</p><ul><li><p><strong>Start with recursive chunking.</strong> It&#8217;s the pragmatic default that handles 80% of cases well enough.</p></li><li><p><strong>Move to heading-based</strong> once your parser reliably emits Markdown headings.</p></li><li><p><strong>Add the hierarchical layer</strong> when you need to serve precise answers from large documents &#8212; this is the production pattern.</p></li><li><p><strong>Use semantic chunking selectively</strong> for high-value unstructured content, not as a default.</p></li><li><p><strong>Always attach metadata.</strong> This isn&#8217;t optional. It&#8217;s the difference between a demo and a system.</p></li></ul><div><hr></div><h2>What&#8217;s next</h2><p>The chunker&#8217;s output is a stream of text segments with metadata. But text isn&#8217;t what the vector index stores &#8212; it stores numbers. The next layer, the <strong>embedder</strong>, converts each chunk into a dense vector that captures its semantic meaning.</p><p>In <a href="/__u/avanichaskar.substack.com/">Part </a>4, we&#8217;ll cover embedding models &#8212; why dimension count matters, the tradeoff between open-source and API-based models, how to handle multi-lingual content, and the latency/cost arithmetic of embedding at scale.</p><p><em>This is Part 3 of the <strong>System Design for RAG</strong> series. <a href="/__u/avanichaskar.substack.com/p/system-design-for-rag-the-parsing">Part 2 on The Parsing Layer</a> is live now.</em></p><div><hr></div><p><strong>If you&#8217;re building RAG in production and these tradeoffs resonate, subscribe &#8212; each article in this series goes deep on one layer of the pipeline, with code you can actually use.</strong></p>]]></content:encoded></item><item><title><![CDATA[What is an AI Agent Skill?]]></title><description><![CDATA[A technical, no-fluff breakdown of the Agent Skills framework, how it beats fine-tuning, and what it means for your stack.]]></description><link>https://avanichaskar.substack.com/p/the-end-of-the-mega-prompt-agent</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/the-end-of-the-mega-prompt-agent</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Sat, 20 Jun 2026 10:20:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!RRFx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your AI agent is smart. It just doesn&#8217;t know how <em>your</em> team does things.</p><p>It doesn&#8217;t know which checklist your reviewers run before approving a PR. It doesn&#8217;t know the seventeen edge cases your compliance team checks before a PDF goes out the door. </p><p>That&#8217;s not a reasoning gap - it&#8217;s a knowledge gap. And until recently, there were exactly two options for closing it -</p><p><strong>Stuff it in the system prompt:</strong> Every additional instruction is permanent context. A prompt carrying 20K tokens of niche procedure pays that cost on every single request - higher latency, wasted tokens, and a well-documented failure mode where models attend less reliably to instructions buried in the middle of a long context window.</p><p><strong>Fine-tune:</strong> Bakes the behavior into weights. Works, but it&#8217;s slow to iterate, expensive to run for narrow procedural knowledge, and the wrong tool for &#8220;tax goes on its own line item.&#8221;</p><div><hr></div><p><em><span data-color="#0b5394" style="color: rgb(11, 83, 148);">This article is in collaboration with </span><a href="https://x.com/asmah2107"><span data-color="#0b5394" style="color: rgb(11, 83, 148);">Ashutosh</span></a><span data-color="#0b5394" style="color: rgb(11, 83, 148);"> - He&#8217;s a software engineer at YouTube, ex-Google Search, and Microsoft Azure. Connect with him if you want to get deep dives into system design, AI, and software engineering.</span></em></p><div class="embedded-publication-wrap" data-attrs="{&quot;id&quot;:1560201,&quot;embedding_publication_id&quot;:null,&quot;name&quot;:&quot;Ashutosh&#8217;s Newsletter&quot;,&quot;logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!e0B2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7dcb403-4679-43aa-8bae-ad1023e77e4d_600x600.png&quot;,&quot;base_url&quot;:&quot;https://ashutoshmaheshwari.substack.com&quot;,&quot;hero_text&quot;:&quot;A newsletter to help everyone understand software engineering and computer science concepts in depth.&quot;,&quot;author_name&quot;:&quot;Ashutosh Maheshwari&quot;,&quot;show_subscribe&quot;:true,&quot;logo_bg_color&quot;:&quot;#eef2ff&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="EmbeddedPublicationToDOMWithSubscribe"><div class="embedded-publication show-subscribe"><a class="embedded-publication-link-part" native="true" href="/__u/ashutoshmaheshwari.substack.com/?utm_source=substack&amp;utm_campaign=publication_embed&amp;utm_medium=web"><img class="embedded-publication-logo" src="/__u/substackcdn.com/image/fetch/$s_!e0B2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7dcb403-4679-43aa-8bae-ad1023e77e4d_600x600.png" width="56" height="56" style="background-color: rgb(238, 242, 255);"><span class="embedded-publication-name">Ashutosh&#8217;s Newsletter</span><div class="embedded-publication-hero-text">A newsletter to help everyone understand software engineering and computer science concepts in depth.</div><div class="embedded-publication-author-name">By Ashutosh Maheshwari</div></a><form class="embedded-publication-subscribe" method="GET" action="/__u/ashutoshmaheshwari.substack.com/subscribe"><input type="hidden" name="source" value="publication-embed"><input type="hidden" name="autoSubmit" value="true"><input type="email" class="email-input" name="email" placeholder="Type your email..."><input type="submit" class="button primary" value="Subscribe"></form></div></div><div><hr></div><h2>Agent Skills is the alternative</h2><p>Package procedural knowledge as a folder of files an agent loads conditionally, rather than baking it into the prompt or the weights. </p><p>Anthropic shipped it in October 2025 and open-sourced the format the following month under what&#8217;s now governed at <a href="http://agentskills.io">agentskills.io</a>. </p><p>As of this writing, 26+ platforms implement it - Claude, Claude Code, OpenAI Codex, Gemini CLI, GitHub Copilot, Cursor, VS Code, and others.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!RRFx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!RRFx!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!RRFx!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!RRFx!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RRFx!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!RRFx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:745949,&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://avanichaskar.substack.com/i/202819675?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.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_!RRFx!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!RRFx!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!RRFx!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RRFx!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bca44c0-e05d-473e-adfb-47fa46dfea9a_1024x559.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>The file format</h2><p>A skill is a directory. The only required file is <code>SKILL.md</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;98a3870e-7e2e-4afd-9912-661ac8ce0f2d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">my-skill/
&#9500;&#9472;&#9472; SKILL.md          # required: frontmatter + instructions
&#9500;&#9472;&#9472; scripts/           # optional: executable code
&#9500;&#9472;&#9472; references/         # optional: docs loaded on demand
&#9500;&#9472;&#9472; assets/            # optional: templates, files the agent reads or copies
</code></pre></div><p><code>SKILL.md</code> is YAML frontmatter followed by a markdown body. The spec defines two required fields and several optional ones:</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/Kd8RW/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e72d1a0-c2db-490f-9d85-f32678e4e9ed_1220x844.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7d643844-6d83-46c4-bdc0-a3d70fe27721_1220x844.png&quot;,&quot;height&quot;:419,&quot;title&quot;:&quot;Created with Datawrapper&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/Kd8RW/1/" width="730" height="419" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><p><span data-color="rgb(31, 35, 40)" style="color: rgb(31, 35, 40);">For example, Claude Code layers on custom extensions like </span><code>disable-model-invocation</code><span data-color="rgb(31, 35, 40)" style="color: rgb(31, 35, 40);"> (useful for safe deployments) or </span><code>context: fork</code><span data-color="rgb(31, 35, 40)" style="color: rgb(31, 35, 40);"> (to run a skill in an isolated subagent). A basic skill works unmodified across all platforms; platform-specific extensions only work where supported.</span></p><p>Minimal example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;452e6866-6ee4-4d04-aa03-650ef1303f2e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">---
name: invoice-formatter
description: Formats outgoing invoices to match company template. Use when generating or editing invoice documents.
---

# Invoice Formatter

1. Use the template in assets/invoice_template.xlsx
2. Line items go in columns B&#8211;E
3. Tax is a separate row, never folded into line items
</code></pre></div><p>That&#8217;s the entire artifact. No training run, no eval harness required to ship it - though you&#8217;ll want one if you&#8217;re maintaining it at scale.</p><div><hr></div><h2>Progressive disclosure: the actual mechanism</h2><p>The spec defines three loading tiers, each with a rough token budget:</p><ol><li><p><strong>Metadata (~100 tokens/skill).</strong> At session start, the agent loads only <code>name</code> and <code>description</code> for every available skill. This is what makes the system scale &#8212; fifty skills costs a few thousand tokens, not fifty system prompts&#8217; worth of context.</p></li><li><p><strong>Instructions (&lt;5,000 tokens recommended).</strong> When a request matches a skill&#8217;s description, the full <code>SKILL.md</code> body loads into context. This is the only point where the agent commits real context budget to the skill.</p></li><li><p><strong>Resources (loaded as needed).</strong> Bundled scripts, reference docs, and assets load only if the instructions direct the agent to use them &#8212; often via a script execution rather than a context read, which keeps even large bundled resources off the token bill entirely.</p></li></ol><p>The practical consequence: skill <em>count</em> scales cheaply, skill <em>invocation</em> doesn&#8217;t. You can register hundreds of skills without bloating every request, but each activated skill still has to fit its instructions inside a few thousand tokens, which forces concision in a way the unstructured system-prompt approach never did.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!vFq6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!vFq6!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png 424w, /__u/substackcdn.com/image/fetch/$s_!vFq6!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png 848w, /__u/substackcdn.com/image/fetch/$s_!vFq6!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png 1272w, /__u/substackcdn.com/image/fetch/$s_!vFq6!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!vFq6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png" width="1024" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f26dc968-fe35-4c14-b162-888837a903e0_1024x512.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:512,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:985671,&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://avanichaskar.substack.com/i/202819675?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a3156a-e340-466d-9694-8ccbb60340a6_1024x559.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_!vFq6!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png 424w, /__u/substackcdn.com/image/fetch/$s_!vFq6!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png 848w, /__u/substackcdn.com/image/fetch/$s_!vFq6!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.png 1272w, /__u/substackcdn.com/image/fetch/$s_!vFq6!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff26dc968-fe35-4c14-b162-888837a903e0_1024x512.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>Why the description field is the real interface</h2><p>Discovery relies entirely on description matching. There is no separate routing model. The description has to do two things at once: state what the skill does, and anticipate the language a user would generate when the task is relevant.</p><p>Writing this is like writing a search index entry. Too narrow, and the skill never activates. Too broad, and it misfires on irrelevant tasks.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;d5ac02bd-9a12-4127-87cd-e4f2904cd223&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown"># under-specified &#8212; won't reliably fire
description: Handles spreadsheets.

# specified for activation
description: Use when creating, editing, or analyzing Excel files (.xlsx) &#8212;
  formulas, pivot tables, financial models. Triggers on mentions of
  spreadsheets, workbooks, or .xlsx.
</code></pre></div><div><hr></div><h2>Agent Skills in Production</h2><ul><li><p><strong><a href="https://github.com/anthropics/skills"><span>anthropics/skills</span></a></strong><span> : the reference implementation.</span></p><ul><li><p><span>Four skills (</span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">docx</span><span>, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">pdf</span><span>, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">pptx</span><span>, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">xlsx</span><span>) are the actual source-available code behind Claude.ai&#8217;s document creation feature, not a demo of it.</span></p></li><li><p><span>The rest cover creative/design (</span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">algorithmic-art</span><span>, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">canvas-design</span><span>, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">theme-factory</span><span>), dev tooling (</span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">frontend-design</span><span>, MCP scaffolding, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">skill-creator</span><span>), and enterprise workflows (</span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">brand-guidelines</span><span>, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">internal-comms</span><span>, </span><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">doc-coauthoring</span><span>).</span></p></li></ul></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;73e9a94a-22a8-4657-8368-e00c06a63d7e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">/plugin marketplace add anthropics/skills
/plugin install document-skills@anthropic-agent-skills</code></pre></div><ul><li><p><strong><a href="https://github.com/obra/superpowers"><span>obra/superpowers</span></a></strong><span> &#8212; skills as enforced policy, not optional context. Its own instructions state it plainly: if a skill applies, the agent has no discretion to skip it.</span></p><ul><li><p><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">test-driven-development</span><span> &#8212; enforces red-green-refactor before any implementation.</span></p></li><li><p><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">systematic-debugging</span><span> &#8212; four-phase root-cause process instead of guess-and-check fixes.</span></p></li><li><p><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">verification-before-completion</span><span> &#8212; blocks marking a task done without proof it&#8217;s done.</span></p></li><li><p><span data-color="rgb(24, 128, 56)" style="color: rgb(24, 128, 56);">subagent-driven-development</span><span> &#8212; splits implementation and review across separate subagents, composing with Claude Code&#8217;s existing subagent tooling rather than replacing it.</span></p></li></ul></li><li><p><strong><a href="https://github.com/travisvn/awesome-claude-skills"><span>travisvn/awesome-claude-skills</span></a></strong><span> &#8212; a maintained index for surveying the ecosystem.</span></p></li></ul><div><hr></div><h2>Skills vs. MCP vs. fine-tuning - where each actually sits</h2><blockquote><p>These concepts get conflated constantly. Here is how we can separate them in our mental models:</p></blockquote><ul><li><p><strong>MCP (Model Context Protocol) / Function Calling = Capability.</strong> This gives the agent hands to act on the world (call APIs, query databases). Without a tool, there&#8217;s no action.</p></li><li><p><strong>Agent Skills = Procedure.</strong> Skills tell the agent <em>how</em> to use its capability. In what order, under what constraints, and with what conventions.</p></li><li><p><strong>Fine-Tuning = Weights.</strong> This is the model itself. It&#8217;s slow to produce and expensive to iterate. Skills, on the other hand, are plain files: diffable, reviewable in a PR, and instantly revertible.</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_!vJhe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!vJhe!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!vJhe!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!vJhe!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!vJhe!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!vJhe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:602400,&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://avanichaskar.substack.com/i/202819675?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.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_!vJhe!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!vJhe!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!vJhe!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!vJhe!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F486aa142-3a6b-4195-a282-f6e0ff887248_1024x572.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>Threat model for Agent Skills</h2><p><span>Skills can ship arbitrary scripts. We need to treat them like production dependencies, not just saved prompts, because they carry the exact same supply-chain risks.</span></p><p>To secure our AI stack, we should follow these rules:</p><ul><li><p><strong><span>Vet the source:</span></strong><span> Actively maintained repositories with real issue and PR traffic carry far less risk than abandoned gists.</span></p></li><li><p><strong><span>Enforce boundaries:</span></strong><span> </span><code>allowed-tools</code><span> is a pre-approval list, not a sandbox. If we want true restrictions, we must pair it with explicit platform-level deny rules.</span></p></li><li><p><strong><span>Audit the code:</span></strong><span> The description is just marketing copy. We must read the bundled scripts before installing anything.</span></p></li><li><p><strong><span>Monitor external calls:</span></strong><span> We must treat any skill that pings an external URL or API exactly like a new third-party dependency.</span></p></li></ul><div><hr></div><h2>Evaluating agent skills</h2><p>Writing a <code>SKILL.md</code> is the easy. Proving it works is the hard part.</p><p>Anthropic&#8217;s own <code>skill-creator</code> skill now ships with a built-in eval mode that handles this directly:</p><ul><li><p><strong>Isolated, parallel test runs.</strong> It launches multiple sub-agents at once, each in its own clean context, so one test&#8217;s output can&#8217;t bleed into another&#8217;s grading.</p></li><li><p><strong>Specific grading, not just pass/fail.</strong> Each sub-agent runs a defined test prompt through the skill and scores the output against your stated success criteria - example &#8220;missed the hardcoded API key on line 42,&#8221; not just a red X.</p></li><li><p><strong>A concrete fix loop.</strong> It proposes instruction edits to close gaps, allowing developers to quickly bump pass rates from 60% to 90%+.</p></li><li><p><strong>Blind comparison for iteration.</strong> A separate &#8220;improve&#8221; mode runs the same prompts through two versions of a skill and provides both outputs to a comparator agent that doesn&#8217;t know which version produced which - removing the author&#8217;s own bias from &#8220;did my edit actually help.&#8221;</p></li></ul><div><hr></div><h2>The structural shift</h2><p>The most important takeaway isn&#8217;t the file format itself. It&#8217;s that agent capability is moving from massive, fragile system prompts toward composable, independently versioned units. </p><p>This is the exact same shift software engineering made decades ago&#8212;moving from monoliths to libraries and packages. Now, a legal team can version a contract-review skill while a platform team versions SQL conventions. Both get pulled in independently, without breaking the shared system prompt.</p><div><hr></div><p><em>If you're building with agents and want more breakdowns like this, subscribe for breakdowns about the parts of the agent stack that actually matter, minus the hype.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Agent Has No Clock: Architecting Time in LLM Systems]]></title><description><![CDATA[Understanding is not enforcement. Building a harness which makes an agent respect the clock.]]></description><link>https://avanichaskar.substack.com/p/the-agent-has-no-clock-architecting</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/the-agent-has-no-clock-architecting</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Fri, 12 Jun 2026 03:30:28 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/299f5a54-e8d9-4bbb-86a2-2192c01982f7_1408x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever wondered how an AI agent actually gets a notion of time? At its core, an LLM is just a stateless API call. It has no internal clock, no pulse, and absolutely no sense of seconds ticking by.</p><p>Picture this: You&#8217;re debugging a live production issue. You tell your DevOps agent, <em>&#8220;Find out why the API is throwing 500 errors. You have exactly 5 minutes to investigate before we roll back.&#8221;</em></p><p>The model understands you perfectly. It parses the constraint, scans the server logs, and spots a database timeout. It connects to the database to run an <code>EXPLAIN</code> query, notices a missing index, and spins up a subagent to cross-reference yesterday&#8217;s schema migrations.</p><p>Twenty minutes later, your API is still down, and your agent is deeply engrossed in reading PostgreSQL documentation.</p><p>What failed? Not the LLM. It can reason about time constraints and write brilliant essays on incident management. The problem is your <strong>agent harness</strong> - the orchestration code managing the loop. The model <em>said</em> it would stop, but because it operates in a timeless void, it relied on the harness to check the clock. And the harness failed.</p><p>Some thoughts about time management system for LLM agents:</p><div><hr></div><h3>The Gap Between Language and Reality</h3><p>When you command an agent to &#8220;stop after 5 minutes,&#8221; you are actually issuing two entirely different system requirements:</p><ol><li><p><strong>Semantic Understanding:</strong> The model needs to factor the time budget into its planning. It should prioritize breadth over depth, and start wrapping up as time gets short rather than initiating a deep-dive.</p></li><li><p><strong>Execution Enforcement:</strong> The harness needs to terminate the execution loop when the deadline passes, regardless of what the model decides to do.</p></li></ol><p>Most developers only build the first layer. They write the time constraint into the prompt and trust the model to honor it. That&#8217;s the semantic layer. It works <em>most</em> of the time.</p><p>But &#8220;most of the time&#8221; isn&#8217;t good enough for a production system. Models lose context. Long sessions get compacted. A model deep in a reasoning chain doesn&#8217;t check its watch - it just keeps reasoning. Semantic-only time constraints are advice. They are not guarantees.</p><p>To fix this, you need both layers: a Dual-Layer Architecture. The semantic layer makes the model time-aware. The enforcement layer makes the harness time-authoritative.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!9avB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!9avB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg" width="1090" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1090,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:108459,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://avanichaskar.substack.com/i/201621509?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36b4f308-2223-4615-adb6-4eb8796af64c_1408x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!9avB!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3025cf5-db2b-4cfc-ad4c-c295c7810c94_1090x558.jpeg 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><h3>Layer 1: Giving the Model a Textual Clock</h3><p>The model has no native sense of elapsed time. What it has is context - the conversation history it can see. So the harness must build the model a clock out of text, and inject it into <em>every single turn</em>.</p><p>It looks like this, prepended to every message the model receives:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;808dad0a-27c6-4bfe-8989-bb2ee2670ca6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">&lt;time-status&gt;
Session started: 14:32:10
Elapsed: 3m 44s
Remaining: 1m 16s
&lt;/time-status&gt;</code></pre></div><p>On every turn, the model knows exactly where it stands. It doesn&#8217;t have to guess based on context length. But the real architectural leverage comes from <strong>urgency escalation</strong>. The status message changes as time runs out:</p><ul><li><p><strong>T-minus 2m:</strong> <em>&#8220;Begin winding down new sub-tasks.&#8221;</em></p></li><li><p><strong>T-minus 30s:</strong> <em>&#8220;&#9888;&#65039; FINAL 30 SECONDS - wrap up now.&#8221;</em></p></li></ul><p>A model that was about to fire another deep search reads &#8220;final 30 seconds&#8221; and stops-not because the harness forced it, but because it understood the instruction and adjusted. That&#8217;s the semantic layer working correctly.</p><h3>Layer 2: The Harness Owns the Deadline</h3><p>The semantic layer is good. It is not sufficient. The enforcement layer exists for everything the semantic layer can&#8217;t handle: the model that forgets, the subagent that runs long, the tool call that spirals.</p><p>The enforcement layer works like this: before every single API call, the harness checks the wall clock. If the deadline has passed, the loop doesn&#8217;t proceed. Execution stops. But hard-stopping produces half-written JSON or truncated sentences. Instead, you need <strong>Graceful Degradation</strong>.</p><ol><li><p><strong>The Watchdog Thread:</strong> A background thread counts down independently of the main loop. If an API call hangs or a tool response never comes back, the watchdog fires anyway.</p></li><li><p><strong>The Shutdown Turn:</strong> When the deadline approaches, the harness injects a graceful shutdown message first: <em>&#8220;Time limit reached. Stop all current work. Provide a summary of what you found and what remains incomplete.&#8221;</em></p></li><li><p><strong>The Cutoff:</strong> The model gets one final turn to produce something useful. Then, 30 seconds later, the loop exits regardless of whether that final turn completed.</p></li></ol><div><hr></div><h3>The Subagent Black Hole</h3><p>Here&#8217;s where naive implementations break completely. Simple agents run in a single loop. You control the loop, you check the clock, it works.</p><p>But modern agent frameworks (Claude Code, LangChain) spawn subagents - child sessions that run independently. When a parent agent spawns a subagent, it waits. From the parent&#8217;s perspective, it&#8217;s working. From the system&#8217;s perspective, the parent is blocked waiting for a child that may never return.</p><p>Practitioners call this the <strong>Subagent Black Hole</strong>. The parent&#8217;s clock runs out while it sits blocked on a child that silently failed two minutes ago.</p><p><strong>The Fix:  Budget Propagation.</strong><br>When a parent spawns a subagent, it doesn&#8217;t give the subagent the full original time budget. It carves off a <em>fraction</em> of its remaining time. Parent has 4 minutes left? The subagent gets a hard limit of 2 minutes. This mathematically guarantees the child will resolve (or time out) before the parent deadline fires, always leaving a buffer for the parent to finish its own work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!XwXr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!XwXr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3581368,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://avanichaskar.substack.com/i/201621509?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!XwXr!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea377b2b-88dd-4850-a9fd-629ec31dc428_1408x768.jpeg 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><h3>What the Model Actually Experiences</h3><p>It&#8217;s worth tracing through what a 5-minute debugging session looks like from the model&#8217;s side.</p><ul><li><p><strong>At turn one:</strong> The model sees the task and a status showing zero elapsed time, 5 minutes remaining. It makes a tight plan: check logs, check DB status, summarize. The time constraint shapes the plan before a single tool call fires.</p></li><li><p><strong>At turn four:</strong> The model sees 3 minutes elapsed, 2 minutes remaining. It&#8217;s doing fine. It finds the database timeout.</p></li><li><p><strong>At turn six:</strong> The model sees 4 minutes and 30 seconds elapsed and a warning injected by the harness: <em>&#8220;&#9888;&#65039; FINAL 30 SECONDS - Start wrapping up and summarizing findings.&#8221;</em> It was about to start a new search thread for Postgres schema documentation. It doesn&#8217;t. It starts pulling together the root cause analysis.</p></li><li><p><strong>At turn seven:</strong> With 5 seconds left, the harness injects the graceful shutdown message directly. The model produces a final summary for the team: <em>&#8220;API failing due to table lock on users. Could not verify recent migrations in time. Roll back recommended.&#8221;</em></p></li></ul><p>Thirty seconds later, the loop exits. The hard deadline has passed. The session is over.</p><p>Two things made this work: the model knew where it stood at every step, and the harness guaranteed the outcome regardless.</p><div><hr></div><h3>Production Realities: The 3 Ways This Still Breaks</h3><p>Even with both layers in place, senior engineers should anticipate these three failure modes:</p><p><strong>1. The API Call Eats the Deadline</strong><br>You have 30 seconds left. The model takes 45 seconds to respond. The deadline fires during the call, but the call blocks until it returns. You&#8217;re already 15 seconds over when it comes back.</p><ul><li><p><strong>Fix:</strong> Every API call gets a dynamic timeout equal to the remaining time minus a small buffer. You&#8217;ll sometimes get an incomplete response, but you won&#8217;t hang past your deadline.</p></li></ul><p><strong>2. The Ignored Shutdown</strong><br>You inject the shutdown message. But the model fires one more tool call first&#8212;&#8221;just one more query.&#8221; Now it&#8217;s 40 seconds over waiting on a result it doesn&#8217;t need.</p><ul><li><p><strong>Fix: Tool Stripping.</strong> Strip the tool definitions from the final API call payload entirely. The model <em>cannot</em> call a tool if the schema doesn&#8217;t exist. The graceful shutdown turn must always be forced into a pure text turn.</p></li></ul><p><strong>3. Clock Drift via Context Compaction</strong><br>You&#8217;re running a long pipeline. After context compaction, the early messages -including the system prompt that defined what &lt;time-status&gt; tags mean - are gone. The tags stop working.</p><ul><li><p><strong>Fix:</strong> Treat compaction as a mini-restart. Re-inject a fresh definition of the time-status contract into the compacted context. Don&#8217;t assume rule continuity across context boundaries.</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_!BefQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!BefQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg" width="1401" height="632" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:632,&quot;width&quot;:1401,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:234450,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://avanichaskar.substack.com/i/201621509?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd83d297a-2d08-4f2d-b9cc-91126cfe86d5_1408x768.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!BefQ!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb46146a-2c05-4191-a1b0-4e86d51659b9_1401x632.jpeg 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><h3>The Part That Should Stick</h3><p>The model understands &#8220;stop after 5 minutes&#8221; perfectly well in language. It can reason about it, plan around it, and honor it when things go smoothly.</p><p>But understanding is not enforcement.</p><p>Time in agents is a strict contract between three parties: the user who states the constraint, the model that reasons within it, and the harness that enforces it.</p><ul><li><p>Build a harness that only gives the model a clock, and you rely on the model to be disciplined every single time. Usually isn&#8217;t good enough.</p></li><li><p>Build a harness that only enforces the deadline without telling the model, and you get hard cutoffs with no graceful summary - an agent that just abruptly dies mid-sentence.</p></li></ul><p>Build both, and you get an agent that actually behaves like an engineer with a deadline.</p><div><hr></div><blockquote><p><strong>Checkout GitHub repo for code example - </strong><em><strong><a href="https://github.com/alwaysavani/time-aware-harness">https://github.com/alwaysavani/time-aware-harness </a></strong></em></p></blockquote><div><hr></div><p><em>Thanks for reading this article! </em></p><p><em>The newsletter now reaches more than 100 readers&#127881;</em></p><p><em>Please like and restack. Subscribe to get future issues in your inbox:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[System Design for RAG: The Parsing layer]]></title><description><![CDATA[The Layer Nobody Tunes: Why Your RAG Pipeline&#8217;s Real Problem Is the Parser]]></description><link>https://avanichaskar.substack.com/p/system-design-for-rag-the-parsing</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-for-rag-the-parsing</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Mon, 08 Jun 2026 19:46:29 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0471e71e-a828-40a4-b931-24de85cf4367_1408x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most teams building RAG pipelines spend weeks on the same things &#8212; benchmarking embedding models, tuning chunk sizes, layering in rerankers.</p><p>And yet the answers are still wrong.</p><p>It&#8217;s a frustrating place to be. You&#8217;ve done everything the blog posts say. The retrieval scores look reasonable. But somewhere between the document and the answer, something is getting lost.</p><p>That something is usually the parser. And it&#8217;s almost never what people look at first.</p><div><hr></div><h2>Where does the information actually disappear?</h2><p>It&#8217;s worth tracing through a concrete example. Take a hardware spec sheet &#8212; the kind with component tables, temperature ranges, voltage specs. Put it through a naive PDF extractor. Here&#8217;s what comes out the other side.</p><p>The original table:</p><pre><code><code>| Component | Max Temp | Min Temp | Voltage |
|-----------|----------|----------|---------|
| Sensor A  | 95&#176;C     | -40&#176;C    | 3.3V    |
| Sensor B  | 110&#176;C    | -55&#176;C    | 5.0V    |</code></code></pre><p>What the embedder actually receives after a left-to-right OCR pass:</p><pre><code><code>Component Max Temp Min Temp Voltage Sensor A 95&#176;C -40&#176;C 3.3V Sensor B 110&#176;C -55&#176;C 5.0V</code></code></pre><p>Every number made it through. Every relationship between them didn&#8217;t.</p><p>So when someone asks <em>&#8220;what&#8217;s the minimum operating temperature for Sensor B?&#8221;</em> &#8212; the model is looking at a string where <code>-55&#176;C</code> and <code>-40&#176;C</code> are both sitting near <code>Sensor B</code>. It guesses. And guessing is not what you built a RAG pipeline for.</p><p>This isn&#8217;t really a retrieval failure or an embedding failure. The information was destroyed earlier - before any of that machinery ran. And it tends to happen in one of three recognizable patterns.</p><p><strong>Column bleeding:</strong> Multi-column PDFs get read left-to-right across the full page width. A three-column spec sheet collapses into one long run-on string. The numbers survive. Their context doesn&#8217;t.</p><p><strong>Header severance:</strong> A section heading sits at the bottom of page 4. Its content flows to page 5. A paginated parser splits them into separate chunks. What you end up with is a floating fragment - <em>&#8220;Maximum allowable torque: 47 N&#183;m under sustained load&#8221;</em> - with no indication of what system it belongs to.</p><p><strong>Table flattening:</strong> A table carries 2D relational structure - rows, columns, the meaning created by their intersection. Once that gets linearized into a prose string, the structure is gone. Nothing downstream can reconstruct it.</p><p>All three follow a pattern. All three happen before the first vector is computed.</p><div><hr></div><h2>Why Markdown keeps coming up as the middle layer</h2><p>The choice of intermediate format - what the parser hands off before the chunker sees it - quietly shapes everything that follows.</p><p>Plain text is the obvious default, but it discards structure. HTML preserves structure but gets verbose fast. The same table in HTML runs roughly 3&#8211;5x more tokens than Markdown pipe format - and across a large knowledge base, that difference compounds on every retrieval call.</p><p>The LLMs were trained on large amounts of markdowns. <code>##</code> headings and <code>|</code> table delimiters carry genuine semantic weight - HTML tags largely got stripped from training corpora. A Markdown table passed to a model is legible to it in a way that&#8217;s difficult to replicate with other formats.</p><p>For exmaple: Heading-based chunking - splitting at <code>##</code> and <code>###</code> boundaries - is one of the more reliable chunking strategies available. It only works if the parser actually emits headings. Plain text gives you nothing to split on.</p><p>It&#8217;s just that the format choice quietly constrains what&#8217;s possible later, and Markdown tends to cause the fewest downstream surprises.</p><div><hr></div><h2>Three approaches, and when each one fits</h2><p>Different document types behave very differently under parsing. What works cleanly on a Word export will silently mangle a scanned freight invoice. Most pipelines apply one approach uniformly because it&#8217;s operationally simpler - and it is, right up until it isn&#8217;t.</p><h3>Lightweight heuristic converters</h3><p><strong>Tools in this space:</strong> MarkItDown, PyMuPDF</p><p>These wrap existing document parsers: pdfminer, python-docx - and apply rule-based logic to detect document elements and translate them into Markdown. No ML inference, no GPU, pure heuristics.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!PcJa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!PcJa!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 424w, /__u/substackcdn.com/image/fetch/$s_!PcJa!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 848w, /__u/substackcdn.com/image/fetch/$s_!PcJa!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!PcJa!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!PcJa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png" width="779" height="240" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34d56889-77a8-4b48-8f76-b059d275c641_779x240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:240,&quot;width&quot;:779,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34437,&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://avanichaskar.substack.com/i/201180451?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82b2dfb5-827d-4b5b-8e7c-a39ada628dba_779x283.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_!PcJa!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 424w, /__u/substackcdn.com/image/fetch/$s_!PcJa!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 848w, /__u/substackcdn.com/image/fetch/$s_!PcJa!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!PcJa!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34d56889-77a8-4b48-8f76-b059d275c641_779x240.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>For digitally-authored documents - anything written in Google Docs, Notion, Confluence, or Word and exported cleanly - these are fast, cheap, and accurate. Sub-100ms per page on typical hardware, easy to run synchronously.</p><p>The limitation is equally clear: scanned documents, photographed manuals, legacy exports with unusual layouts - the heuristics don&#8217;t hold.</p><p>The parser processes the scanned manual, returns output, and the pipeline continues normally: the multi-column spec table comes back as a linearized string. The embeddings get computed. </p><p>Everything looks healthy. </p><p>The problem only surfaces when someone asks a question that needs a number from that table.</p><div><hr></div><h3>Vision-language model parsers</h3><p><strong>Tools in this space:</strong> Docling, Marker, LlamaParse, Azure Document Intelligence</p><p>These take a different approach - treating documents as visual objects rather than text streams. Pages get rasterized first. Then a VLM identifies bounding boxes around each element and classifies them (title, paragraph, table, figure, footnote) before any text extraction happens. Reading order is reconstructed from spatial position on the page, not from the byte stream.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!NYlu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!NYlu!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 424w, /__u/substackcdn.com/image/fetch/$s_!NYlu!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 848w, /__u/substackcdn.com/image/fetch/$s_!NYlu!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NYlu!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!NYlu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png" width="1149" height="271" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:271,&quot;width&quot;:1149,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:45958,&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://avanichaskar.substack.com/i/201180451?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.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_!NYlu!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 424w, /__u/substackcdn.com/image/fetch/$s_!NYlu!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 848w, /__u/substackcdn.com/image/fetch/$s_!NYlu!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NYlu!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1df78dbc-18f4-4d3b-8b27-691d8d8834df_1149x271.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The tradeoff : GPU required to be practical at scale, 2&#8211;10 seconds of latency per page depending on complexity. But for documents that originated as physical artifacts - scanned manuals, freight invoices, legacy filings, multi-column academic papers - the quality difference over heuristic parsers isn&#8217;t marginal. It&#8217;s categorical.</p><p>Coming back to that hardware spec table - here&#8217;s what a vision parser produces with the same document:</p><pre><code><code>## Thermal Limitations

| Component | Max Temp | Min Temp | Voltage |
|-----------|----------|----------|---------|
| Sensor A  | 95&#176;C     | -40&#176;C    | 3.3V    |
| Sensor B  | 110&#176;C    | -55&#176;C    | 5.0V    |</code></code></pre><p>The table structure is intact. The parent heading <code>Thermal Limitations</code> is attached. The 2D relationships survived. Same document, different parser, completely different retrieval behavior.</p><div><hr></div><h3>DOM-aware web parsers</h3><p><strong>Tools in this space:</strong> Firecrawl, BeautifulSoup with custom traversal, Playwright + extraction</p><p>The instinct when parsing a wiki or docs site is usually to strip HTML tags and keep the text. HTML is noisy, but stripping tags also discards the semantic tree, which is where a lot of the structure actually lives.</p><p>The DOM already knows what everything is. <code>&lt;h1&gt;</code> signals the most important heading. <code>&lt;li&gt;</code> signals a list item. <code>&lt;table&gt;</code> signals relational structure. A DOM-aware parser traverses that tree and translates the hierarchy into Markdown rather than throwing it away.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!_Glu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!_Glu!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png 424w, /__u/substackcdn.com/image/fetch/$s_!_Glu!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png 848w, /__u/substackcdn.com/image/fetch/$s_!_Glu!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_Glu!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!_Glu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png" width="713" height="547" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:547,&quot;width&quot;:713,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:47210,&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://avanichaskar.substack.com/i/201180451?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2269e1c9-1fd8-435a-8457-b56b6a68e21a_713x584.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_!_Glu!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png 424w, /__u/substackcdn.com/image/fetch/$s_!_Glu!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png 848w, /__u/substackcdn.com/image/fetch/$s_!_Glu!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_Glu!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38d7b40a-1960-4419-80f1-c3c7d25f1ea1_713x547.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>The catch: it depends heavily on source HTML quality. Well-structured wikis with semantic markup parse cleanly. CMS-generated pages with deeply nested <code>&lt;div&gt;</code> containers and JavaScript-rendered content are harder. It&#8217;s also fragile to redesigns &#8212; a navigation restructure can start pulling nav links into every chunk.</p><p>Where it tends to shine is internal wikis and documentation sites with clean markup. The hierarchy matters: a subsection about rate limits stays connected to its parent heading &#8220;Authentication and Access Controls,&#8221; so a retrieved chunk still carries the context that those limits apply to authenticated endpoints specifically.</p><div><hr></div><h2>What&#8217;s worth sending alongside the Markdown</h2><p>Structured Markdown gets you most of the way there. But there&#8217;s one more thing worth thinking about is the metadata that travels with it.</p><p>A chunk in isolation is missing context the LLM would benefit from having. Wrapping the Markdown in a payload that carries document provenance gives the rest of the pipeline more to work with:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;c720df45-5029-40a3-869c-8d28fb5218fa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">{
  "document_id": "doc_99482",
  "source_system": "confluence_wiki",
  "element_type": "table",
  "page_number": 12,
  "bounding_box": [120, 450, 600, 800],
  "content_markdown": "| Component | Max Temp |\n|---|---|\n| Sensor A | 95&#176;C |\n| Sensor B | 110&#176;C |",
  "parent_header": "Thermal Limitations"
}</code></pre></div><p>Two fields in particular tend to earn their weight. <code>parent_header</code> gives a retrieved table chunk the scoping context it would otherwise be missing - the model knows this table is from <em>Thermal Limitations</em>, not electrical specs or mechanical specs. Without it, documents with multiple similar-looking tables start producing ambiguous answers.</p><p><code>bounding_box</code> is the audit trail. When someone wants to know where an answer came from, you can point to the exact region of the original document rather than just the filename. In regulated industries this tends to go from nice-to-have to required fairly quickly.</p><div><hr></div><h2>A rough heuristic for choosing</h2><p>Mixed corpus - which is most real-world situations - just routes by document type at ingestion. It&#8217;s a conditional, not a redesign.</p><p>Often just one parser is uniformly applied for operational simplicity. It works fine - until the heuristic parser quietly mangles every scanned document in the index and three days of debugging trace back to something that happened at text extraction.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!V5tw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!V5tw!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!V5tw!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!V5tw!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!V5tw!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!V5tw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:690079,&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://avanichaskar.substack.com/i/201180451?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.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_!V5tw!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!V5tw!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!V5tw!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!V5tw!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa16c5b3b-5389-49f1-892c-68e3db5049a0_1024x559.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>A step that&#8217;s easy to skip in the debugging loop</h2><p>The usual path when RAG answers go wrong: inspect retrieved chunks &#8594; look at similarity scores &#8594; tweak embeddings &#8594; add a reranker &#8594; try again.</p><p>This loop is reasonable except it starts in the wrong place if the parsing layer is where the problem actually lives.</p><p>Something worth trying at this stage: pull 10&#8211;20 chunks corresponding to the queries that went wrong and just read the Markdown. Not the source documents - the parsed output. </p><p>If the tables are linearized, headers are floating, or columns have bled together, that&#8217;s the diagnosis. A reranker can&#8217;t reconstruct a 2D table that was destroyed before the embedder ever saw it. The fix has to happen earlier.</p><p>It&#8217;s a small thing to check and it explains a surprising number of retrieval failures that look, on the surface, like embedding problems.</p><div><hr></div><h2>Further reading</h2><ul><li><p><strong>Dropbox:</strong> <a href="https://dropbox.tech/machine-learning/using-machine-learning-to-index-text-from-billions-of-images">Using machine learning to index text from billions of images</a></p></li><li><p><strong>Netflix:</strong> <a href="https://netflixtechblog.com/powering-multimodal-intelligence-for-video-search-3e0020cf1202">Synchronizing the Senses: Powering Multimodal Intelligence for Video Search</a></p></li><li><p><strong>Pinterest:</strong> <a href="https://medium.com/pinterest-engineering/unified-context-intent-embeddings-for-scalable-text-to-sql-793635e60aac">Unified Context-Intent Embeddings for Scalable Text-to-SQL</a></p></li></ul><div><hr></div><p><em>Thanks for reading!<br>Please like, restack and share your thoughts about parsers for RAG pipelines. </em></p><p><em>Do subscribe - if you would like to receive future issues -</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[System Design for RAG: Ingestion Layer]]></title><description><![CDATA[How to minimize data drift using Batch, Webhooks, and CDC architectures]]></description><link>https://avanichaskar.substack.com/p/system-design-for-rag-ingestion-layer</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-for-rag-ingestion-layer</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Sat, 06 Jun 2026 18:37:14 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/60677c44-d333-443d-928f-659b328c1bda_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p style="text-align: justify;">In production Retrieval-Augmented Generation (RAG), data freshness dictates accuracy. Delays between a source update and a vector index sync create data drift, forcing the LLM to hallucinate based on stale context. In this article, we will explore the three ingestion architectures: Scheduled Batch, Event-Driven Webhooks, and Change Data Capture (CDC), that enterprise engineering teams use to solve this problem and build scalable, low-latency data pipelines.</p><div><hr></div><h2>Quantifying Data Drift</h2><p>Data drift is the time delta between a data modification in a source system (e.g., Confluence, Jira, PostgreSQL) and its availability in the vector database.</p><p><strong>               Data Drift Latency = T(Vector Available) - T(Source Mutation)</strong></p><p>Minimizing this latency requires moving away from heavy, periodic batch syncs toward real-time stream processing architectures. </p><p>Let&#8217;s break down the three primary patterns:</p><div><hr></div><h2>Pattern 1: Scheduled Batch Ingestion (Pull-Based)</h2><p>Batch ingestion relies on scheduled ETL pipelines to take periodic snapshots of the data 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_!f24R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!f24R!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!f24R!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!f24R!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!f24R!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!f24R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:205518,&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://avanichaskar.substack.com/i/200917772?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.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_!f24R!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!f24R!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!f24R!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!f24R!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8a55b56-b7de-4012-8150-c0bc344241d2_1024x559.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>How it Works</h3><ol><li><p>A workflow orchestrator (e.g., Apache Airflow) triggers an Apache Spark or Databricks job on a fixed schedule (e.g., every 6 or 24 hours).</p></li><li><p>The job runs a full or incremental scan on the data lake (e.g., AWS S3, Snowflake).</p></li><li><p>The cluster processes text segments, generates embeddings in parallel batches, and writes them to the vector index.</p></li></ol><h3>Engineering Tradeoffs</h3><ul><li><p><strong>Pros:</strong> Highly compute-efficient for bulk historical data processing. Minimizes continuous network connections.</p></li><li><p><strong>Cons:</strong> High data drift latency. If a document updates immediately after a batch finishes, that update remains invisible to the RAG system until the next cycle.</p></li><li><p><strong>Real-World Usage:</strong> Teams use heavy batch processing pipelines for non-time-critical data, such as indexing large historical catalogs or archiving years of system logs.</p></li></ul><div><hr></div><h2>Pattern 2: Event-Driven Webhooks (Push-Based)</h2><p>Event-driven ingestion shifts the responsibility of data movement from the target pipeline to the source application via HTTP push notifications or message queues.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!jAeR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!jAeR!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!jAeR!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!jAeR!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jAeR!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!jAeR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:195765,&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://avanichaskar.substack.com/i/200917772?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.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_!jAeR!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!jAeR!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!jAeR!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jAeR!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc0c171d-72b7-42df-8703-de07286be0d5_1024x559.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>How it Works</h3><ol><li><p>A user modifies a document in a SaaS application (e.g., Notion, Slack, Jira).</p></li><li><p>The application triggers a webhook event containing the document ID and mutation details.</p></li><li><p>An API gateway captures the event and places it into a distributed task queue (e.g., Celery, AWS SQS).</p></li><li><p>Isolated worker processes fetch the specific updated document, chunk it, re-embed it, and upsert the vector index.</p></li></ol><h3>Engineering Tradeoffs</h3><ul><li><p><strong>Pros:</strong> Low latency. Updates reflect in near real-time. Eliminates unnecessary compute overhead from scanning unchanged documents.</p></li><li><p><strong>Cons:</strong> Prone to API rate limiting (HTTP 429) when fetching large document bodies from SaaS providers during peak traffic. Requires robust retry mechanisms with exponential backoff.</p></li><li><p><strong>Real-World Usage:</strong> Highly responsive event-driven mechanisms are ideal for third-party SaaS tools, ensuring internal bots reflect system status changes instantly without polling backend systems.</p></li></ul><div><hr></div><h2>Pattern 3: Change Data Capture (CDC) with Stream Processing</h2><p>For internal application databases where application-level webhooks are unfeasible, Change Data Capture (CDC) intercepts data mutations directly at the database engine layer.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!5Nh8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!5Nh8!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!5Nh8!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!5Nh8!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5Nh8!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!5Nh8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02a003af-8866-47ff-8797-8595216e7792_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:231351,&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://avanichaskar.substack.com/i/200917772?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.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_!5Nh8!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!5Nh8!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!5Nh8!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5Nh8!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a003af-8866-47ff-8797-8595216e7792_1024x559.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>How it Works</h3><ol><li><p>An application modifies a row in a database.</p></li><li><p>The database engine records the transaction in its Write-Ahead Log (WAL) or binary log.</p></li><li><p>A CDC connector (e.g., Debezium) tails the log, extracts the row-level diff, and publishes it as an event to a log broker like Apache Kafka.</p></li><li><p>A stream processing engine (e.g., Apache Flink) consumes the stream, flattens the schema, chunks the data in-flight, computes embeddings, and pushes to the vector layer.</p></li></ol><h3>Engineering Tradeoffs</h3><ul><li><p><strong>Pros:</strong> Near-zero data drift latency (milliseconds). Zero performance impact on production application queries since it reads directly from transaction logs.</p></li><li><p><strong>Cons:</strong> High infrastructure complexity. Requires managing distributed log streaming platforms and stateful stream processors.</p></li><li><p><strong>Real-World Usage:</strong> This is the standard for core internal application databases. It guarantees the vector index is always perfectly synchronized with the system of record.</p></li></ul><div><hr></div><h2>Summary</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!2GCN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 424w, /__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 848w, /__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 1272w, /__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!2GCN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png" width="1456" height="264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:264,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89593,&quot;alt&quot;:&quot;&quot;,&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://avanichaskar.substack.com/i/200917772?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 424w, /__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 848w, /__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 1272w, /__u/substackcdn.com/image/fetch/$s_!2GCN!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda9afd92-3329-4649-8d12-ac4058753f9e_1546x280.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Further Reading</h2><p>To see how these ingestion patterns operate at hyperscale, read these architectural teardowns from the teams that built them:</p><ul><li><p><em><a href="https://www.infoq.com/news/2026/02/uber-pull-based-opensearch/">Uber Moves In-House Search Indexing to Pull-Based Ingestion</a> </em></p></li><li><p><em><a href="https://netflixtechblog.com/keystone-real-time-stream-processing-platform-a3ee651812a">Keystone Real-time Stream Processing Platform</a> </em></p></li><li><p><em><a href="https://engineering.linkedin.com/data-ingestion/gobblin-big-data-ease">Gobblin&#8217; Big Data With Ease</a> </em></p></li></ul><div><hr></div><p>Thanks for reading. In the next issue, we break down Stage 2 of the RAG pipeline -Parsers.</p><p> Please like and consider subscribing :</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p><p style="text-align: justify;"></p>]]></content:encoded></item><item><title><![CDATA[System Design Deep Dive: How AI Agents Remember]]></title><description><![CDATA[AI agents hit the same wall.]]></description><link>https://avanichaskar.substack.com/p/system-design-deep-dive-how-ai-agents</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-deep-dive-how-ai-agents</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Sat, 30 May 2026 07:46:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!XvMC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI agents hit the same wall. They reasons well, call tools, look smart. </p><p>Then real users run multi-step tasks. </p><p>The agent forgets context from five minutes ago. It repeats work. It makes the same mistake twice.</p><p>The problem is not the model. It is memory.</p><p>LLMs are stateless. Every API call starts fresh. You send the full conversation. The model reads it. Then it forgets. This works for chatbots. It fails for agents. Agents run multi-step tasks. They hand off work. They need continuity.</p><p>Continuity requires a memory system. </p><div><hr></div><h3>The 4 Layers of Agent Memory</h3><p>System design is about trade-offs. Each memory layer trades speed for capacity.</p><h4>1. In-Context Memory (Working Memory)</h4><p>This is short-term memory. It lives inside the current prompt window. It holds the system prompt, recent history, and tool outputs.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;75055e46-ed29-463f-ae53-6ac546f4ba93&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">messages = [
    {"role": "system", "content": system_prompt},
    {"role": "user",   "content": user_request},
    {"role": "assistant", "content": last_response},
    {"role": "tool",   "content": tool_output}
]
response = llm.chat(messages)
</code></pre></div><p><strong>Pros:</strong> Fast. Zero retrieval error. Simple.</p><p><strong>Cons:</strong> Small capacity. High inference cost. Ephemeral.</p><p><strong>When to use:</strong> Current task state. Keep working context under 20K tokens. Summarize old turns. Do not bloat the prompt.</p><h4>2. Semantic Memory (Vector Store)</h4><p>This is the long-term knowledge base. You embed facts as vectors. You store them in a database. You retrieve relevant chunks at query time.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b4ca8ecd-4fc4-49cc-a91d-8991bcde87b1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Store
embedding = embed_model.encode("Refund policy is 30 days.")
vector_store.upsert(id="doc-42", vector=embedding, metadata={"type": "policy"})

# Retrieve
query_vec = embed_model.encode(user_query)
results = vector_store.query(query_vec, top_k=5, filter={"type": "policy"})
</code></pre></div><p><strong>Pros:</strong> Massive scale. Persistent. High signal-to-noise ratio.</p><p><strong>Cons:</strong> Retrieval errors. Added latency (50&#8211;200ms). Requires chunking strategy.</p><p><strong>When to use:</strong> Domain knowledge and user preferences.</p><p><strong>Pro-tip:</strong> Do not use pure vector search. Add a metadata filter. Use hybrid search (Vector + BM25).</p><h4>3. Episodic Memory (Task History)</h4><p>This stores actions, not facts. It holds past conversations, tool calls, and outcomes. The agent learns from experience.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;864387be-44d3-4621-add1-0ea601e57314&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">summary = llm.summarize(completed_task)
episodic_store.save({
    "user_id": user.id,
    "task_type": "analysis",
    "summary": summary,
    "outcome": "success",
    "timestamp": now()
})
</code></pre></div><p><strong>Pros:</strong> Prevents repeated mistakes. Enables personalization.</p><p><strong>Cons:</strong> Summaries lose detail. Hard to retrieve the exact right episode. Fast storage growth.</p><p><strong>When to use:</strong> Recurring tasks for the same user. Customer support histories. Codebase debugging logs.</p><h4>4. Procedural Memory (Instructions)</h4><p>This is the know-how. It contains system prompts, tool schemas, and workflows. It rarely changes at runtime.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;38fa981a-f90a-46c5-9ccb-62f695f28667&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">system_prompt = "You are a data analyst. Verify SQL syntax. Never expose PII."
tools = [{"name": "run_sql", "parameters": {...}}]
response = llm.chat(messages, system=system_prompt, tools=tools)
</code></pre></div><p><strong>Pros:</strong> Zero latency. Highly reliable. Easy to version control.</p><p><strong>Cons:</strong> Inflexible mid-run. Consumes context window.</p><p><strong>When to use:</strong> Always. Keep system prompts under 500 tokens. Move rare instructions to semantic retrieval.</p><div><hr></div><h3>State Management Patterns</h3><p>Memory stores data. State tracks current execution. These are distinct problems. Use one of three patterns.</p><h4>Pattern 1: Stateless Agents</h4><p>No persistence between calls. The agent gets a task, executes, and returns.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;ddf4f86b-6cec-4e40-ae54-ba1ac750a63b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">User Request --&gt; Agent --&gt; Response</code></pre></div><ul><li><p><strong>Use case:</strong> Simple classification. Question answering. Horizontal scaling.</p></li><li><p><strong>Trade-off:</strong> Easy to build. Zero continuity.</p></li></ul><h4>Pattern 2: Stateful Session Agents</h4><p>The agent maintains state in RAM during a session. It persists a summary to episodic memory when finished.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;5a6179d2-b36f-4726-9b53-1299e8f800e9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Start --&gt; Load Context --&gt; Agent Loop --&gt; Persist Summary --&gt; End</code></pre></div><ul><li><p><strong>Use case:</strong> Multi-turn research. Coding. Chat support.</p></li><li><p><strong>Trade-off:</strong> The production sweet spot. Moderate complexity. High utility.</p></li></ul><h4>Pattern 3: Long-Horizon Agents</h4><p>Tasks span days. State lives in a durable database. The agent can pause, resume, or crash without losing progress.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;d35c532c-1ef7-498d-a817-98711153ddd1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Task DB --&gt; Wake &amp; Load --&gt; Run Checkpoint --&gt; Sleep --&gt; Resume</code></pre></div><ul><li><p><strong>Use case:</strong> Background jobs. Multi-agent swarms. Fragile, long-running workflows.</p></li><li><p><strong>Trade-off:</strong> High complexity. Requires orchestration tools like Temporal or LangGraph.</p></li></ul><div><hr></div><h3>The Trade-Off Matrix</h3><p>Combine memory types. No single layer does it all.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!djr0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!djr0!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 424w, /__u/substackcdn.com/image/fetch/$s_!djr0!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 848w, /__u/substackcdn.com/image/fetch/$s_!djr0!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 1272w, /__u/substackcdn.com/image/fetch/$s_!djr0!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!djr0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png" width="1114" height="212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:212,&quot;width&quot;:1114,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53988,&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://avanichaskar.substack.com/i/199840739?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4e4b611-6fae-4b88-94d9-e199090a8284_1124x212.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_!djr0!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 424w, /__u/substackcdn.com/image/fetch/$s_!djr0!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 848w, /__u/substackcdn.com/image/fetch/$s_!djr0!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 1272w, /__u/substackcdn.com/image/fetch/$s_!djr0!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2adcb5ef-ef96-41eb-a572-d95b3b875dda_1114x212.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h3>Production Anti-Patterns</h3><p>Senior engineers repeatedly make these five mistakes.</p><ul><li><p><strong>Context dumping:</strong> More context is not better. Models ignore the middle of long prompts. Compress and summarize.</p></li><li><p><strong>Naive top-K retrieval:</strong> Top-5 vector search fails in production. Add metadata filters. Use BM25. Re-rank your chunks.</p></li><li><p><strong>Ignoring freshness:</strong> Six-month-old preferences mislead agents. Timestamp everything. Build decay policies.</p></li><li><p><strong>Brittle retrieval:</strong> Vector databases go down. Queries fail. Design graceful degradation. Tell the agent when context is empty.</p></li><li><p><strong>Monolithic memory:</strong> Vector stores are not session buffers. Use the right layer for the right job.</p></li></ul><div><hr></div><h3>Reference Architecture</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!XvMC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!XvMC!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png 424w, /__u/substackcdn.com/image/fetch/$s_!XvMC!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png 848w, /__u/substackcdn.com/image/fetch/$s_!XvMC!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png 1272w, /__u/substackcdn.com/image/fetch/$s_!XvMC!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!XvMC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png" width="932" height="353" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:353,&quot;width&quot;:932,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:584533,&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://avanichaskar.substack.com/i/199840739?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64e91931-2a5a-4852-8b39-d0d9924f62f6_1024x572.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_!XvMC!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png 424w, /__u/substackcdn.com/image/fetch/$s_!XvMC!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png 848w, /__u/substackcdn.com/image/fetch/$s_!XvMC!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.png 1272w, /__u/substackcdn.com/image/fetch/$s_!XvMC!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d10266-bc2d-4637-85ee-0991f5d86525_932x353.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><figcaption class="image-caption">caption...</figcaption></figure></div><div><hr></div><h3>Key Takeaways</h3><ul><li><p>LLMs are stateless. Memory creates continuity.</p></li><li><p>Keep working context small. It saves money and maintains model focus.</p></li><li><p>Semantic memory needs hybrid search. Pure vectors fail in production.</p></li><li><p>Episodic memory requires strict metadata and timestamps.</p></li><li><p>State is active execution. Memory is passive recall. Design for both.</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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">Thanks for reading! Subscribe for free to receive new posts.</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[The Six Questions to Ask Before Saying Yes to Any AI Feature Request ]]></title><description><![CDATA[Requirements are being pitched for AI features faster than engineering teams can evaluate them.]]></description><link>https://avanichaskar.substack.com/p/the-six-questions-to-ask-before-saying</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/the-six-questions-to-ask-before-saying</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Thu, 28 May 2026 03:50:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!EHQ-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Requirements are being pitched for AI features faster than engineering teams can evaluate them. Not every &#8216;add AI to this&#8217; idea is worth building. You need a fast filter that works before you commit to months of work.</p><h3><strong>Why a Filter Matters</strong></h3><p>AI features are not like regular software features. The failure modes are different: </p><ul><li><p>Quality is probabilistic, not deterministic. </p></li><li><p>Maintenance involves monitoring, retraining, and eval management - not just bug fixes. </p></li></ul><p>The cost of a bad AI feature is not just wasted engineering time. It is user trust eroded by a system that confidently produces wrong answers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!EHQ-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!EHQ-!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!EHQ-!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!EHQ-!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EHQ-!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!EHQ-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:915912,&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://avanichaskar.substack.com/i/199551619?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.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_!EHQ-!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png 424w, /__u/substackcdn.com/image/fetch/$s_!EHQ-!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png 848w, /__u/substackcdn.com/image/fetch/$s_!EHQ-!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EHQ-!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78cf7c28-dad6-49ca-bcbb-67dc9a5af8e4_1024x572.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><h3><strong>The Six Questions</strong></h3><ol><li><p><strong>Is this a task AI is actually good at? </strong>AI is strong at summarisation, classification, retrieval, and generation. It is weak at precise numerical reasoning, tasks requiring real-time information without retrieval, and tasks with strict correctness requirements. Identify which category this request falls into before scoping.</p></li><li><p><strong>What does failure look like? </strong>A wrong recommendation in a movie app is mildly annoying. A wrong answer in a medical triage tool is dangerous. Define the failure mode before you design the system - it determines how much safety investment is required.</p></li><li><p><strong>What data do we have? </strong>Is there labelled training data? Is the knowledge base current? Is the data representative of all users? A feature that requires data you do not have is a data project before it is an AI project.</p></li><li><p><strong>Who evaluates quality and how? </strong>If the answer is &#8216;the product manager eyeballs some outputs before launch,&#8217; the feature is not ready to be built. Evals must be defined before development starts.</p></li><li><p><strong>What is the latency and cost budget? </strong>LLM calls are 10&#8211;100x more expensive than database queries. They are also 100&#8211;1,000x slower. If the feature requires sub-100ms response time, verify the architecture can achieve it before committing.</p></li><li><p><strong>Who maintains this after launch? </strong>AI features require ongoing maintenance: drift monitoring, eval updates, occasional retraining, prompt tuning. If no one owns it after launch, it will silently degrade.</p></li></ol><div><hr></div><h3><strong>The Two-Day Hackathon</strong></h3><p>Before committing to a multi-month AI project, run a two-day feasibility hackathon. Build the simplest possible version. Evaluate it against 50 real examples. If it cannot pass that bar, the feature is not ready - regardless of how compelling the pitch was.</p><p>A two-day spike that kills a bad idea is one of the highest-ROI investments an engineering team can make.</p><div><hr></div><h3><strong>Takeaway</strong></h3><p>Treat AI pitches with extreme skepticism. Filter them quickly using a 6-question stress test (covering data, cost, failure modes, and maintenance) and a 2-day prototype sprint. Saying "no" to bad AI is just as important as building good AI.</p><div><hr></div><p>Like and subscribe if you liked this article and share what questions do you ask to check whether AI should be used to solve a problem or not?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.substack.com/p/the-six-questions-to-ask-before-saying/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/avanichaskar.substack.com/p/the-six-questions-to-ask-before-saying/comments"><span>Leave a comment</span></a></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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"></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[System Design of a Multi-Tenant LLM Serving Platform]]></title><description><![CDATA[How Multi-Tenant LLM Platforms Work: Isolation, Cost Attribution, and Data Residency at Scale]]></description><link>https://avanichaskar.substack.com/p/system-design-of-a-multi-tenant-llm</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-of-a-multi-tenant-llm</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Sun, 24 May 2026 15:43:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!2eja!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A look inside how platforms like Azure OpenAI, AWS Bedrock, and Anthropic API serve thousands of enterprise customers on shared infrastructure &#8212; without letting them interfere with each other.</p><div><hr></div><h3>The Core Problem</h3><p>When a company builds an LLM API platform, it faces a challenge that traditional SaaS platforms have never dealt with at this scale: GPU time is expensive, inference is stateful, and enterprise customers have wildly different requirements around privacy, performance, and compliance.</p><p>How do you serve a startup on a $99/month plan and a bank on a $500,000/year dedicated contract &#8212; on the same infrastructure &#8212; without one affecting the other?</p><p>The answer is a layered isolation architecture. Let&#8217;s break it down.</p><div><hr></div><h3>The Three-Tier Isolation Model</h3><p>Not all tenants need the same level of isolation. Building one mode for everyone either wastes resources or fails compliance requirements. The industry solution is three tiers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!2eja!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!2eja!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!2eja!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!2eja!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!2eja!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!2eja!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg" width="790" height="556" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:556,&quot;width&quot;:790,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69513,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&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_!2eja!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!2eja!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!2eja!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!2eja!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88ad592b-15c5-4218-86b2-7b293d0ce161_790x556.jpeg 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><strong>Tier 1 : Shared cluster (logical isolation)</strong></p><p>All tenants share the same GPU cluster and the same model weights. Tenant identity is tracked at the session level &#8212; a tenant ID injected by the gateway, RBAC claims resolved at auth time, audit logs tagged per tenant.</p><p>This works for most customers. There is no hardware separation, but there is no cross-tenant data leakage either. The model processes sessions sequentially and the gateway ensures no tenant can access another&#8217;s conversation history.</p><p><em>Who uses it:</em> Startups, developers, low-sensitivity workloads.</p><p><strong>Tier 2 : Dedicated instance (process-level isolation)</strong></p><p>The tenant gets their own vLLM process, potentially with a custom fine-tuned model loaded. They still share the physical GPU cluster but have dedicated KV cache memory. Logs are fully isolated and never co-mingled with other tenants.</p><p>This tier suits enterprises that need contractual log isolation, custom model weights, or a guaranteed throughput floor.</p><p><em>Who uses it:</em> Mid-market enterprises, companies with internal compliance requirements.</p><p><strong>Tier 3 : Dedicated cluster (physical isolation)</strong></p><p>Full VPC isolation. Dedicated physical hardware. Completely airgapped from shared infrastructure. Every prompt, completion, and log stays within the customer&#8217;s dedicated environment.</p><p>This is the only tier acceptable for HIPAA, FedRAMP, and most government customers whose legal teams will not accept logical separation as a substitute for physical separation.</p><p><em>Who uses it:</em> Healthcare, government, finance, heavily regulated industries.</p><div><hr></div><h3>The Key Insight Most People Miss</h3><p>Most engineers think of isolation as a <strong>security property</strong> &#8212; prevent Tenant A from reading Tenant B&#8217;s data.</p><p>That is necessary, but not sufficient.</p><p>Isolation is also a <strong>commercial property</strong>.</p><p>A customer paying for a dedicated tier is not just buying privacy. They are buying a performance guarantee : a throughput floor that cannot be eroded when another tenant spikes traffic at 3am.</p><p>The architecture must enforce this at the <strong>resource allocation level</strong>, not just the logical routing level. Separate GPU memory. Separate queues. Separate circuit breakers. Logical separation without resource separation is not truly dedicated.</p><div><hr></div><h3>The Gateway Layer</h3><p>Every request from every tenant - regardless of tier - passes through a shared API gateway. This is where the critical cross-cutting concerns live.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!OIxX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!OIxX!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!OIxX!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!OIxX!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!OIxX!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!OIxX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg" width="1024" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1024,&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_!OIxX!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!OIxX!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!OIxX!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!OIxX!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ce69f8-8dff-455a-a755-3577410778a6_1024x819.jpeg 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> The gateway handles:</p><ul><li><p><strong>Authentication:</strong> JWT validated against your IdP (Okta, Entra). Claims resolved once per request.</p></li><li><p><strong>Quota enforcement:</strong> Token-per-minute (TPM) and request-per-minute (RPM) tracked per tenant in a Redis sliding window. Soft limits trigger a warning. Hard limits return <code>429 Too Many Requests</code>.</p></li><li><p><strong>Admission control:</strong> When the shared cluster is near capacity, Tier 1 traffic is shed first - queued or rate-limited. Tier 2 and Tier 3 customers are never touched by this mechanism.</p></li><li><p><strong>Cost tagging:</strong> Every request is tagged with tenant ID, model ID, and timestamp before it hits the inference layer.</p></li></ul><div><hr></div><h3>Cost Attribution: Metering at the Token Level</h3><p>LLM usage is metered in tokens - not requests, not seconds, not API calls. Input tokens and output tokens are priced differently (output is more expensive to generate), so they must be tracked separately.</p><p><strong>The metering pipeline:</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!PpCd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!PpCd!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!PpCd!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!PpCd!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!PpCd!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!PpCd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg" width="1024" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1024,&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_!PpCd!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!PpCd!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!PpCd!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!PpCd!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6f8ebc3-f0e4-45db-8a30-11cb22f2b33f_1024x819.jpeg 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><br>Each customer sees their own usage dashboard: daily token spend, model breakdown, cost by API key or feature. Budget alerts fire at 80% consumption so customers are never surprised by an overage.</p><p>One nuance worth calling out: <strong>cached tokens</strong> should be tracked separately. If the KV cache reuses a prompt prefix the customer has sent before, the effective cost is lower. Customers who build systems with consistent prompt prefixes benefit from this &#8212; it should be reflected in their bill.</p><div><hr></div><h3>Data Residency</h3><p>Enterprise customers - especially in Europe - have contractual and legal requirements about where their data lives. A prompt sent by an EU customer must be processed in the EU and never leave EU infrastructure.</p><p><strong>Region-aware routing</strong> is the foundation. Each tenant&#8217;s home region is stored in the gateway config. Every request is routed based on that config - not on where the request originated, but on where the tenant&#8217;s data is allowed to go.</p><p>This is enforced at the <strong>network level</strong> (subnet routing rules) not just the application level. A misconfigured application cannot accidentally forward an EU request to a US server if the network layer blocks it.</p><p><strong>No-log mode</strong> for the strictest tenants. When enabled:</p><ul><li><p>The gateway tags the request with a <code>do-not-persist</code> flag</p></li><li><p>The inference server processes the prompt entirely in memory</p></li><li><p>No prompt or completion is written to any storage layer</p></li><li><p>Only metadata (token counts, latency, model version) is written to the billing database</p></li><li><p>The billing database itself is also region-scoped for these tenants</p></li></ul><p><strong>Encryption</strong> uses tenant-managed keys. Each tenant&#8217;s data is encrypted under a key stored in a tenant-controlled KMS. Key rotation happens on a tenant-defined schedule. Even a storage layer breach does not expose readable data without the tenant&#8217;s key.</p><div><hr></div><h3>The Serving Layer: How Inference Actually Works</h3><p>For the shared tier, <strong>vLLM with continuous batching</strong> is the production standard. Continuous batching processes multiple requests concurrently rather than waiting for one to finish before starting another. It is why shared GPU clusters can sustain 60&#8211;70% utilisation without constant memory pressure.</p><p><strong>PagedAttention</strong> manages KV cache memory. The KV cache is the main memory bottleneck in LLM inference - it stores the attention state for every token in every active request. PagedAttention allocates cache memory in non-contiguous blocks (like virtual memory), preventing the fragmentation that causes out-of-memory crashes under bursty load.</p><p>For tenants with <strong>custom fine-tuned models</strong>, LoRA adapters are the right architecture:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!oYb0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!oYb0!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!oYb0!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!oYb0!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!oYb0!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!oYb0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&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_!oYb0!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!oYb0!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!oYb0!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!oYb0!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bc2937-c111-4a77-8dd8-4613b9f407a4_1024x559.jpeg 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>The base model weights are loaded once. Only the small adapter weights are tenant-specific. A 7B-parameter base model with a rank-16 LoRA adapter adds fewer than 20 million parameters &#8212; orders of magnitude less GPU memory than running separate model copies per tenant.</p><div><hr></div><h3>Noisy Neighbour: The Hardest Operational Problem</h3><p>A tenant on the shared tier with a traffic spike should not degrade performance for a tenant on a dedicated tier. Three mechanisms enforce this.</p><p><strong>Separate resource pools.</strong> Tier 2 and Tier 3 tenants have reserved GPU memory and compute. The shared cluster&#8217;s queue is physically separate from the dedicated cluster&#8217;s queue. A Tier 1 traffic surge cannot consume Tier 2/3 resources.</p><p><strong>Priority queuing at the gateway.</strong> Requests are enqueued with priority based on tier. Under load, Tier 1 requests are the first to be delayed or shed.</p><p><strong>Circuit breakers per session.</strong> If a single session is generating unusually high load (a runaway agent loop, for example), a circuit breaker trips for that session. The tenant gets a structured error. The rest of the platform is unaffected.</p><div><hr></div><h3>Observability: What Each Tenant Sees vs. What the Platform Sees</h3><p><strong>Tenant view:</strong></p><ul><li><p>Their own latency percentiles (p50, p95, p99)</p></li><li><p>Token throughput over time</p></li><li><p>Quota consumption vs. limit</p></li><li><p>Error rate and error breakdown</p></li><li><p>Cost by day, model, and API key</p></li></ul><p><strong>Platform view:</strong></p><ul><li><p>Overall GPU utilisation across all clusters</p></li><li><p>Tier 1 shed rate (how often shared-tier traffic is being dropped or delayed)</p></li><li><p>Per-region capacity headroom</p></li><li><p>Cross-tenant anomalies (a tenant spiking unusually)</p></li><li><p>Queue depth per tier</p></li></ul><p>The separation matters. A Tier 1 customer should never be able to infer anything about a Tier 3 customer&#8217;s usage from their own dashboard. Platform-level metrics are only visible to operators.</p><div><hr></div><h3>Summary</h3><p>Concern Solution Tenant isolation Three-tier model: shared &#8594; dedicated instance &#8594; dedicated cluster Performance guarantees Resource-level separation, not just logical routing Cost attribution Token-level metering through Kafka pipeline Quota enforcement Redis sliding window per tenant at gateway Data residency Region-aware routing + no-log mode + tenant-managed encryption keys Custom models LoRA adapters over shared base weights Noisy neighbours Separate resource pools + priority queuing + circuit breakers Observability Tenant-scoped dashboards + operator-level platform view</p><div><hr></div><h3>The Design Principle That Ties It Together</h3><p>Every decision in this architecture flows from one principle: <strong>different customers have bought different things</strong>.</p><p>A startup on the shared tier bought cheap, best-effort access. An enterprise on the dedicated tier bought a performance contract. A regulated institution on the isolated cluster bought a compliance guarantee.</p><p>The platform&#8217;s job is to honour all three contracts simultaneously &#8212; without any customer&#8217;s experience affecting another&#8217;s. That is the multi-tenancy problem in AI infrastructure.</p><div><hr></div><p>Please like and restack if you found this article informative!!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://avanichaskar.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/avanichaskar.substack.com/subscribe"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[System Design for MCP: The Enterprise Gateway Pattern and Agentic Resiliency]]></title><description><![CDATA[Most teams first meet MCP in a local environment - a lightweight script, a stdio connection, an agent talking to a local database.]]></description><link>https://avanichaskar.substack.com/p/system-design-for-mcp-the-enterprise</link><guid isPermaLink="false">https://avanichaskar.substack.com/p/system-design-for-mcp-the-enterprise</guid><dc:creator><![CDATA[Avani Chaskar]]></dc:creator><pubDate>Sat, 23 May 2026 17:34:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HCoQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most teams first meet MCP in a local environment - a lightweight script, a stdio connection, an agent talking to a local database. Production is a different beast.</p><div><hr></div><p>Go from a single-user setup to a cloud-hosted, multi-tenant system and things break fast. Statelessness, authentication, and agent retry loops will find every crack in your design.</p><p>Here are some common techniques to run MCP reliably at scale.</p><div><hr></div><h2>The Most Common Mistake: Auth Baked Into Individual Servers</h2><p>Say you have three MCP servers - one for your issue tracker, one for Prometheus metrics, one for your user directory. If each one handles its own auth, you get three different OAuth flows, three security models, and three things to maintain.</p><p>The fix is the <strong>API Gateway pattern</strong> - the same one Netflix and Uber use for microservice routing.</p><p>In production, MCP servers live on a private subnet. They expose tools. They do not handle user identity. A single Gateway sits between your AI clients and your MCP backends.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!HCoQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!HCoQ!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!HCoQ!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!HCoQ!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!HCoQ!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_webp, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!HCoQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:831436,&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://avanichaskar.substack.com/i/198984372?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.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_!HCoQ!, /__u/avanichaskar.substack.com/w_424, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png 424w, /__u/substackcdn.com/image/fetch/$s_!HCoQ!, /__u/avanichaskar.substack.com/w_848, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png 848w, /__u/substackcdn.com/image/fetch/$s_!HCoQ!, /__u/avanichaskar.substack.com/w_1272, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.png 1272w, /__u/substackcdn.com/image/fetch/$s_!HCoQ!, /__u/avanichaskar.substack.com/w_1456, /__u/avanichaskar.substack.com/c_limit, /__u/avanichaskar.substack.com/f_auto, /__u/avanichaskar.substack.com/q_auto:good, /__u/avanichaskar.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f7ccb6a-31f2-4337-97e7-8aac130762a6_1024x559.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><strong>Here is how a request flows:</strong></p><ol><li><p>The AI client connects to the Gateway via HTTP/SSE with a JWT in the <code>Authorization</code> header.</p></li><li><p>The Gateway checks the token against your IdP - Okta, Entra, whatever you use - and reads the user&#8217;s permissions.</p></li><li><p>The Gateway handles rate limiting and audit logging.</p></li><li><p>The Gateway sends a clean JSON-RPC payload to the right internal MCP server.</p></li></ol><p>One place for auth. Simple servers behind it.</p><div><hr></div><h2>Insight 1: Filter Tools Based on Who Is Asking</h2><p>Here is where most teams get tripped up:</p><p>Securing a REST API is straightforward. A client hits an endpoint. The server checks the role. If the user is not allowed, it returns <code>403</code>. The client never sees what it cannot access.</p><p>MCP works differently. Before anything else, the server sends the AI host a full list of every tool it offers. The host puts that list into the LLM&#8217;s context window.</p><p><strong>So if your server lists </strong><code>refund_stripe_charge</code><strong> or </strong><code>trigger_kubernetes_rollback</code><strong> for everyone - and then relies on a backend </strong><code>403</code><strong> to stop unauthorised users - you have already told the LLM those tools exist.</strong></p><p>That is a problem. LLMs hallucinate. Knowing a tool is there may be enough to make an agent try to use it. You get wasted tokens and noisy errors at best. A bad action at worst.</p><p><strong>The fix: filter tools before the LLM ever sees them.</strong></p><p>Do it at the session handshake, before discovery runs.</p><ol><li><p>Session starts. The Gateway reads the JWT and fetches the user&#8217;s permissions.</p></li><li><p>The Gateway pulls the full tool list from the backend servers.</p></li><li><p>The Gateway trims it. A guest gets <code>query_public_docs</code>. A support agent gets <code>read_customer_telemetry</code>. Only a senior on-call engineer sees <code>restart_service</code>.</p></li><li><p>The trimmed list goes to the AI host.</p></li></ol><p>The agent cannot try to use a tool that was never in its context. You are not hoping the backend says no. You are making sure the agent never asks.</p><div><hr></div><h2>Insight 2: Agents Retry Hard. Design for It.</h2><p>Human users pause, think, and move on. Agents do not.</p><p>If an LLM sends a malformed query and gets an error, it retries - immediately, with a small tweak. If it gets stuck, it can fire 50 requests in three seconds. All of them mutating state.</p><p><strong>An MCP server with no safeguards is a self-inflicted DDoS.</strong></p><p>Three patterns fix this.</p><p><strong>1. Idempotency Keys on Every Mutation</strong></p><p>Agents time out. They drop connections. They retry.</p><p>Borrow Stripe&#8217;s approach: require a unique <code>Idempotency-Key</code> header on any tool call that changes state. If the agent retries <code>scale_dynamodb_capacity</code> after a network blip, the backend sees the same key and returns the cached result. It does not run the operation twice.</p><p>No idempotency keys means no safety net when agents retry.</p><p><strong>2. Rate Limiting Per Session, Not Per IP</strong></p><p>IP-based rate limiting will not help you here. Every agent request comes from the same cloud infrastructure. You cannot tell them apart by IP.</p><p>Limit at the Gateway instead. Use a token-bucket or sliding-window counter, scoped per user session and per tool. One agent cannot flood your backend.</p><p><strong>3. Circuit Breakers and the 428 Pattern</strong></p><p>If an agent fails to use a tool correctly three times in a row, stop it. Trip a circuit breaker at the Gateway. Return a hard error that tells the LLM to stop and ask a human for help.</p><p>For high-stakes actions - dropping a table, issuing a refund, triggering a rollback - use the HTTP <code>428 Precondition Required</code> response. The server sends back a payload that says: a human needs to approve this. The host UI pauses the agent and shows an Approve / Deny button. Nothing happens until a person clicks.</p><p>This is the step most MCP tutorials skip. It is also the step that keeps your production systems safe.</p><div><hr></div><h2>What to Build First</h2><p>The SDKs will not protect you from distributed systems problems. Get these three things right before you ship MCP to production:</p><ul><li><p><strong>One Gateway for auth.</strong> Keep individual MCP servers simple and internal.</p></li><li><p><strong>Filter tools by identity.</strong> Never let an LLM see a tool it cannot use.</p></li><li><p><strong>Add idempotency and circuit breakers.</strong> Agents retry hard. Your infrastructure needs to hold.</p></li></ul><p>Build the control plane first. Everything else scales safely behind it.</p><div><hr></div><p><em>Found this useful? Please like, restack and follow!!</em></p>]]></content:encoded></item></channel></rss>