GBNF grammar-constrained tool calling is a technique that forces a language model to emit only structurally valid output. A GBNF grammar defines the exact shape allowed, such as a JSON array of tool calls. At each decoding step, the model can sample only tokens the grammar permits, making malformed tool calls impossible.
What is GBNF?
GBNF stands for GGML BNF. It’s a grammar format that llama.cpp supports natively for constrained decoding. If you’ve ever seen Backus-Naur Form, the notation used to describe the syntax of programming languages, GBNF will look familiar. It’s a BNF-like notation that defines which sequences of tokens are legal.
Think of it as a set of rails. A train can move fast because it physically cannot leave the track. A GBNF grammar is the track. It doesn’t tell the model what to think, but it does decide what the model is even allowed to say next.
For agents, the useful thing to constrain is the output that carries actions: a JSON array of tool calls. If that array is well-formed every single time, the runtime that reads it never chokes on a parse error.
How does a grammar force valid JSON?
To understand the mechanism, you have to look at how a model actually generates text.
At each decoding step, the model produces a probability distribution over its entire vocabulary, tens of thousands of possible next tokens. Normally, a sampler picks one of the high-probability tokens and appends it. Then it repeats. This is where malformed output comes from: the model is free to pick any token, including one that breaks your JSON, like a stray word where a } should go.
Grammar-constrained decoding changes one thing. Before the sampler picks, the grammar is consulted. Given everything generated so far, the grammar computes the set of tokens that would keep the output valid. Every token outside that set has its probability forced to zero. The model literally cannot sample it.
So if the grammar says the next character must be [ or whitespace, then no matter how confident the model was about emitting the word “Sure,” that token is masked out. It never had a chance. Reliability stops being a matter of prompt luck and becomes a property of the decoder.
Here’s a tiny, illustrative GBNF grammar for a JSON array of tool calls:
root ::= "[" ws call (ws "," ws call)* ws "]"call ::= "{" ws "\"name\"" ws ":" ws string ws "," ws "\"arguments\"" ws ":" ws object ws "}"object ::= "{" ws (pair (ws "," ws pair)*)? ws "}"pair ::= string ws ":" ws valuevalue ::= string | number | object | "true" | "false" | "null"string ::= "\"" ([^"\\] | "\\" .)* "\""number ::= "-"? [0-9]+ ("." [0-9]+)?ws ::= [ \t\n]*Read top to bottom, this says: the root must be a [...] array of one or more call objects; each call must have a name string and an arguments object; and so on down to what a valid string or number looks like. There is no production rule that produces prose, an apology, or a trailing comma. Those outputs are simply unreachable.
Why do small local models need this?
Frontier models hosted in the cloud are large enough that they usually emit clean JSON when you ask nicely. Small quantized models running on your laptop are a different story.
A local AI agent runs a compressed model, often quantized down to 4 bits or so, on consumer hardware. These models are capable, but they’re less reliable at the fiddly, format-sensitive parts of the job. Left unconstrained, a small local model will occasionally:
- Wrap its JSON in a chatty preamble (“Sure, here’s the tool call:”).
- Hallucinate a tool-call shape that looks plausible but doesn’t match your schema.
- Emit a trailing comma, an unclosed brace, or a smart quote that breaks the parser.
In an agent, any one of these is fatal. The whole loop depends on the runtime being able to read the model’s output, look up the tool, and run it. One malformed array and the chain falls over.
This is why grammar constraints matter so much for local agents. Together with good quantization, which keeps the model smart enough to reason after compression, grammar-constrained decoding is one of the two things that moved local agents from “toy” to “usable.” Quantization keeps the reasoning; the grammar guarantees the output is always machine-readable.
How Atomic Agent uses grammar-constrained tool calling
Atomic Agent’s agent loop is built around this. One model inference equals one step. Each step, the model emits a JSON array of 1 to N tool calls and stops. That single inference is GBNF grammar-constrained, so the array is always well-formed.
The runtime then takes over. For each call in the array it:
- Looks the tool up in a tool registry by fully-qualified name, like
os.fs.readorbrowser.navigate. - Validates the arguments.
- Checks whether the tool is dangerous.
- Runs it.
- Compresses the result so the next step’s context stays lean.
Then it calls the model again with the results, and the loop continues. Because the grammar guarantees the array parses, none of these steps ever have to defend against garbage input at the JSON level. You can read more about how the tool layer is structured in the docs.
What a grammar guarantees, and what it doesn’t
This is the honest part, and it’s important to get right.
A GBNF grammar guarantees:
- The output is valid JSON (or whatever structure you defined).
- The tool-call array parses every time, with no retry loop.
- The model cannot invent a call shape that breaks your parser.
- An entire class of failures, parse errors and hallucinated formats, is removed at the source.
A GBNF grammar does not guarantee:
- That the model picked the right tool.
- That the arguments are semantically correct.
- That the plan makes sense.
In other words, the grammar enforces valid structure, not correct semantics. The model can still emit a perfectly well-formed call to the wrong tool, or pass a valid-but-wrong argument. Constraining structure doesn’t make the model smarter. It removes the noise so that the model’s actual reasoning is what gets tested, not its JSON luck.
That distinction is the whole point. You want the model spending its capacity on what to do, not on remembering to close a brace.
GBNF vs JSON Schema, and other approaches
There’s more than one way to get structured output. It’s worth knowing where grammar-constrained decoding sits.
- Native tool-calling APIs. Hosted providers expose a tool-calling mode that returns structured calls. Convenient, but it’s a cloud dependency and you don’t control the decoder.
- JSON-Schema-based constraint. Some stacks convert a JSON Schema into a decoding constraint. This is closely related to GBNF; under the hood, a schema often gets compiled down to a grammar.
- Retry-and-reparse. Let the model generate freely, try to parse, and if it fails, ask again. This works but is slow, wastes tokens, and can loop forever on a stubborn small model.
- Prompt-and-hope. Just ask for JSON in the prompt. Fine for big models, fragile for small ones.
Grammar-constrained decoding is more robust than prompt-and-hope or retry loops for one structural reason: invalid tokens are never sampled in the first place. There’s nothing to catch and retry because the failure can’t occur. You pay the cost once, at decode time, instead of paying it repeatedly in failed attempts.
The proof: GAIA Level 1
This isn’t theory. Grammar-constrained tool calling is a core reason Atomic Agent hits 69.8% on GAIA Level 1 with a small local model, competitive with setups that lean on much larger hosted models. When the tool-call format is guaranteed, the benchmark measures the agent’s reasoning instead of its parsing luck. You can read the full write-up in the GAIA L1 experiment doc.
The short version
- GBNF (GGML BNF) is llama.cpp’s native grammar format for constrained decoding.
- At each decoding step, the grammar masks out every token that would break the structure, so malformed JSON and hallucinated tool-call shapes become impossible.
- Small quantized local models need this because unconstrained they emit broken JSON that topples the whole agent loop.
- Grammar constraints plus good quantization are what moved local agents from “toy” to “usable.”
- A grammar guarantees valid structure, not correct semantics. The model can still pick the wrong tool, it just can’t emit an unparseable one.
- It’s a core reason Atomic Agent reaches 69.8% on GAIA Level 1 with a small local model.
Want to run a grammar-constrained local agent yourself? Start with the quickstart.