<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://cuibuaa.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://cuibuaa.github.io/" rel="alternate" type="text/html" hreflang="en"/><updated>2026-08-03T09:14:31+00:00</updated><id>https://cuibuaa.github.io/feed.xml</id><title type="html">blank</title><subtitle>Qian Cui — Senior Applied Scientist &amp;amp; Tech Lead. Agentic AI, LLM/SLM systems, and security ML: building agents that are honest about what they do not know. </subtitle><entry><title type="html">When a 3B model beats the frontier on private-domain RAG (and when it doesn’t)</title><link href="https://cuibuaa.github.io/blog/2026/beating-the-frontier-private-domain-rag/" rel="alternate" type="text/html" title="When a 3B model beats the frontier on private-domain RAG (and when it doesn’t)"/><published>2026-08-02T00:00:00+00:00</published><updated>2026-08-02T00:00:00+00:00</updated><id>https://cuibuaa.github.io/blog/2026/beating-the-frontier-private-domain-rag</id><content type="html" xml:base="https://cuibuaa.github.io/blog/2026/beating-the-frontier-private-domain-rag/"><![CDATA[<h2 id="tldr">TL;DR</h2> <p>On a private domain, a frontier model — a top-end general-purpose model — has no memorized advantage. It has to read the retrieved documents, same as a small model. That’s the RAG setup: retrieve relevant documents, then ask a model to answer from them. So I asked a narrow question: <strong>can I make a local 3B model competitive with the frontier on private-domain RAG?</strong> The honest answer:</p> <ul> <li><strong>With clean retrieval — context without irrelevant distractors — the adapted 3B beats the honestly-measured frontier, 82.3% vs 77.0%</strong> — at ~1/5 the serving cost, ~94 ms locally, fully on-prem.</li> <li><strong>With noisy retrieval — context that includes those distractors — it’s a near-tie (75.2% vs 76.4%), frontier marginally ahead</strong> — the small model wins on cost/latency/privacy, not accuracy. I’m not going to pretend otherwise.</li> <li>The lever wasn’t clever routing (that failed) and it wasn’t model size (a tuned 3B matched a 7B). It was <strong>domain adaptation</strong> — fine-tuning on examples from the target domain — plus the discipline of <strong>measuring the frontier honestly</strong>. Its headline RAG number turned out to be inflated by public-benchmark leakage: answers from memorized public facts, not just the retrieved context.</li> </ul> <p>The headline clean/noisy experiment below uses PopQA-440 as a public stand-in for a private corpus. The routing and multi-hop sections use their own named benchmarks.</p> <h2 id="the-question-and-why-the-obvious-framings-mislead">The question, and why the obvious framings mislead</h2> <p>Enterprise RAG over a private corpus wants high accuracy at low cost and latency, on-prem. The instinct is “use the frontier model, it’s smarter.” But on data it has never seen, <em>smarter</em> mostly means <em>has more world knowledge</em> — and world knowledge is exactly what a private corpus doesn’t reward. Both models live or die by retrieval.</p> <p>So the real question is narrow and testable: <strong>for single-hop extraction — answering from one retrieved source — can adaptation make a local small language model (SLM) competitive with a frontier model restricted to that same context?</strong> I answered it by following the evidence, and two of the most useful findings were things that <em>didn’t</em> work.</p> <h2 id="detour-1-adaptive-routing-doesnt-deliver-a-useful-negative">Detour 1: adaptive routing doesn’t deliver (a useful negative)</h2> <p>The trendy answer is <em>routing</em>: send easy queries to a cheap model, hard ones to the frontier, spend compute by difficulty. The oracle looks amazing. Here, the <em>oracle</em> is a hindsight-only router: it already knows which models answered each query correctly, then picks the cheapest correct one. That foreknowledge makes it a useful but cheating upper bound, not a deployable system. On RouterBench, this oracle chooses across 11 models and hits 95.4% at ~¼ the cost of the best single model. The <em>oracle advantage</em> is that ideal cost-versus-quality gain over sticking with one fixed model.</p> <p>Then I actually built the router and audited the <em>decision</em> in two ways. <em>Calibration</em> asks whether its confidence numbers are on the right scale. <em>Discrimination</em>, or predictability, asks whether cheap features can tell which tier will be right. Here, <em>capture</em> means the share of the oracle advantage a real router recovers without hindsight. A realistic calibrated router captures only <strong>5.6%</strong> of that oracle advantage with a weak cheap tier, and <strong>35.8%</strong> even with a strong one. Most of the apparent upside vanishes once the router has to choose without seeing the answer.</p> <p>The bottleneck isn’t calibration. Putting confidence on the right scale cannot create a missing signal. The hard part is discrimination: can the cheap features tell which tier will be right? And the savings you <em>can</em> reach are unsafe. Cutting serving cost 5× raises the served <em>confidently-wrong</em> rate from 0.1% to 33.5%.</p> <p>I even tried the appealing fix — generate with the cheap model, then use a groundedness verifier, which checks whether the retrieved context supports the answer, to escalate the unsupported ones. It <strong>failed</strong>: its AUROC, a threshold-independent measure of how well the score separates correct answers from incorrect ones, was 44.8. Groundedness is not correctness: a cheap answer can be perfectly faithful to a stale or wrong retrieved page. <strong>Reporting that refutation of my own idea is the point</strong> — it’s what told me routing was a dead end and the lever had to be elsewhere.</p> <h2 id="the-unlock-measure-the-frontier-honestly">The unlock: measure the frontier honestly</h2> <p>Here’s the finding that reframed everything. Frontier-RAG is usually reported with a prompt that lets the model use <em>its own knowledge in addition to</em> the retrieved context. On PopQA that scores <strong>90.5%</strong>. But restrict it to reading <em>only the retrieved page</em> — its true behavior on data it never memorized — and it drops to <strong>77.0%</strong>.</p> <p><strong>13.5 of those 90.5 points were public-benchmark leakage, not reading.</strong> On a genuinely private corpus, the 90.5 number simply doesn’t exist.</p> <p>So 77.0% is the honest baseline — and, tellingly, it’s the same score an off-the-shelf 3B gets reading the same page (77.1%). Once you remove world knowledge, the frontier has no comprehension edge on clean single-hop extraction.</p> <p>If you take one thing from this post: <strong>measure your baseline on the data it will actually face, not the public knowledge it happens to have memorized.</strong></p> <h2 id="the-method-adapt-the-small-model">The method: adapt the small model</h2> <p>Nothing exotic — the contribution is the honest, measured integration, not a new algorithm:</p> <ol> <li><strong>Domain retrieval</strong> over the corpus.</li> <li><strong>Retrieval-Augmented Fine-Tuning (RAFT)</strong> — train the SLM on (retrieved context, question) → answer, loss on the answer tokens only, so it learns to <em>extract from your documents</em>, not recite them.</li> <li><strong>Distractor augmentation</strong> — mix irrelevant retrieved docs into the training examples. That way, robustness to noisy retrieval is <em>learned</em>, not assumed.</li> <li><strong>A groundedness abstention guard at serving time</strong> — flag answers the context doesn’t support so the system abstains instead of asserting (used for abstention, <em>not</em> as the correctness router that failed above).</li> </ol> <h2 id="results-clean-beats-noisy-ties">Results: clean beats, noisy ties</h2> <table> <thead> <tr> <th>configuration</th> <th style="text-align: right">clean</th> <th style="text-align: right">noisy</th> <th style="text-align: right">latency</th> </tr> </thead> <tbody> <tr> <td>3B off-the-shelf + retrieval</td> <td style="text-align: right">77.1</td> <td style="text-align: right">67.3</td> <td style="text-align: right">65 ms</td> </tr> <tr> <td>3B + RAFT</td> <td style="text-align: right">81.8</td> <td style="text-align: right">73.9</td> <td style="text-align: right">80 ms</td> </tr> <tr> <td><strong>3B + distractor-RAFT</strong></td> <td style="text-align: right"><strong>82.3</strong></td> <td style="text-align: right"><strong>75.2</strong></td> <td style="text-align: right">94 ms</td> </tr> <tr> <td>7B off-the-shelf + retrieval</td> <td style="text-align: right">82.3</td> <td style="text-align: right">71.8</td> <td style="text-align: right">139 ms</td> </tr> <tr> <td>7B + RAFT</td> <td style="text-align: right">81.8</td> <td style="text-align: right">75.5</td> <td style="text-align: right">143 ms</td> </tr> <tr> <td>frontier-RAG <strong>strict (honest)</strong></td> <td style="text-align: right"><strong>77.0</strong></td> <td style="text-align: right"><strong>76.4</strong></td> <td style="text-align: right">API</td> </tr> <tr> <td>frontier-RAG hybrid (public leak)</td> <td style="text-align: right">90.5</td> <td style="text-align: right">—</td> <td style="text-align: right">API</td> </tr> </tbody> </table> <p>Three things I didn’t expect to be so clean:</p> <ul> <li><strong>Clean retrieval: the adapted 3B beats the honest frontier</strong>, 82.3 vs 77.0.</li> <li><strong>Domain adaptation substitutes for scale here</strong> — the tuned 3B (82.3) matches an off-the-shelf 7B (82.3), at 94 ms vs 139 ms. Adapting the small model beat using a bigger one.</li> <li><strong>Robustness to noise is a <em>training</em> property, not a size one.</strong> Under noisy retrieval, an off-the-shelf 3B falls to 67 and an off-the-shelf 7B reaches 72. Distractor training lifts that same small model to 75, past the larger off-the-shelf model. Training beat scale in that comparison. That’s what buys the noisy near-tie.</li> </ul> <p>And it’s not a lucky seed: retrained on three seeds, the RAFT 3B is <strong>82.5 ± 0.67</strong>, every seed above 77.0.</p> <p>I also worried it had just memorized PopQA’s ~10 question templates, so I ran a paraphrase check. Most questions were rewritten while keeping the entity, context, and answer fixed. Across original, natural, and telegraphic wording, accuracy stayed flat (82.3 / 81.4 / 83.0). It learned to read, not to pattern-match the template.</p> <h2 id="wheres-the-ceiling-really-the-part-im-most-glad-i-checked">Where’s the ceiling, really? (the part I’m most glad I checked)</h2> <p>It would’ve been easy to stop at “3B beats frontier, ship it.” Instead I asked where the system actually breaks. The answer split cleanly in two — <strong>and neither is the size of the reader model</strong>, meaning the model that reads the retrieved documents and produces the answer, not the person reading this post.</p> <p><strong>Reasoning.</strong> Holding the context fixed and only varying the reader model, single-hop is near its measured ceiling on this proxy (a tuned 3B = a 7B). But multi-hop composition — combining multiple pieces of evidence — is a cliff: the 3B gets <strong>12–17%</strong> on MuSiQue, and scaling to 7B only reaches 20–30%. Scale helps, but it does not close the gap. These results support escalating multi-hop cases; they do not establish a decomposition recipe.</p> <p><strong>Retrieval.</strong> My earlier ablation removed retrieval errors: I handed the reader model its gold page, the source page intended for the question. With a real retriever, <em>answer-recall</em> means the share of queries whose retrieved text contains the answer. Full-system accuracy cannot exceed answer-recall, which looked stuck at ~90%. If retrieval never supplies the answer, even a perfect reader model has nothing to extract.</p> <p>So I dug in. <em>Doc-recall</em>, by contrast, means the share of queries for which the intended source document is retrieved. It was ~100%, meaning <em>the right page was retrieved but its text didn’t contain the answer.</em> The cause was mundane and fixable: my corpus builder used intro-only extracts, dropping Wikipedia infoboxes and the rest of each article. I ingested infoboxes plus full text, and answer-recall jumped <strong>90.9 → 95.4</strong>. The “ceiling” was a corpus-construction artifact, not a retrieval limit — only ~1.8% of queries were genuine retrieval failures. <em>Debug the metric before you accept it.</em></p> <h2 id="what-id-tell-a-practitioner">What I’d tell a practitioner</h2> <ul> <li>On this proxy’s single-hop task, a well-adapted 3B is near the measured reader ceiling. <strong>Spend first on retrieval quality and fine-tuning on your own Q&amp;A, before buying a bigger reader model.</strong></li> <li><strong>Measure the frontier honestly</strong> (context-only), or you’ll compare against a number that won’t survive contact with your private data.</li> <li><strong>Escalate known reasoning-hard cases, don’t upgrade everything.</strong> Multi-hop queries go up a tier; do not use the cheap confidence signals that failed above as the trigger.</li> <li><strong>Report what didn’t work.</strong> The routing refutation and the corpus-artifact debugging were more useful than any single accuracy number.</li> </ul> <h2 id="honest-limitations">Honest limitations</h2> <p>PopQA is a public proxy for a private domain (its 90.5 hybrid number wouldn’t exist on a real internal corpus). Under noisy retrieval the frontier is marginally ahead on <em>accuracy</em>; the SLM’s win there is cost/latency/privacy. Costs are nominal relative units; latency compares local-GPU-batched vs an API round-trip. Only the headline 3B-RAFT cell is multi-seed. Multi-hop reasoning remains the SLM’s real weakness.</p> <h2 id="code--paper">Code + paper</h2> <p>Everything here is reproducible — every number in this post regenerates from the committed scripts and results.</p> <ul> <li><strong>Repo: <a href="https://github.com/cuibuaa/private-domain-rag">github.com/cuibuaa/private-domain-rag</a></strong> — the scripts, the training/eval harness, and the committed <code class="language-plaintext highlighter-rouge">results_*.json</code> behind every figure.</li> <li><strong><a href="https://github.com/cuibuaa/private-domain-rag/blob/main/paper/PAPER.md">Paper</a></strong> — the rigorous write-up, with each claim traced in the <a href="https://github.com/cuibuaa/private-domain-rag/blob/main/paper/verification_report.md">verification report</a>.</li> <li><strong><a href="https://github.com/cuibuaa/private-domain-rag/tree/main/groundedness-guardrail">Sibling study</a></strong> — the distilled RAG-groundedness guardrail (ModernBERT student, 58.89 min-AUROC vs LettuceDetect’s 57.3) that provides the abstention guard.</li> </ul>]]></content><author><name></name></author><category term="engineering"/><category term="rag"/><category term="retrieval"/><category term="slm"/><category term="evaluation"/><category term="agents"/><summary type="html"><![CDATA[An honest engineering exploration - what actually moves the needle for retrieval-augmented QA on data a frontier model has never seen, and where the ceilings really are.]]></summary></entry></feed>