Development evidence · 2026-08-02 · bf17de5 · not acceptance evidence
This page publishes what we have measured about speed and answer quality, the arithmetic that turns those measurements into a claim, and the places where we do not yet have a number. Every figure names the run that produced it.
Retrieval quality gain
+71 pts
strict correctness, base 8B, 100 development questions
Less text to read
4.1×
median context vs shell search over the same corpus
Gold source found
28/29
shell search 26/29
Answer, end to end
19.5s
median round trip on the deployed service, 150 questions · 95th percentile 27.7s
The decision this evidence supports. Retrieval is the component doing the work. It moved strict correctness from 15% to 86% on the development matrix. Fine-tuning did not beat the base model in either mode and is not the default offer.
The deployed system, measured. On August 4 we put 150 development questions through the system a customer would use. It scored 107 strictly correct under the first grading rubric and 100 under the second. Neither rubric is validated. It answered 8.7% of questions wrongly under the first and 16% under the second. It declined to answer only 4 questions, because the control that makes it decline unsupported questions is not switched on. The median answer took 19.5 seconds round trip.
An earlier local run. On August 3 we stopped modelling the agent comparison and ran it, on a local development setup rather than the deployed system. Thirty questions went through a real LLM-driven file-search loop and through retrieval, with the same answer model on both sides. Retrieval was faster on every single pair, found the gold document on 29 of 30 questions against the agent's 20, and cost less. Answer correctness was statistically tied. The details are in the sections below.
The things to fix. The deployed service is slow: the median service time is 9.7s and the median round trip is 19.5s. The re-ranking stage averaged 1.6s of that. It cannot simply be switched off, because it also supplies the signal for declining unsupported questions. And the control that uses that signal is not yet switched on.
What this is not. No number here comes from a sealed benchmark. These are visible development sets and live smoke runs. They are honest about direction and not yet strong enough to be an acceptance claim.
Newest evidence
This is the only run on this page that answered through the system a customer would use. The questions are a visible development set, not a sealed test. Graded by an automated judge that has not been validated, under two versions of the rubric.
Strictly correct, rubric 1
107/150
same answers, first rubric
Strictly correct, rubric 2
100/150
same answers, second rubric
Wrong answers
8.7% / 16%
share of all questions, rubric 1 / rubric 2
Declined to answer
4/150
the decline control is switched off
No request failed in 150. The right document was cited on 122 of 150 questions. Service time was 9.7s median and 13.8s at the 95th percentile. The full round trip was 19.5s median and 27.7s at the 95th percentile, with a slowest request of 29.9s. Source: docs/verification/2026-08-04-eval150-deployed-system.md.
Plain language
What you get
A question goes in and a source-backed answer comes out. On the deployed system the median is about 20 seconds round trip. It cited the right document on 122 of 150 development questions. In an earlier local test it handed the model 4.1× less text than a shell search that reads whole files. A real search agent reads less text than that, but pays for it in extra model turns. When we ran one locally, it was slower on every single question and found the right document a third less often. The system does not yet reliably decline when the answer is not in your documents. The control for that exists and is switched off.
What it costs
Time. Searching adds seconds to every answer, and the deployed service is slow today: 9.7s median service time. The re-ranking stage averaged 1.6s of that. Cost per answer in GPU-seconds is not yet measured, and we say so rather than estimating it.
Why not just let an agent grep the folder?
It is a fair question and we measured it rather than arguing about it. Grep is genuinely faster than our retrieval call — about 18× faster on the search step alone. But a shell-search agent has to think three times to get one answer: pick search terms, pick files, then answer. Generating text is the slow part of a language model, roughly 485× slower than reading it. So the extra thinking costs far more than the extra reading, and the two approaches finish within a fifth of a second of each other today.
That near-tie comes from the August 2 development run. The deployed service measured later is much slower than that run, so the speed comparison has not been shown to hold on the production path. The arithmetic for the August 2 run is in bench notes.
High level
A user waits for two things: finding the evidence, then writing the answer. Both arms below run the same model on the same deployment.
Question in, finished answer out
Median over 29 questions · qwen3-4b-tuned — Qwen3-4B base + LoRA adapter on Modal L4
The reranker
The cross-encoder rerank stage measured 4.0–5.3s across the production smoke, against a 4.4s median retrieval call in the search-leg benchmark. Those are different runs, so they do not subtract cleanly. A later run on the deployed service (150 questions, August 4) measured the stage at 1.6s on average, of a 9.7s median service time. Treat the earlier range as superseded. The stage cannot simply be removed, because the control for declining unsupported questions depends on it.
Warm median wall time by arm and model size
Production smoke · 16 requests total, 2 per cell
Retrieval costs 4.2s at 4B and 7.7s at 8B in this smoke. The 4B retrieval arm matched the 8B retrieval arms on both behaviour checks while running about 30% faster. That makes the 4B base model with search the leading production candidate — as a hypothesis for the next run, not a launch claim.
Mid level
Speed only matters if the answer is right. On the 8B development matrix, retrieval is the only component with a large within-model quality signal.
Strict correctness · 100 development questions per arm · Wilson 95%
Base 8B, closed book
15% (15/100)
Base 8B, retrieval
86% (86/100)
Tuned 8B, closed book · overlap
7% (7/100)
Tuned 8B, retrieval · overlap
81% (81/100)
Retrieval moved the base 8B model from 15% to 86% — a 71-point difference on this set. The tuned rows are marked overlap because the development set overlaps tuning exposure, so they cannot support a generalisation claim. They are published to guide experiment design, not to sell fine-tuning. Tuning did not beat the base model in either mode.
A separate leakage-free rescore on a fresh 49-question draw is published with its confidence intervals and a paired significance note on the evaluation screen.
Mid level
The comparison people ask for is grep against vector search. That is the wrong unit. The right unit is the whole path to an answer, because a shell-search agent pays for its search again on every extra model turn.
Below, each line is one cost term, measured as shell search minus retrieval. Bars to the right are where retrieval is ahead.
Which term costs what
Shell search minus retrieval, seconds
Prefill rate
13,585
tokens per second — reading the prompt
Decode rate
28
tokens per second — writing the answer
Ratio
485×
why extra turns cost more than extra context
Reading is nearly free; writing is not. That is why handing the model 4.1× more text costs only 0.6s, while making it generate two more times costs 3.7s. Any estimate that prices context at a decode rate overstates it by two orders of magnitude.
Executed, not modelled · 2026-08-03 · development evidence
Everything above this line is a model built from measured rates. On August 3 the comparison itself was executed: 30 paired questions through a real agent that chooses its own rg searches and file reads, against the same retrieval pipeline, all three arms answering with one equalized hosted model.
| Metric | File-tool agent | Retrieval + reranker | Retrieval, no reranker |
|---|---|---|---|
| Median answer latency | 2.219s | 1.369s | 0.784s |
| Gold document found | 20/30 | 29/30 | 29/30 |
| Strictly correct answers | 17/30 | 17/30 | 19/30 |
| Declined an answerable question | 10/30 | 5/30 | 4/30 |
| Median model calls | 4 | 1 | 1 |
| Answer-model cost, 30 questions | $0.01454 | $0.00926 | $0.00908 |
| Completed without protocol failure | 28/30 | 30/30 | 30/30 |
The paired result is one-sided: the agent was slower than non-reranked retrieval on 28 of 28 usable pairs, a median 1.418s behind. Its retrieval-recall deficit is statistically significant (exact McNemar p = 0.0117); final correctness is not — and the mechanism matters. The file-tool agent's failure mode was silence, not wrong answers: it declined to answer a third of answerable questions, almost always after failing to find the gold document. The reranker was slower on every question, a paired median 0.552s, and bought no measured recall or quality — which is the modelled conclusion above, confirmed by a second, independent method.
Two honest limits. The agent arm is a floor, not a ceiling: a small model under tight turn caps; a stronger agent would buy recall back by paying more per turn. And this ran on local hardware with a hosted answer model — it has not been repeated on the production serving path.
Tables
Search leg · 29 usable of 30 attempted
| Metric | CorpusAegis | Shell search | Note |
|---|---|---|---|
| Gold-document recall | 28/29 | 26/29 | descriptive; visible development set |
| Median context | 8,602 ch | 35,035 ch | 4.1× less text |
| Median context tokens | ~2,150 | ~8,759 | 4-chars-per-token proxy |
| Search-stage latency | 4.4s | 0.24s | not like-for-like: network vs local |
| Round trips before generation | 1 model turn | 3+ model turns | structural, not an executed agent loop |
| End to end | 8.57s | 8.73s | measured rates, declared turn structure |
Production smoke · 16 requests total, 2 per cell
| Arm | Size | Cited gold source | Declined unsupported | Median latency |
|---|---|---|---|---|
| Base | 4B | n/a | 0/1 | 3.8s |
| Base | 8B | n/a | 1/1 | 3.8s |
| Base + search | 4B | 1/1 | 1/1 | 8s |
| Base + search | 8B | 1/1 | 1/1 | 11.5s |
| Tuned | 4B | n/a | 0/1 | 2.9s |
| Tuned | 8B | n/a | 0/1 | 4.7s |
| Tuned + search | 4B | 1/1 | 1/1 | 8.4s |
| Tuned + search | 8B | 1/1 | 1/1 | 12.1s |
Development matrix · 8B · 100 questions per arm
| Configuration | Strict correct | Wilson 95% | Evidence use |
|---|---|---|---|
| Base 8B, closed book | 15/100 | 9.3–23.3% | development evidence |
| Base 8B, retrieval | 86/100 | 77.9–91.5% | development evidence |
| Tuned 8B, closed book | 7/100 | 3.4–13.8% | generalisation claim blocked by development overlap |
| Tuned 8B, retrieval | 81/100 | 72.2–87.5% | generalisation claim blocked by development overlap |
Deep detail
Everything above rests on an arithmetic model with two unmeasured inputs. This section states the model, shows how far the conclusion moves when those inputs move, and names what would settle it. It is deliberately text-first: the charts here are for people who intend to check the reasoning.
A calibration sweep asks the live deployment the same question at six increasing context sizes plus six closed-book probes, reads generation milliseconds and token counts off the service meter, and fits three parameters jointly by least squares: fixed per-call overhead, milliseconds per prompt token, and milliseconds per completion token.
An earlier version fitted these in sequence — decode from the closed-book probes, prefill from the residual. It was unstable: the same deployment returned 3,743, then 9,567, then 9,090, then 15,748 tokens per second of prefill on consecutive runs, while decode never moved. Overhead and prefill were absorbing each other. The joint fit that replaced it repeats to within 1.6%. Its solver is covered by unit tests that check it recovers known coefficients exactly, keeps prefill the faster direction under noise, and returns nothing rather than a number when the probes did not vary enough to identify a rate.
| Prefill | 13,585 tok/s |
| Decode | 28 tok/s |
| Fixed cost per call | 80 ms |
| Probes | 12 |
| Calibrated prompt range | 213–3,552 tokens |
Writing shell search minus retrieval, in seconds:
net = search delta + context + 2 × overhead + T / d
= −4.16 + 0.60 + 0.16 + T / dT is the number of tokens the agent generates across its two extra turns. d is the decode rate. Everything else is measured. The ledger in the section above is this function at the declared T = 100 and the measured d = 28.
Neither T nor d is a constant of nature. T has never been counted on a real agent loop — the harness does not run one. d is our L4 deployment. Both charts below exist because a single number here would be a guess wearing a decimal point.
Sensitivity to agent verbosity
How far retrieval is ahead, against tokens the agent generates in its extra turns · log scale · at the measured 28 tok/s
Sensitivity to serving speed
Net advantage against decode rate, at T = 400 tokens. Above zero, retrieval wins.
Remove the reranker and retrieval falls to roughly 0.40s. The search-leg term goes from −4.16s to −0.16s and the model reduces to:
net = context + T / d
Both terms are non-negative by construction. Context can only be zero or positive; T / d is positive for any agent that emits anything at all. So with the reranker removed, shell search loses end to end for every value of T, every decode rate, and every prefill rate. No unmeasured parameter can flip it.
In the August 2 run, the near-tie was not evidence that grep competes with retrieval. It was evidence that one stage of our own pipeline cost about as much as an entire agent loop. The August 4 deployed run measured that stage at a much smaller 1.6s, so this argument is unproven for the deployed system.
Measured. Both token rates. Fixed per-call cost. Context sizes. Question lengths. Grep output size. Search times. Recall.
Declared, and chosen to favour shell search. Three turns. A 400-token system prompt. 400 tokens of tool schemas. 40 tool-call tokens on the first turn, 12 per file read on the second. A real harness carries more of each and usually takes more than three turns, so the shell total is a floor.
Extrapolated. The shell arm’s largest turn prompt is 8,819 tokens. The calibration sweep reaches 3,552. That one line is an extrapolation, and the harness prints a warning where it happens. The conclusion above is deliberately stated in a form that does not use the prefill rate at all.
Prefix caching. The harness computes a cached and an uncached shell total, because caching decides which is real. vLLM caches by default, so the cached — smaller — number is the one quoted. The compounding survives the assumption favourable to shell search. Cache-hit rate was not recorded.
A paired 150-question experiment compared a plain answer prompt against a source-restricted prompt on gemini-2.5-flash-lite (laboratory, not the shipped service). Strict correctness and the absolute number of fully supported answers were identical. It is published because a null result is still evidence, and because the supported-answer “improvement” it appeared to show was an artefact.
Plain prompt versus source-restricted prompt
Paired, same 150 questions · gemini-2.5-flash-lite (laboratory, not the shipped service)
An omission a reader can see is not a claim. These are the fields a complete proof artifact needs, and how far the runs so far fill them.
| Field | Status |
|---|---|
| Cost per correct-and-supported answer | Answer-model cost per strict-correct answer was measured on Aug 3 in the development run. Indexing, retrieval compute, and hosting are still not priced in. |
| Answer quality in the shell-search arm | Graded on Aug 3 by a reference judge: statistically tied with retrieval. Blinded human adjudication has not happened. |
| Real agent-loop token counts | Measured on Aug 3: the executed agent used a median 3,370 cumulative prompt tokens over 4 calls — far below the modeled worst case. Unmeasured on the production Qwen path. |
| Per-request stage timings and prefix-cache hit rate | Not recorded. vLLM exposes queue, prefill, time-to-first-token and decode; per-request metrics are off in the current launch command. |
| Memory use on target hardware | Not published for this run. |
| Sealed acceptance evidence | None on this page. Every number here comes from a visible development set or a live smoke. |
| Request reliability | 1 of 30 live retrieval requests returned a non-200, undiagnosed. In the Aug 3 run the file-tool agent lost 2 of 30 attempts to its multi-turn protocol; both retrieval arms went 30 for 30. |
| Declining questions the documents cannot answer | The control that makes the deployed system decline unsupported questions exists and is switched off. In the August 4 run it declined 4 of 150 questions. |
| Validated grading | Answers were graded by an automated judge against a written rubric. Neither has been validated by people. The same 150 answers score 107 strict under rubric version 1 and 100 under version 2. |
| Production replication of the agent-loop result | The Aug 3 end-to-end win is a local development run on a hosted answer model. It has not been repeated on the Qwen/Modal production path or a customer corpus. |
| Repository evidence | bf17de5d1285ae0e99b573d1cdf92db4be9ea5de |
| Run date | 2026-08-02 |
| Harness | scripts/search_bench.py |
| Corpus | 236 Markdown files (development corpus, artifact1) |
| Answer model | qwen3-4b-tuned — Qwen3-4B base + LoRA adapter |
| Hardware | Modal L4 |
| Serving | vLLM, prefix caching at its default |
| Reproduce | .venv/bin/python scripts/search_bench.py --n 30 --top-k 5 |
| Executed agent-loop run | 2026-08-03 · scripts/agent_search_bench.py · gemini-2.5-flash-lite (equalized across all three arms) |