← the shad0w blog

94.3%, 51.3%, 0%: honest numbers from shadowing a hosted LLM

We put shad0w in front of a hosted LLM through the official OpenAI client and measured how much shad0w answered, how often it agreed with the LLM, how fast, and where it answered nothing at all.

Benchmarks for developer tools usually measure the happy path on the author's laptop. We tried to do better:

Every figure here comes from a result file in the benchmark kit and is inserted by the site build. None is typed by hand.

The three numbers in the title: shad0w answered 94.3% of fresh traffic on a task with 8 intents, 51.3% on a task with 77 intents, and 0% on sentiment. The last one is the right answer.

One term first. α is the most disagreement you allow. α = 5% means shad0w may differ from your LLM on at most 1 in 20 answers it gives. All runs here use α = 5%.

How much shad0w answers depends on how many options you have#

The set-up a user would have: the official openai SDK talks to shad0w proxy, which talks to the LLM. We logged the LLM's answers, trained shad0w, then sent fresh messages shad0w had never seen. To check agreement, we asked the LLM again directly.

With 8 intents shad0w answered nearly everything. With 77 intents it answered about half. More options means fewer messages shad0w is sure about, and more logged answers before it is sure at all. shad0w's answers came back in milliseconds at the client, instead of 1397 ms from the LLM.

It starts at zero#

On BANKING77 (77 options), shad0w answered 0% of traffic after 2,000 logged answers, 40% after 5,000, and 60% after 10,000.

This is the honest version of "set and forget". A task with a few options gets going far sooner. Until then, shad0w just logs and forwards. It costs you nothing, and it saves nothing.

Speed spans almost six orders of magnitude#

Time for one decision, middle value of many runs, on an idle laptop CPU:

Sentiment: 0% is correct behaviour#

On SST-2, shad0w answered 0% and sent every message to the LLM. shad0w reads words, not sarcasm. On its own it gets about 79% of messages right, nowhere near good enough to promise it agrees with the LLM. The safety check saw that and refused to let shad0w answer. Saving nothing is the right outcome when the alternative is silent wrong answers.

How often shad0w differed from the LLM: 3.0–6.0% against a 5% limit#

We set α = 5%: among the answers shad0w gives, it may differ from the LLM on at most 1 in 20. On fresh traffic, how often shad0w actually gave a different answer than the LLM was 3.0–6.0% across tasks. So yes, one task went over 5%.

That is allowed, and here is why. The promise holds with 90% confidence. It is computed on a sample, and a sample can be kind. Roughly one promise in ten may overshoot by design. Our simulation found 0.0–8.1% of certificates exceeded α in simulation (δ allows 10%).

The bigger cause is traffic shift. On CLINC150 the test traffic had about ten times the off-topic share of the logged traffic. The promise covers "traffic like the traffic shad0w was checked on", and this was not. In live use the drift guard flags this after a few hundred decisions and sends those messages to the LLM. The benchmark runs without the guard, so it shows the unguarded number. Lesson: log off-topic traffic at its real rate and always offer an other option.

Against a semantic cache, under the same test#

On BANKING77 we gave shad0w and its alternatives the same LLM answers to learn from, and the same safety check at α = 5%. Everyone had to prove their answers, not just make them.

What we did not measure#

Reproduce#

The benchmark kit has setup and run scripts and a results template. Every figure on this site rebuilds from its output:

bash bench/setup.sh && bash bench/run_all.sh

Commands, hardware and the source file behind every number are on the benchmarks page.

Run it on your own traffic. shad0w proxy --upstream https://api.openai.com/v1 --mode shadow answers nothing itself and counts what shad0w would have done. See zero-code System Zero.
Try it on your own traffic.One line in front of the LLM you already call, or zero code with the proxy. pip install "shad0wllm[compile]"Start in 5 minutes →