<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>RAG on Nikhil Adhikari</title><link>https://kai2055.github.io/tags/rag/</link><description>Recent content in RAG on Nikhil Adhikari</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Sat, 25 Jul 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://kai2055.github.io/tags/rag/index.xml" rel="self" type="application/rss+xml"/><item><title>Confident Matches on Vocabulary, Not Mechanism</title><link>https://kai2055.github.io/p/vocabulary-not-mechanism/</link><pubDate>Sat, 25 Jul 2026 00:00:00 +0200</pubDate><guid>https://kai2055.github.io/p/vocabulary-not-mechanism/</guid><description>
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;The short version, for anyone:&lt;/strong&gt; The system finds past incidents by comparing
&lt;em&gt;meaning&lt;/em&gt;, but sometimes two very different failures use the same words —
&amp;ldquo;Cloudflare,&amp;rdquo; &amp;ldquo;edge,&amp;rdquo; &amp;ldquo;database&amp;rdquo; — and it confidently returns the wrong one. This
is the most dangerous kind of error: not a crash, not an obvious miss, but a
confident wrong answer that would send an engineer down the wrong path during a
live outage. Rather than hide it or fake a better score by deleting the test, I&amp;rsquo;ve
kept it visible and documented — because knowing exactly where a reliability
system fails &lt;em&gt;is&lt;/em&gt; the reliability work, and this particular fix is real design
work, not a quick tuning tweak.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Full finding below.&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Where it shows:&lt;/strong&gt; Layer 1 retrieval and Layer 2 diagnosis — the same failure,
twice. &lt;strong&gt;Status:&lt;/strong&gt; Understood, not fixed. Kept visible on purpose.&lt;/p&gt;
&lt;h2 id="the-short-version"&gt;The short version
&lt;/h2&gt;&lt;p&gt;The system sometimes matches on shared words rather than shared cause, and does it
with high confidence. A query and an incident can use the same vocabulary while
describing completely different failures. The embedding scores them as close, the
system returns the wrong incident, and nothing about the score signals that it&amp;rsquo;s
wrong. This is the failure the whole project exists to guard against: not a crash,
not an obvious miss, but a confident wrong answer that would mislead an on-call
engineer during a live outage.&lt;/p&gt;
&lt;h2 id="instance-1--layer-1-retrieval-the-ddos-probe"&gt;Instance 1 — Layer 1 retrieval: the DDoS probe
&lt;/h2&gt;&lt;p&gt;A no-match probe: &lt;em&gt;&amp;ldquo;DDoS attack overwhelmed our CDN edge nodes and caused a 12-hour
outage.&amp;rdquo;&lt;/em&gt; This is meant to retrieve nothing — the corpus has no DDoS incident.
Against the 15-document corpus it correctly declined. Against the 20-document corpus
it now matches a Cloudflare incident at distance &lt;strong&gt;0.236&lt;/strong&gt; — well inside the 0.30
threshold, a confident hit.&lt;/p&gt;
&lt;p&gt;But that Cloudflare incident is a &lt;strong&gt;configuration-error&lt;/strong&gt; incident: a database
access-control change that cascaded. It has nothing to do with a DDoS, which is a
volumetric attack. The match is on surface vocabulary — &amp;ldquo;Cloudflare,&amp;rdquo; &amp;ldquo;edge,&amp;rdquo;
&amp;ldquo;outage&amp;rdquo; — not on the failure mechanism. The probe stays classified as a no-match;
its continued matching is the finding, not something to reclassify away.&lt;/p&gt;
&lt;h2 id="instance-2--layer-2-diagnosis-the-xid-wraparound-attractor"&gt;Instance 2 — Layer 2 diagnosis: the XID-wraparound attractor
&lt;/h2&gt;&lt;p&gt;In the Layer 2 baseline, three descriptions produced a diagnosis of Postgres
transaction-ID (XID) wraparound. Two were correct (a real Sentry Postgres incident).
The third, L2-007, was &lt;strong&gt;wrong&lt;/strong&gt; — it&amp;rsquo;s Roblox&amp;rsquo;s service-registry cascade, nothing to
do with Postgres. But it shares symptom vocabulary — read-only, cascade, database —
with the Sentry incident, and the model reached for the specific,
authoritative-sounding failure it had seen before.&lt;/p&gt;
&lt;h2 id="why-these-are-the-same-failure"&gt;Why these are the same failure
&lt;/h2&gt;&lt;p&gt;Both are the system latching onto a &lt;strong&gt;specific, plausible, well-documented failure&lt;/strong&gt;
because the surface features match, while the actual mechanism does not. The danger
in both is the &lt;strong&gt;confidence&lt;/strong&gt;. A vague wrong answer is easy to distrust. &amp;ldquo;PostgreSQL
XID wraparound&amp;rdquo; and a 0.236 distance both look authoritative. During an incident,
that&amp;rsquo;s worse than silence — it&amp;rsquo;s a false lead delivered with conviction.&lt;/p&gt;
&lt;h2 id="why-it-is-being-kept-visible-rather-than-patched"&gt;Why it is being kept visible rather than patched
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;It is honest signal.&lt;/strong&gt; The Layer 1 decline rate is 0.500 — three of four no-match
probes decline correctly, and the DDoS probe is the one that doesn&amp;rsquo;t. Forcing that
number to look better by deleting the probe would hide a real property of the system.
The metric is more useful with the known failure inside it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The fix is not local.&lt;/strong&gt; This isn&amp;rsquo;t a threshold to nudge or a prompt line to add.
It&amp;rsquo;s a limitation of matching on embedding similarity and symptom vocabulary.
Addressing it properly means giving the system more to discriminate on — richer
context at retrieval time, or a verification step that checks whether the mechanism
actually fits before returning a confident answer. That&amp;rsquo;s real design work, recorded
here as the direction rather than attempted as a patch.&lt;/p&gt;
&lt;h2 id="what-would-actually-address-it"&gt;What would actually address it
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Retrieval:&lt;/strong&gt; search richer text so the match rests on more than a few shared
nouns. More context gives the embedding more to separate genuinely-similar
incidents from merely-similarly-worded ones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt; a mechanism-check before a candidate is returned with high
confidence — does the cited incident&amp;rsquo;s actual failure mode match the symptoms, or
only their vocabulary? This is a grounding step one level deeper than
citation-checking: not &amp;ldquo;is this incident real and retrieved,&amp;rdquo; but &amp;ldquo;does this
incident actually &lt;em&gt;explain&lt;/em&gt; what was reported.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both are deferred. Both are the honest fix. Neither is a tuning change.&lt;/p&gt;</description></item><item><title>The Layer 2 Baseline, and a Grounding Filter That Leaked</title><link>https://kai2055.github.io/p/layer2-baseline-grounding-filter/</link><pubDate>Fri, 24 Jul 2026 00:00:00 +0200</pubDate><guid>https://kai2055.github.io/p/layer2-baseline-grounding-filter/</guid><description>
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;The short version, for anyone:&lt;/strong&gt; Before you can detect whether a system is
&lt;em&gt;getting worse&lt;/em&gt;, you need a measured snapshot of how good it is &lt;em&gt;now&lt;/em&gt; — a baseline.
Building that baseline for the diagnostic agent immediately caught a real bug that
30 existing unit tests had missed: a safeguard meant to strip out &amp;ldquo;made-up&amp;rdquo;
citations was letting them through, as long as at least one &lt;em&gt;real&lt;/em&gt; citation rode
alongside. One genuine reference could smuggle in any number of invented ones. The
lesson: &lt;em&gt;unit tests check the cases you thought of; measuring against real output
catches the ones you didn&amp;rsquo;t.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Full finding below.&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Measured on:&lt;/strong&gt; 15 descriptions through the full agent · &lt;strong&gt;Result:&lt;/strong&gt; first measured
Layer 2 baseline; one grounding bug found and fixed.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why this matters
&lt;/h2&gt;&lt;p&gt;Layer 2 had 30 unit tests proving the machinery worked, and one integration run
judged by eye. It had no numbers. You cannot build a regression gate — which detects
regressions by comparing against a baseline — without a baseline to compare to. This
is that baseline. Building it also caught a real bug the unit tests had missed.&lt;/p&gt;
&lt;h2 id="the-bug-the-baseline-caught"&gt;The bug the baseline caught
&lt;/h2&gt;&lt;p&gt;The first run reported &lt;strong&gt;2 grounding violations&lt;/strong&gt; — candidates citing incident IDs
that were never retrieved. That number is supposed to be impossible by construction,
so the tripwire fired.&lt;/p&gt;
&lt;h3 id="why-it-slipped-through"&gt;Why it slipped through
&lt;/h3&gt;&lt;p&gt;The grounding filter kept a candidate if &lt;strong&gt;any&lt;/strong&gt; cited ID was real:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cited &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#f92672"&gt;...&lt;/span&gt;} &lt;span style="color:#75715e"&gt;# every id the model cited&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; cited &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; valid_ids: &lt;span style="color:#75715e"&gt;# at least one is real?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grounded&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(d) &lt;span style="color:#75715e"&gt;# keep the whole thing, evidence unchanged&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So a candidate citing &lt;code&gt;aws-s3-2017-02-28, gitlab-2017-01-31&lt;/code&gt; passed — aws-s3 is real,
the intersection is non-empty — and carried the fabricated gitlab citation through
untouched. &lt;strong&gt;The filter checked that at least one citation was grounded. It never
checked that every citation was.&lt;/strong&gt; One real id smuggled in any number of invented
ones.&lt;/p&gt;
&lt;h3 id="the-fix"&gt;The fix
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;real &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cited &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; valid_ids
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; real:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt; &lt;span style="color:#75715e"&gt;# nothing grounds this, drop it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;d[&lt;span style="color:#e6db74"&gt;&amp;#34;evidence&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(sorted(real)) &lt;span style="color:#75715e"&gt;# keep only the real citations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;grounded&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(d)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A candidate now survives only if it has a real citation, and its evidence is
rewritten to contain &lt;em&gt;only&lt;/em&gt; real citations. Fabricated ids are stripped, not
tolerated. Covered by a new test that feeds one real and one fake id and asserts only
the real one remains.&lt;/p&gt;
&lt;h3 id="why-the-unit-tests-missed-it"&gt;Why the unit tests missed it
&lt;/h3&gt;&lt;p&gt;The existing grounding test used candidates that were &lt;em&gt;entirely&lt;/em&gt; fabricated — those
were correctly dropped. The gap was the &lt;em&gt;mixed&lt;/em&gt; case: one real citation plus one
fake. No test exercised it, so nothing failed. &lt;strong&gt;The scoring run on real model output
was the first thing to hit it. That&amp;rsquo;s the point of scoring against real output: unit
tests check the cases you thought of, evaluation catches the ones you didn&amp;rsquo;t.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="the-baseline-after-the-fix"&gt;The baseline (after the fix)
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Metric&lt;/th&gt;
 &lt;th&gt;Value&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;top-1 accuracy&lt;/td&gt;
 &lt;td&gt;0.625 (5 of 8 with a primary cause)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;any-hit rate&lt;/td&gt;
 &lt;td&gt;0.615&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;noise rate&lt;/td&gt;
 &lt;td&gt;0.444&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;decline rate&lt;/td&gt;
 &lt;td&gt;1.000 (2 of 2 no-match entries)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;mean candidates&lt;/td&gt;
 &lt;td&gt;1.80&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;grounding violations&lt;/td&gt;
 &lt;td&gt;0&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;mean iterations&lt;/td&gt;
 &lt;td&gt;2.20&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;No targets were set in advance — the first clean run &lt;em&gt;is&lt;/em&gt; the baseline, so the gate
enforces &amp;ldquo;do not fall below this,&amp;rdquo; not an invented number. The one exception is
grounding violations, which isn&amp;rsquo;t a target but an invariant: it must be zero.&lt;/p&gt;
&lt;h2 id="reading-it"&gt;Reading it
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;What works:&lt;/strong&gt; decline rate is perfect — the reliability behaviour (declining rather
than inventing) holds. Mean candidates fell from 5 to 1.8; the list no longer pads.
Grounding is clean and now provably so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What doesn&amp;rsquo;t:&lt;/strong&gt; noise rate 0.444 is high, but &lt;em&gt;concentrated, not spread&lt;/em&gt; — two
specific descriptions (L2-003, L2-007) produce almost all the noise in the whole
suite. Every other entry is clean or nearly so. So the problem is two descriptions
the agent handles badly, not a diffuse quality issue — a located problem to work on
rather than a general sense that quality is mediocre.&lt;/p&gt;
&lt;h2 id="what-this-unblocks"&gt;What this unblocks
&lt;/h2&gt;&lt;p&gt;Layer 2 now has a committed baseline across seven metrics and a clean grounding
invariant. The regression gate has something to regress against — and two concrete,
located problems to work on: the noise concentrated in L2-003 and L2-007, and the
XID-wraparound attractor (documented separately).&lt;/p&gt;</description></item><item><title>The three layers where ML systems fail</title><link>https://kai2055.github.io/p/the-three-layers-where-ml-systems-fail/</link><pubDate>Thu, 12 Feb 2026 00:00:00 +0100</pubDate><guid>https://kai2055.github.io/p/the-three-layers-where-ml-systems-fail/</guid><description>&lt;p&gt;Most ML portfolios show a model that runs once. The interesting engineering is
in the part that comes after: keeping it running. In my experience, ML systems
fail at three predictable points.&lt;/p&gt;
&lt;h2 id="1-bad-data-gets-in"&gt;1. Bad data gets in
&lt;/h2&gt;&lt;p&gt;The model trains or scores on corrupt input and nobody notices until the numbers
are wrong three steps downstream. The fix is boring and essential: validate data
&lt;em&gt;before&lt;/em&gt; it reaches the model — missing values, duplicates, malformed headers,
schema drift.&lt;/p&gt;
&lt;h2 id="2-the-model-drifts-in-production"&gt;2. The model drifts in production
&lt;/h2&gt;&lt;p&gt;A model that passed every test on launch day quietly degrades as the world
changes underneath it. The first sign shouldn&amp;rsquo;t be an angry user — it should be
a metric. That means monitoring distribution shift (PSI, Wasserstein) and gating
deployments on it.&lt;/p&gt;
&lt;h2 id="3-the-same-failure-repeats"&gt;3. The same failure repeats
&lt;/h2&gt;&lt;p&gt;An outage happens, someone writes a post-mortem, it gets buried in a wiki, and
six months later the same thing happens again. Turning past failures into
&lt;em&gt;searchable, deployment-gating&lt;/em&gt; feedback is the third layer — and the one almost
nobody builds.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;One principle — reliability — across three layers: data, model, system. That&amp;rsquo;s
the thread running through everything on my &lt;a class="link" href="https://kai2055.github.io/projects/" &gt;projects&lt;/a&gt; page.&lt;/p&gt;</description></item></channel></rss>