TL;DR

Constraining outputs to a JSON schema lifts schema-valid rate by 9–22 points across all eleven models. Once you do that, a 24B model matches a 70B one on exact-match. Latency, not accuracy, becomes the differentiator.

Every agent framework we have deployed for clients this year failed in the same, boring way: not because the model reasoned badly, but because it emitted a tool call the runtime could not parse. A missing quote. A stray markdown fence. An enum value that almost matched. We wanted a number on how often this happens, per model, under a protocol we could rerun in six months.

So we built a harness. This article is the first published run. We will re-run it quarterly and keep the raw .parquet files in the repo.

Protocol

Protocol · v1.0 · frozen 28 Aug 2026
Models
11 open-weight, 7B–70B, instruct variants
Hardware
2× H100 80GB · vLLM 0.9.2 · bf16
Dataset
toolcall-v2 · 200 tasks × 11 models
Metrics
exact-match · schema-valid · p95 latency
Decoding
temp 0.0 · seed 42 · max 512 tok
Repo
github.com/ExakisNelite/bench-toolcall @ a3f9c1e

Each task gives the model a system prompt, 2–6 tool definitions in OpenAI function format, and a user request that requires exactly one call. We score the first emitted call. Exact match means the tool name and every argument equal the reference. Schema-valid means the call parses and validates against the tool’s JSON schema, regardless of correctness. Each model runs twice: once in free-form mode (the model writes the call as text), once in constrained mode (vLLM guided decoding against the schema).

Results

Toggle the mode and the metric. The chart is a small React island; everything around it is static HTML.

Fig. 1 — Per-model score, toolcall-v2, n=200, seed 42. Best model highlighted. Hover-free by design: the numbers are printed.
ModelParamsExact (free)Exact (constr.)Valid (free)Valid (constr.)p95
qwen3-32b32B0.7920.8710.8240.9411.84 s
llama-4-scout17B×160.7610.8420.7800.9021.21 s
mistral-small-3.224B0.7230.8150.7410.9271.05 s
llama-3.3-70b70B0.7680.8090.8020.9153.10 s
gemma-3-27b27B0.6880.7790.7120.8681.62 s
qwen3-8b8B0.6410.7520.6550.8890.62 s
phi-414B0.6020.7180.6300.8510.88 s
Table 1 — Four further models (7B class) are in the raw data but omitted here; all scored below 0.65 exact-match in constrained mode.

Why schema strictness wins

Two effects stack. The obvious one: constrained decoding removes the parse failures, which were 4–11 % of all free-form calls. The less obvious one is that the model’s choice of tool improves too. With a grammar in place, the first token has to be one of the allowed tool names, which prunes the “let me explain what I’m about to do” preamble that derails smaller models.

from principia.bench import Harness

h = Harness(models=MODELS, seed=42, temperature=0.0)
h.run(tasks="toolcall-v2", n=200, mode="constrained")
h.run(tasks="toolcall-v2", n=200, mode="free")
h.report()   # → results/2026-09-02/*.parquet

The gap between 32B and 70B collapses once you constrain. If your budget is a single node, this is the practical finding: spend the memory on context and concurrency, not parameters.

Variance across seeds

We re-ran the top five models with seeds 1–5 at temperature 0.3, which is closer to what people deploy. This is where the story gets uncomfortable, and where we stopped trusting single-run numbers. Three of the five models swap places between runs, and the

Continue reading

Get the full article, the analysis and the raw data

We'll unlock the rest of this article and send you the occasional Principia note — new benchmarks, new demos. No spam.

By continuing you accept our privacy notice. Double opt-in; unsubscribe in one click.

Limits

Locked — 4 paragraphs.

Reproduce it

Locked — commands, Docker image, expected runtime.